巴西利亚夏令时转折于2037-10-18

时间:2016-06-29 09:24:51

标签: 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巴西利亚时间"周围的日期时间,结果显示" 2037-10-18 00:00: 000巴西利亚时间"应该是" 2037-10-18 01:00:00.000巴西利亚夏令时"这意味着巴西利亚在那个时刻进入夏季。

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

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

我可以改为使用" TimeZone.getDefault()。inDaylightTime(Date date)"判断是否存在转换,但我仍然想知道它是否是JDK的错误。

1 个答案:

答案 0 :(得分:0)

我尝试使用调试器逐步执行代码,这似乎是时区和交换机本身的问题: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(pre-dst)返回false,但01:00:00(这是dst的第一个小时)返回true。因此,最终时间将是相同的,因为您总是添加10800000毫安,但从较高的值减去3600000毫安,高于3600000毫安:)

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

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

修改:在比较dstOffset01:00:00.000时,似乎01:00:00.001中可能存在一个错误,该错误会为前者返回360000的dst偏移量当后者在格式化器中填充日历的字段时,两者都将假设为dst。

编辑2:当更改解析器格式以接受时区快捷方式时,您可以观察到相同的行为,即ZoneInfo.getOffsets(time, offsets, type)00:00:00.000 BRT被解析为2139447600000并再次格式化为{ {1}} 01:00:00.000 BRST01:00:00.000 BRST被解析为2139447600001并格式化为00:00:00.001 BRT - 这本身就是正确的。