【问题标题】:Milliseconds of equal dates calculated at different times do differ android在不同时间计算的相等日期的毫秒确实不同android
【发布时间】:2023-04-07 19:15:01
【问题描述】:

最近我尝试使用当前日期的毫秒表示作为数据库(领域)的主键

我可以获取日期作为字符串并存储它,但由于我需要对大量数据执行快速升序搜索,我决定以毫秒格式存储数据。

为了实现这一点,我遵循了以下步骤:

1) 初始化一个 GregorianCalendar 实例并传递当前日期

    Calendar c = new GregorianCalendar();
    c.setTime(new Date());

2) 设置时间以精确匹配当天的午夜

    c.set(Calendar.HOUR_OF_DAY,0);
    c.set(Calendar.MINUTE,0);
    c.set(Calendar.SECOND,0);
    c.set(Calendar.MILLISECOND,0);

3) 将结果转换为毫秒

    c.getTime().getTime()

后来我通过将对象插入数据库来测试它。 下面是日志输出。我多次计算了同一个日期,都是通过上面的代码,没有任何变化。

01-03 06:59:38.607 16256-16256/com.example.bizarre.bindaccess D/Day: Long: 1 422 528 534 032 Date: 29.01.15
01-03 06:59:38.611 16256-16256/com.example.bizarre.bindaccess D/Day: Long: 1 422 528 647 420 Date: 29.01.15
01-03 06:59:38.611 16256-16256/com.example.bizarre.bindaccess D/Day: Long: 1 422 528 669 794 Date: 29.01.15
01-03 06:59:38.611 16256-16256/com.example.bizarre.bindaccess D/Day: Long: 1 422 528 707 566 Date: 29.01.15
01-03 06:59:38.615 16256-16256/com.example.bizarre.bindaccess D/Day: Long: 1 422 532 686 557 Date: 29.01.15
01-03 06:59:38.615 16256-16256/com.example.bizarre.bindaccess D/Day: Long: 1 422 532 726 052 Date: 29.01.15
01-03 06:59:38.615 16256-16256/com.example.bizarre.bindaccess D/Day: Long: 1 422 532 754 147 Date: 29.01.15

尽管我的操作为零小时、分钟、秒和毫秒,并且每次我得到稍微不同的值时都会得到一个以毫秒为单位的清除午夜值。

我的建议是:

1) 取决于设备时间方案。

2)它本身与android有关。

3) 麻烦就在我身边。

如果有人可以帮助我,我会很高兴。

附:我还使用了一个站点http://www.fileformat.info/tip/java/date2millis.htm 来获得 29.01.15 的毫秒数,结果是 1 422 489 600 000。

更新 #1。 1)所有插入都在同一个设备上,本地时区没有改变,所以环境不受影响。 2) 正如我上面所说的,也许不清楚 - 我将数据保存在 millis 中,在一个 long 类型的类字段中。无论如何,该字段也不受影响。

更新 #2。实际上,我的问题是:“我遇到的行为如何解释?”。但由于它可能过于宽泛或不清楚,我将重点放在我最初想要实现的目标上 - “如何以一致的毫秒值的形式获取本地(设备上)时区的当前日期(类型长)?

【问题讨论】:

  • 您如何从数据库中保存和检索日历?如果您将时间设置为零,则不应改变毫秒值
  • 所有设备都使用相同的时区吗?
  • @k3b 更新答案,提供您要求的信息。
  • @Hugo 更新了答案,提供了您要求的信息。
  • 您的问题是:如何获取一个从纪元开始计数的数字并将其更改为以 UTC 表示该日期的 00:00:00 时间?

标签: java android database date time


【解决方案1】:

tl;博士

Instant.ofEpochMilli( 1_422_528_534_032L )
       .truncatedTo( ChronoUnit.DAYS ) 

2015-01-29T00:00:00Z

你的问题?

虽然不清楚,但我将您的问题解释为:如何获取一个从纪元开始计数的数字并将其更改为以 UTC 表示该日期的 00:00:00 时间?

“如何以一致的毫秒值(long类型)的形式获取本地(设备上)时区的当前日期?”

从纪元开始计数几乎总是在UTC 中(否则这样做很麻烦,而且近乎疯狂,恕我直言)。因此,您可能会混淆两个不同的方面:(a) UTC 中的时代计数(Instant),以及 (b) 同一时刻,但从人们使用的挂钟时间的角度来看特定区域(ZonedDateTime)。

提示:作为程序员,要学会在 UTC 中思考和工作。您的日志记录和存储/交换数据也都使用 UTC。 将 UTC 视为 The One True Time™,而所有其他都只是变体。

另一个提示:不要花费你宝贵的时间和精力去思考遗留的Date/Calendar 课程。随着业界领先的 java.time 类的到来,我们现在可以愉快地抛弃它们了。

避免使用旧的日期时间类

您正在使用麻烦的旧日期时间类,现在已被 java.time 类取代。对于 Android,请参见下文。

请务必将您的计数从 epoch 数字处理为 64 位 long/Long 而不是 32 位 int/Integer

long input = 1_422_528_534_032L ; // Numeric literal for a `long` in modern Java.

Instant 类表示UTC 时间线上的时刻,分辨率为nanoseconds(最多九 (9) 位小数)。

Instant instant = Instant.ofEpochMilli( input ) ;

instant.toString(): 2015-01-29T10:48:54.032Z

为了有效地将时间清除为 00:00:00,我们可以通过指定 ChronoUnit.DAYS 进行截断。

Instant instantTruncated = instant.truncatedTo( ChronoUnit.DAYS ) ;

instantTruncated.toString(): 2015-01-29T00:00:00Z

如果你想比较,你可以提取一个从纪元开始的计数。

long millisSinceEpoch = instantTruncated.toEpochMilli() ;

1422489600000

为了好玩,让我们计算一下增量。

long delta = ( input - millisSinceEpoch ) ;

38934032

此外,让我们将该增量表示为Duration,其中toString 方法生成标准ISO 8601 格式的字符串。

Duration d = Duration.ofMillis( delta ) ;

d.toString(): PT10H48M54.032S

看到这一切code run live at IdeOne.com

如果你想看到同一时刻,时间轴上的点,通过特定地区人们看到的挂钟时间的镜头,应用时区(ZoneId)得到ZonedDateTime对象。

ZoneId z = ZoneId.of( "Europe/Sofia" ) ; // Or "Asia/Kolkata", whatever.
ZonedDateTime zdt = instant.atZone( z ) ;  // Same moment in history, but viewed using the wall-clock time in some particular region.

顺便说一句,虽然在 UTC 中一天的开始时间始终是 00:00:00,但在其他时区并非总是如此。由于夏令时 (DST) 和其他异常情况,某些区域中的某些日期可能从一天中的其他时间开始,例如 01:00:00。让 java.time 确定一天的开始:

ZonedDateTime zdt = LocalDate.of( 2015 , Month.JANUARY , 29 ).atStartOfDay( ZoneId.of( "Asia/Gaza" ) ) ;
long millisFromEpoch = zdt.toInstant().toEpochMilli() ;  // Generate a count-from-epoch of UTC in milliseconds.

关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

从哪里获得 java.time 类?

【讨论】:

  • 再次感谢您对该主题的详细而简短的解释:)以及所有提示!我必须完美地解决我的问题。
猜你喜欢
  • 1970-01-01
  • 2013-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多