Java HOUR_OF_DAY为值0和1设置相同的小时

Java HOUR_OF_DAY为值0和1设置相同的小时,java,timezone,Java,Timezone,我有一个关于java时区的棘手问题(至少对我来说是这样)。我将从一个预期效果的示例开始: TimeZone tz = TimeZone.getDefault(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat(); simpleDateFormat.setTimeZone(tz); Calendar calendar = Calendar.getInstance(); calendar.setTimeZone(tz); cal

我有一个关于java时区的棘手问题(至少对我来说是这样)。我将从一个预期效果的示例开始:

TimeZone tz = TimeZone.getDefault();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
simpleDateFormat.setTimeZone(tz);
Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(tz);

calendar.set(Calendar.HOUR_OF_DAY, 0);
System.out.println("0: " + simpleDateFormat.format(calendar.getTime()));

calendar.set(Calendar.HOUR_OF_DAY, 1);
System.out.println("1: " + simpleDateFormat.format(calendar.getTime()));

calendar.set(Calendar.HOUR_OF_DAY, 2);
System.out.println("2: " + simpleDateFormat.format(calendar.getTime()));
输出:

0 4/28/17 12:27 AM
1: 4/28/17 1:27 AM
2: 4/28/17 2:27 AM
0: 4/28/17 1:31 AM
1: 4/28/17 1:31 AM
2: 4/28/17 2:31 AM
现在有一点魔力:

TimeZone tz = TimeZone.getTimeZone("Africa/Cairo");
SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
simpleDateFormat.setTimeZone(tz);
Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(tz);

calendar.set(Calendar.HOUR_OF_DAY, 0);
System.out.println("0: " + simpleDateFormat.format(calendar.getTime()));

calendar.set(Calendar.HOUR_OF_DAY, 1);
System.out.println("1: " + simpleDateFormat.format(calendar.getTime()));

calendar.set(Calendar.HOUR_OF_DAY, 2);
System.out.println("2: " + simpleDateFormat.format(calendar.getTime()));
输出:

0 4/28/17 12:27 AM
1: 4/28/17 1:27 AM
2: 4/28/17 2:27 AM
0: 4/28/17 1:31 AM
1: 4/28/17 1:31 AM
2: 4/28/17 2:31 AM

有人能解释一下为什么0和1的值在同一个特定时区的值上设置了小时数吗?

几年前,该时区计划在2017年4月28日晚上进行DST转换:在00:00am,时钟将向前移动一小时。因此,午夜到凌晨1点之间的时间将因过渡而无效

然而,这种过渡在实践中并没有发生,因为它被取消了(埃及似乎在过去几年中多次取消并恢复了DST)


总之,您可能正在使用旧版本的Java,其中包含过时的时区数据库。

您的机器具有旧版本的时区数据,在该数据中,埃及将在2017年4月28日开始的当地时间午夜将时钟向前拨。因此,上午12:31就不存在了,Java正在向前跳跃一个小时而不是1

2016年7月5日,发布了IANA时区数据2016f,将非洲/开罗时区更改为从该点开始不遵守DST。(这是一个非常晚的宣布,因为下一个变化将在2016年7月8日发生。)

您可以在上看到每个IANA时区的所有转换,跨越IANA数据的不同版本。(免责声明:我创建了该页面。我启动tzvalidate项目是为了验证不同的日期/时间库是否以相同的方式理解时区数据。)



1我认为API应该强制您声明在这种情况下希望发生什么,但我不会在这里讨论java.util.Calendar的所有错误…

您是对的。问题在于28日的时间转换。今天一切正常:)

可能与夏令时(DST)有关。我不知道Java如何处理这些时区的DST,因为这些时区似乎有非常不规则的DST策略。这对我来说很好。我想知道您是否有旧版本的时区数据,2017年4月28日可能是“向前跳”的一天……我使用的是jdk1.8.0_25。不太新鲜,但也不太旧,至少有7个时区数据版本已经过时:)如果开罗真的有这样的转变,这是有意义的。根据目前的数据,这并不重要。我会看看是否能找到一个真实发生这种情况的旧版本。@JonSkeet我在那里检查过:-不确定哪一个是正确的。@JonSkeet我发现开罗在2010年4月30日有这样的DST。我想知道Java的
Date
类中DST的参考是什么。看起来timezoneconverter.com的数据已经过时了,但可能与OP的数据相同。啊,时区数据变化的乐趣。@ArnaudDenoyelle:我相信大多数Java实现都使用IANA数据: