【问题标题】:Brasilia Summer Time transition at 2037-10-182037-10-18 巴西利亚夏令时转换
【发布时间】:2016-06-29 23:48:26
【问题描述】:
TimeZone.setDefault(TimeZone.getTimeZone("BET"));
Locale.setDefault(Locale.ENGLISH);

SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS zzzz");

Date d0 = sdf1.parse("2037-10-17 23:00:00.000");
Date d1 = sdf1.parse("2037-10-17 23:00:00.001");
Date d2 = sdf1.parse("2037-10-17 23:59:59.999");
Date d3 = sdf1.parse("2037-10-18 00:00:00.000");
Date d4 = sdf1.parse("2037-10-18 00:00:00.001");
Date d5 = sdf1.parse("2037-10-18 00:59:59.999");
Date d6 = sdf1.parse("2037-10-18 01:00:00.000");
Date d7 = sdf1.parse("2037-10-18 01:00:00.001");
Date d8 = sdf1.parse("2037-10-18 01:59:59.999");
Date d9 = sdf1.parse("2037-10-18 02:00:00.000");

System.out.println(sdf2.format(d0) + "(" + d0.getTime() + "), dst: " + TimeZone.getDefault().inDaylightTime(d0) + ", offset: " + TimeZone.getDefault().getOffset(d0.getTime()));
System.out.println(sdf2.format(d1) + "(" + d1.getTime() + "), dst: " + TimeZone.getDefault().inDaylightTime(d1) + ", offset: " + TimeZone.getDefault().getOffset(d1.getTime()));
System.out.println(sdf2.format(d2) + "(" + d2.getTime() + "), dst: " + TimeZone.getDefault().inDaylightTime(d2) + ", offset: " + TimeZone.getDefault().getOffset(d2.getTime()));
System.out.println(sdf2.format(d3) + "(" + d3.getTime() + "), dst: " + TimeZone.getDefault().inDaylightTime(d3) + ", offset: " + TimeZone.getDefault().getOffset(d3.getTime()));
System.out.println(sdf2.format(d4) + "(" + d4.getTime() + "), dst: " + TimeZone.getDefault().inDaylightTime(d4) + ", offset: " + TimeZone.getDefault().getOffset(d4.getTime()));
System.out.println(sdf2.format(d5) + "(" + d5.getTime() + "), dst: " + TimeZone.getDefault().inDaylightTime(d5) + ", offset: " + TimeZone.getDefault().getOffset(d5.getTime()));
System.out.println(sdf2.format(d6) + "(" + d6.getTime() + "), dst: " + TimeZone.getDefault().inDaylightTime(d6) + ", offset: " + TimeZone.getDefault().getOffset(d6.getTime()));
System.out.println(sdf2.format(d7) + "(" + d7.getTime() + "), dst: " + TimeZone.getDefault().inDaylightTime(d7) + ", offset: " + TimeZone.getDefault().getOffset(d7.getTime()));
System.out.println(sdf2.format(d8) + "(" + d8.getTime() + "), dst: " + TimeZone.getDefault().inDaylightTime(d8) + ", offset: " + TimeZone.getDefault().getOffset(d8.getTime()));
System.out.println(sdf2.format(d9) + "(" + d9.getTime() + "), dst: " + TimeZone.getDefault().inDaylightTime(d9) + ", offset: " + TimeZone.getDefault().getOffset(d9.getTime()));

输出

2037-10-17 23:00:00.000 Brasilia Time(2139444000000), dst: false, offset: -10800000
2037-10-17 23:00:00.001 Brasilia Time(2139444000001), dst: false, offset: -10800000
2037-10-17 23:59:59.999 Brasilia Time(2139447599999), dst: false, offset: -10800000
2037-10-18 01:00:00.000 Brasilia Summer Time(2139447600000), dst: true, offset: -7200000
2037-10-18 00:00:00.001 Brasilia Time(2139447600001), dst: true, offset: -10800000
2037-10-18 00:59:59.999 Brasilia Time(2139451199999), dst: true, offset: -10800000
2037-10-18 01:00:00.000 Brasilia Summer Time(2139447600000), dst: true, offset: -7200000
2037-10-18 00:00:00.001 Brasilia Time(2139447600001), dst: true, offset: -10800000
2037-10-18 00:59:59.999 Brasilia Time(2139451199999), dst: true, offset: -10800000
2037-10-18 02:00:00.000 Brasilia Summer Time(2139451200000), dst: true, offset: -7200000

