令人困惑的测试失败 - 夏令时
Confusing test fail - daylight saving time
在 Jenkins 的夜间构建中,我们的一项测试在 2:00:12am 失败。经过一段时间的调试和更改计算机的系统时间后,我感到非常困惑。然后我写了下面的测试(模拟问题),它失败了,但我不明白为什么。我尝试了 Google,但没有发现任何相似之处。
谁能解释为什么最后一个断言失败了?
@Test
public void testFirstBeforeSecond_atDayLightSavingTime() throws ParseException {
Date first = new SimpleDateFormat("dd-MM-yyyy HH:mm").parse("25-10-2015 00:59");
Date second = new SimpleDateFormat("dd-MM-yyyy HH:mm").parse("25-10-2015 01:01");
assertThat(first.before(second), is(true)); // Ok, as expected
first = add(first, Calendar.HOUR_OF_DAY, 2);
second = add(second, Calendar.HOUR_OF_DAY, 2);
assertThat(first.before(second), is(true)); // Ok, as expected
first = add(first, Calendar.DAY_OF_YEAR, 2);
second = add(second, Calendar.DAY_OF_YEAR, 2);
assertThat(first.before(second), is(true)); // Fails?
}
private Date add(Date date, int field, int amount) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(TimeZone.getTimeZone("Europe/Brussels"));
calendar.setTime(date);
calendar.add(field, amount);
return calendar.getTime();
}
(在布鲁塞尔时区,夏令时结束于 25-10-15 凌晨 3 点。时钟随后跳回一小时。)
如果你在每一步都打印出 first
和 second
,你会得到:
你的第一场比赛是
Sun Oct 25 00:59:00 CEST 2015
Sun Oct 25 01:01:00 CEST 2015
这完全符合预期。两次,相隔两分钟,欧洲中部夏令时。
第二场比赛变得有趣起来。您为每个日期增加了两个小时:
Sun Oct 25 02:59:00 CEST 2015
Sun Oct 25 02:01:00 CET 2015
现在两次跨越夏令时切换。第一次是在夏令时,2:59;第二次是在标准时间 2:01.
加上两天后,Java 好像忘记了夏令时:
Tue Oct 27 02:59:00 CET 2015
Tue Oct 27 02:01:00 CET 2015
2:59 和 2:01, 完全一样......这可能是日历上的两天后,但第一次肯定不会比第二步晚 48 小时!
如果你把最后的加法组改成
first = add(first, Calendar.HOUR_OF_DAY, 48);
second = add(second, Calendar.HOUR_OF_DAY, 48);
然后问题就解决了:
Tue Oct 27 01:59:00 CET 2015
Tue Oct 27 02:01:00 CET 2015
我的猜测是,Java 设计人员必须对 "N days later" 跨越 DST 切换时的预期行为进行一些猜测。
在 Jenkins 的夜间构建中,我们的一项测试在 2:00:12am 失败。经过一段时间的调试和更改计算机的系统时间后,我感到非常困惑。然后我写了下面的测试(模拟问题),它失败了,但我不明白为什么。我尝试了 Google,但没有发现任何相似之处。
谁能解释为什么最后一个断言失败了?
@Test
public void testFirstBeforeSecond_atDayLightSavingTime() throws ParseException {
Date first = new SimpleDateFormat("dd-MM-yyyy HH:mm").parse("25-10-2015 00:59");
Date second = new SimpleDateFormat("dd-MM-yyyy HH:mm").parse("25-10-2015 01:01");
assertThat(first.before(second), is(true)); // Ok, as expected
first = add(first, Calendar.HOUR_OF_DAY, 2);
second = add(second, Calendar.HOUR_OF_DAY, 2);
assertThat(first.before(second), is(true)); // Ok, as expected
first = add(first, Calendar.DAY_OF_YEAR, 2);
second = add(second, Calendar.DAY_OF_YEAR, 2);
assertThat(first.before(second), is(true)); // Fails?
}
private Date add(Date date, int field, int amount) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(TimeZone.getTimeZone("Europe/Brussels"));
calendar.setTime(date);
calendar.add(field, amount);
return calendar.getTime();
}
(在布鲁塞尔时区,夏令时结束于 25-10-15 凌晨 3 点。时钟随后跳回一小时。)
如果你在每一步都打印出 first
和 second
,你会得到:
你的第一场比赛是
Sun Oct 25 00:59:00 CEST 2015
Sun Oct 25 01:01:00 CEST 2015
这完全符合预期。两次,相隔两分钟,欧洲中部夏令时。
第二场比赛变得有趣起来。您为每个日期增加了两个小时:
Sun Oct 25 02:59:00 CEST 2015
Sun Oct 25 02:01:00 CET 2015
现在两次跨越夏令时切换。第一次是在夏令时,2:59;第二次是在标准时间 2:01.
加上两天后,Java 好像忘记了夏令时:
Tue Oct 27 02:59:00 CET 2015
Tue Oct 27 02:01:00 CET 2015
2:59 和 2:01, 完全一样......这可能是日历上的两天后,但第一次肯定不会比第二步晚 48 小时!
如果你把最后的加法组改成
first = add(first, Calendar.HOUR_OF_DAY, 48);
second = add(second, Calendar.HOUR_OF_DAY, 48);
然后问题就解决了:
Tue Oct 27 01:59:00 CET 2015
Tue Oct 27 02:01:00 CET 2015
我的猜测是,Java 设计人员必须对 "N days later" 跨越 DST 切换时的预期行为进行一些猜测。