夏令时更改期间的Java Calendar.roll和CST

时间:2016-03-14 11:16:15

标签: java datetime timezone dst

我想知道Calendar.roll是否尊重其javadoc合同:

运行以下代码段

    final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("CST"));
    cal.setTimeInMillis(1457928024812l);

    System.out.println(cal.getTime());
    cal.roll(Calendar.HOUR_OF_DAY, true);
    System.out.println(cal.getTime());

以下输出:

Sun Mar 13 23:00:24 CDT 2016
Sun Mar 13 23:00:24 CDT 2016

2016年3月13日是凌晨2点(从CST到CDT)的夏令时变化。 卷状态的javadoc滚动“添加单个时间单位”,此处不添加任何时间单位。 这是该方法的预期行为吗?

编辑:

我将此报告为错误。有关更多信息,请参阅相应OpenJDK票证的链接:https://bugs.openjdk.java.net/browse/JDK-8152077

2 个答案:

答案 0 :(得分:2)

这似乎是roll方法中的实际错误。很好找!

几点说明:

  • 我必须使用SimpleDateFormat来获得您展示的确切结果,因为只需调用getTime即可在本地时区打印一个Date对象。< / p>

  • 最好使用America/Chicago而不是CST,但这不是原因。

  • 对于任何常规日期,第23小时滚动应该在同一天的0小时。如果您只想增加一小时,请使用add代替roll。见add vs rolladd方法似乎工作正常。

  • 在春季前进过渡当天,一天只有23个小时。 roll方法似乎考虑到了这一点,即使它没有越过实际转换(当时钟跳到3:00时,这个时区接近2:00)。如您所示,它将小时设置为23,这是它应该设置的0之前一小时。

  • 在回归过渡当天,当天有25个小时。同样,roll方法尝试将此考虑在内,将小时设置为1而不是0,即使它没有超过实际转换(再次发生在接近凌晨2点)这个时区,当时钟回到1点时。

我做了一个快速搜索,看看是否已经在任何地方进行了报道并且没有找到多少。也许你应该report it

我还要补充一点,您应该考虑将Joda Time用于Java 7或更早版本,将java.time用于Java 8或更高版本。

答案 1 :(得分:1)

正如the Answer by Matt Johnson所说:

  • 使用continent/region格式的proper time zone name。避免使用既不唯一也不标准化的3-4字母代码。
  • 使用Java 8及更高版本中内置的java.time框架。见Tutorial。避免使用旧的日期时间类java.util.Date/.Calendar。

InstantUTC时间轴上nanosecond分辨率的时刻。应用时区(ZoneId)以获得ZonedDateTime

Long input = 1457928024812L;
Instant instant = Instant.ofEpochMilli ( input );

ZoneId zoneId = ZoneId.of ( "America/Chicago" );
ZonedDateTime zdt = ZonedDateTime.ofInstant ( instant, zoneId );

ZonedDateTime zdtHourLater = zdt.plusHours ( 1 );

转储到控制台。

System.out.println ( "input: " + input + " |  instant: " + instant + " | zoneId: " + zoneId + " | zdt: " + zdt + " | zdtHourLater: " + zdtHourLater );
  

输入:1457928024812 |时刻:2016-03-14T04:00:24.812Z | zoneId:America / Chicago | zdt:2016-03-13T23:00:24.812-05:00 [美国/芝加哥] | zdtHourLater:2016-03-14T00:00:24.812-05:00 [美国/芝加哥]

所以,java.time中没有这样的问题。正如预期的那样,从13日晚上11点到14日午夜,加上一小时的午夜时间。

夏令时(DST)

正确处理DST。

越过DST转换

从凌晨1点59分开始加1小时,按时间顺序跳2小时到凌晨3:59。

ZonedDateTime zdtBeforeTwoAm = ZonedDateTime.of ( 2016, Month.MARCH.getValue ( ), 13, 1, 59, 0, 0, zoneId );
ZonedDateTime zdtBeforeTwoAmPlus = zdtBeforeTwoAm.plusHours ( 1 );

转储到控制台。

System.out.println ( "zdtBeforeTwoAm: " + zdtBeforeTwoAm + " |  zdtBeforeTwoAmPlus: " + zdtBeforeTwoAmPlus );
  

zdtBeforeTwoAm:2016-03-13T01:59-06:00 [美国/芝加哥] | zdtBeforeTwoAmPlus:2016-03-13T03:59-05:00 [美国/芝加哥]

要求凌晨2点

要求凌晨2点无效(没有这个时间)。所以java.time会自动移动到有效的等价物,凌晨3点。

ZonedDateTime zdtTwoAm = ZonedDateTime.of ( 2016, Month.MARCH.getValue ( ), 13, 2, 0, 0, 0, zoneId );

转储到控制台。

System.out.println ("zdtTwoAm: " + zdtTwoAm );
  

zdtTwoAm:2016-03-13T03:00-05:00 [美国/芝加哥]