【问题标题】:Why do ZonedDateTime and Calendar disagree on the hour in year 2050? [duplicate]为什么 ZonedDateTime 和 Calendar 在 2050 年的时间上不一致? [复制]
【发布时间】:2020-05-23 08:36:56
【问题描述】:

考虑以下代码:

ZoneId zoneId = ZoneId.of("America/Los_Angeles");
long currMillis = 2530778400000L;
Instant curr = Instant.ofEpochMilli(currMillis);
LocalDateTime dt = LocalDateTime.ofInstant(curr, zoneId); //the local one just for completeness
ZonedDateTime zdt = ZonedDateTime.ofInstant(curr, zoneId);
Calendar calendar = GregorianCalendar.from(zdt);

System.out.println(String.format("%-30s %s", "java-8 LocalDateTime hour:", dt.toLocalTime().getHour()));
System.out.println(String.format("%-30s %s", "java-8 ZonedDateTime hour:", zdt.toLocalTime().getHour()));
System.out.println(String.format("%-30s %s", "Calendar hour:", calendar.get(Calendar.HOUR_OF_DAY)));

印刷:

java-8 LocalDateTime hour:     3
java-8 ZonedDateTime hour:     3
Calendar hour:                 2

似乎在这个小时左右日历从第 2 小时跳到第 4 小时(如果它对应于 DST 更改,通常不一定是问题)。

我正在使用 AdoptOpenJDK 1.8.0_242,但我也检查过 HotSpot 1.8.0_181 - 同样的问题。

为什么日历报告的时间与 ZonedDateTime 不同?
这种不匹配是一个已知问题吗?
我应该更信任谁——在这种情况下是 ZonedDateTime 还是 Calendar?

【问题讨论】:

    标签: java datetime java-8 calendar zoneddatetime


    【解决方案1】:

    假设规则(过渡到 DST 发生在 3 月 8 日或之后的第一个星期日 02:00)在 2050 年没有改变,那一刻是发生间隙过渡的时刻(3 月 13 日),时钟从 01:59 跳到 03:00,所以 02:00 实际上并不存在。 Calendar 在这里大错特错。

    您可以通过检查每个时区类对相关瞬间的说法进一步了解Calendar 的错误程度。 ZonedDateTime 使用 ZoneId,而 Calendar 使用 TimeZone。我使用以下代码将ZoneId 上各种方法的输出与TimeZone 对应方法的输出进行了比较:

    ZoneId zoneId = ZoneId.of("America/Los_Angeles");
    long currMillis = 2530778400000L;
    Instant curr = Instant.ofEpochMilli(currMillis);
    TimeZone tz = TimeZone.getTimeZone(zoneId);
    
    // what's the actual offset at that instant?
    System.out.println(zoneId.getRules().getOffset(curr).getTotalSeconds());
    System.out.println(tz.getOffset(currMillis) / 1000);
    
    // is DST observed at that instant?
    System.out.println(zoneId.getRules().isDaylightSavings(curr));
    System.out.println(tz.inDaylightTime(new Date(currMillis)));
    
    // what's the standard offset at that instant?      
    System.out.println(zoneId.getRules().getStandardOffset(curr).getTotalSeconds());
    System.out.println(tz.getRawOffset() / 1000);
    
    // how many seconds does DST add to the standard offset at that instant?
    System.out.println(zoneId.getRules().getDaylightSavings(curr).getSeconds());
    Calendar calendar = GregorianCalendar.from(ZonedDateTime.ofInstant(curr, zoneId));
    System.out.println(calendar.get(Calendar.DST_OFFSET) / 1000);
    

    结果如下:

    -25200
    -28800
    true
    true
    -28800
    -28800
    3600
    0
    

    如您所见,他们都认为观察到了夏令时,但TimeZone 认为夏令时在标准偏移量上增加了 0 秒,这使得它认为实际偏移量仍然是 -8 小时。

    但谁知道 30 年后会发生什么?让我们希望每个人都摆脱 DST :)

    【讨论】:

    • 很好的答案。但是关于您关于摆脱 DST 的评论:只要有政客,就会改变时区。 DST 只是区域更改的原因之一。历史已经表明政治家改变时区的其他政治、军事、外交和文化原因。我强烈建议所有程序员学习在他们的代码中处理时区,并且防御性地编程以始终期望时区变化,无论 DST 当前是否是他们管辖范围内的问题。
    • @OleV.V.我刚刚查看了tzdata2019c 文件中的northamerica。我没有看到2038 的出现。你发现了一个恰好在 2038 年开始的不同,这让我想知道这是否可能是与 32 位 Year 2038 problem 相关的编程问题。
    • @BasilBourque 我确实注意到了巧合。 Meno Hochschield’s comment here 似乎告诉我们,这不仅仅是巧合。
    • @OleV.V.那么,还有 另一个 理由来避免使用旧的日期时间类吗?
    • @BasilBourque 好像我们还没有很多!是的!
    猜你喜欢
    • 2017-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多