在不同时间计算的毫秒相等日期确实不同

时间:2017-08-07 11:13:13

标签: java android database date time

最近,我尝试将当前日期的毫秒表示用作数据库(Realm)的主键

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

为实现这一点,我按照以下步骤操作:

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)麻烦就在我身边。

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

P.S。我还使用了一个网站http://www.fileformat.info/tip/java/date2millis.htm来获得29.01.15的millis,结果是1 422 489 600 000。

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

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

1 个答案:

答案 0 :(得分:3)

TL;博士

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

2015-01-29T00:00:00Z

您的问题?

虽然不清楚,但我会将您的问题解释为:如何从一个从纪元开始计数并将其更改为代表该日期的00:00:00时间? / em>的

  

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

来自时代的计数几乎总是在UTC(否则是麻烦和近乎疯狂的恕我直言)。因此,您可能会混淆两个不同的方面:(a)从UTC开始计算时代(Instant),以及(b)那个非常相同的时刻,但通过使用的挂钟时间的镜头看到特定地区的人(ZonedDateTime)。

Big big 提示:作为程序员,学会用UTC思考和工作。您在UTC中记录和存储/交换数据也是如此。 将UTC视为 The One True Time™ ,所有其他仅仅是变体。

另一个提示:花费你宝贵的时间和精力去思考遗留的Date / Calendar类。它们是一个可怜的混乱,我们现在可以随着行业领先的java.time类的到来而愉快地放弃。

避免遗留日期时间类

您正在使用现在由java.time类取代的麻烦的旧日期时间类。请参阅下面的Android。

请务必将您的纪元数字处理为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)来获得{ {3}}对象。

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

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

现在位于SimpleDateFormatJoda-Time项目建议迁移到maintenance mode类。

要了解详情,请参阅java.time。并搜索Stack Overflow以获取许多示例和解释。规范是Oracle Tutorial

从哪里获取java.time类?