java - 2037-10-18 巴西利亚夏令时过渡

标签 java date datetime

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 Brasilia Summer Time”表示巴西利亚在那一刻进入夏令时。

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

我的代码使用偏移量来决定两个日期之间是否存在夏令时转换。显然“2037-10-18 01:00:00.000 巴西利亚夏令时”和“2037-10-18 00:59:59.999 巴西利亚时间”这两个日期在这里不起作用。

我可以更改为使用“TimeZone.getDefault().inDaylightTime(Date date)”来决定是否有转换,但我仍然想知道这是否是 JDK 的错误。

最佳答案

我尝试使用调试器单步执行代码,这似乎是时区和开关本身的问题: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(夏令时的第一个小时)。因此,最终时间将是相同的,因为您总是添加 10800000 毫秒,但从更高的值中减去 3600000 毫秒,后者高出 3600000 毫秒:)

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

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

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

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

关于java - 2037-10-18 巴西利亚夏令时过渡,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38095468/

相关文章:

java - 在 Java 中使用单个 for-each 循环遍历多个列表

Python - 以 YYYY-MM-DD 格式获取昨天的日期作为字符串

javascript - 将 Javascript 字符串转换为日期

datetime - 转换为 Weka 日期格式

Python/Pandas - DataFrame 索引 - 向前移动一个月

java - 集中配置管理

XML 文件中没有包含 DTD 声明的 XML 文件的 Java DTD 验证?

java - List[Int] 和 List[Integer] 类型删除的区别

java - 对非今天的日期使用偏移量

java - 使用用户的区域设置时用秒格式化日期和时间