这段代码打印出“2037-10-18 00:00:000 Brasilia Time”前后的日期时间,结果显示“2037-10-18 00:00:000 Brasilia Time”应该是“2037” -10-18 01:00:00.000 巴西利亚夏令时”,这意味着巴西利亚在那一刻进入了夏令时。

我的问题是为什么在“2037-10-18 00:00:00.001 Brasilia Time”和“2037-10-18 00:59:59.999 Brasilia Time”之间,时区偏移仍然使用标准时间偏移。这是JDK时区数据的错误还是这个时区实际上是这样工作的。

我的代码使用偏移量来确定两个日期之间是否存在 dst 转换。显然“2037-10-18 01:00:00.000 Brasilia Summer Time”和“2037-10-18 00:59:59.999 Brasilia Time”这两个日期在这里不起作用。

我可以改用“TimeZone.getDefault().inDaylightTime(Date date)”来判断是否有过渡,但我还是想知道是不是JDK的bug。

【问题讨论】:

  • 如果有错误,它可能在解析器中,因为 d3d6(以及 d4/7d5/8)被解析为相同的 long 值。
  • 什么Java版本和Olson TZDB版本?
  • 这可能是一个错误 - java.time API 似乎给了the correct results
  • 似乎是一个错误。您的测试日期足够接近结束时间,它可能是一个未经测试的边缘条件错误。 “google search 2038 unix bug”了解有关终止时间的详细信息。

标签: java date datetime


【解决方案1】:

我尝试使用调试器单步执行代码,但时区和开关本身似乎存在问题:BRT 到 BRST 在午夜从 00:00:00 切换到 01:00:00,这意味着那个小时两者之间实际上不存在。

从我的调试来看,问题似乎出在GregorianCalendar#computeTime(),尤其是在以下行中:

millis -= zoneOffsets[0] + zoneOffsets[1];

millis 行之前是从解析日期计算的自纪元以来的时间,对于00:00:00 (213946800000) 和01:00:00 (2139440400000) 是不同的。在这两种情况下,zoneOffsets[0] 都是 -10800000,它是 UTC 的原始偏移量。

区别在于zoneOffsets[1]00:00:00 为 0,而01:00:00 为 3600000,即 1 小时。其原因似乎是对inDaylightTime( new Date(millis) ) 的内部调用,它对00:00:00(夏令时前)返回false,但对01:00:00(夏令时的第一个小时)返回true。因此,最后的时间将是相同的,因为您总是添加 10800000 毫秒,但从高出 3600000 毫秒的较高值中减去 3600000 毫秒 :)

最后你会得到一个Date 具有相同的毫秒时间。

再次格式化日期时,格式化程序似乎会根据时区检查毫秒时间,并且每次对应于00:00:00,000 - 59:59:59,999,即可能在两个时区中的时间都将被假定为具有dstOffset 0 而不是 3600000,因此会打印不同的时区。

编辑:在比较01:00:00.00001:00:00.001 时,似乎ZoneInfo.getOffsets(time, offsets, type) 中可能存在一个错误,前者返回360000 的dst 偏移量,后者返回0在格式化程序中填写日历字段时,两者都将采用 dst。

编辑 2: 更改解析器格式以接受时区快捷方式时,您可以观察到相同的行为,即 00:00:00.000 BRT01:00:00.000 BRST 被解析为 2139447600000 并再次格式化为 01:00:00.000 BRST 而 @ 987654342@ 和 01:00:00.001 BRST 被解析为 2139447600001 并格式化为 00:00:00.001 BRT - 这本身是正确的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-10-25
    • 1970-01-01
    • 2015-03-20
    • 2019-04-25
    • 1970-01-01
    • 2012-08-30
    • 1970-01-01
    • 2014-06-02
    相关资源
    最近更新 更多