Java Calendar.roll 和夏令时更改期间的 CST
Java Calendar.roll and CST during daylight saving changes
我想知道 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)。
roll 的 javadoc 指出 roll “ 添加单个时间单位 ”,这里没有添加任何时间单位。
这是此方法的预期行为吗?
编辑:
我将此报告为错误。有关详细信息,请参阅相应 OpenJDK 票证的 link:https://bugs.openjdk.java.net/browse/JDK-8152077
这似乎是 roll
方法中的实际错误。不错的发现!
一些注意事项:
我必须使用 SimpleDateFormat
才能获得您显示的确切结果,因为只需调用 getTime
就会得到一个 Date
对象,该对象在本地打印时区。
最好用America/Chicago
而不是CST
,但这不是原因。
对于任何常规日期,滚动第 23 小时的日期应该在同一天转到第 0 小时。如果您只想增加一个小时,请使用 add
而不是 roll
。参见 add vs roll。 add
方法似乎工作正常。
在spring-向前过渡的那一天,一天只有23个小时。 roll
方法似乎考虑到了这一点,即使它没有跨越实际的转换(在这个时区接近 2:00 时,时钟跳转到 3:00) .如您所示,它将小时设置为 23
,比它应该设置的 0
早一小时。
在回退过渡的那天,一天有 25 个小时。同样,roll
方法试图将这一点考虑在内,将小时设置为 1
而不是 0
,即使它没有跨越实际转换(再次发生接近 2:00 在这个时区,当时钟回到 1:00).
我快速搜索了一下,看看是否已经在任何地方报告过这种情况,但没有找到太多。也许你应该 report it.
我还要补充一点,您可能应该考虑对 Java 8 或更高版本使用 Joda Time for Java 7 or earlier, and java.time
。
正如所说:
- 使用
continent/region
格式的 proper time zone name。避免使用既不唯一也不规范的 3-4 字母代码。
- 使用java.time framework built into Java 8 and later. See Tutorial。避免使用旧的日期时间 类、java.util.Date/.Calendar.
一个Instant
is a moment on the timeline in UTC with nanosecond resolution. Apply a time zone (ZoneId
) to get a 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 );
input: 1457928024812 | instant: 2016-03-14T04:00:24.812Z | zoneId: America/Chicago | zdt: 2016-03-13T23:00:24.812-05:00[America/Chicago] | zdtHourLater: 2016-03-14T00:00:24.812-05:00[America/Chicago]
所以,在 java.time 中没有这样的问题。如预期的那样,从 13 日晚上 11 点到 14 日午夜刚过,增加一个小时。
夏令时 (DST)
正确处理夏令时。
跨夏令时转换
从 1:59 上午增加一小时,时钟上的时间跳到 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[America/Chicago] | zdtBeforeTwoAmPlus: 2016-03-13T03:59-05:00[America/Chicago]
要求凌晨 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[America/Chicago]
我想知道 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)。 roll 的 javadoc 指出 roll “ 添加单个时间单位 ”,这里没有添加任何时间单位。 这是此方法的预期行为吗?
编辑:
我将此报告为错误。有关详细信息,请参阅相应 OpenJDK 票证的 link:https://bugs.openjdk.java.net/browse/JDK-8152077
这似乎是 roll
方法中的实际错误。不错的发现!
一些注意事项:
我必须使用
SimpleDateFormat
才能获得您显示的确切结果,因为只需调用getTime
就会得到一个Date
对象,该对象在本地打印时区。最好用
America/Chicago
而不是CST
,但这不是原因。对于任何常规日期,滚动第 23 小时的日期应该在同一天转到第 0 小时。如果您只想增加一个小时,请使用
add
而不是roll
。参见 add vs roll。add
方法似乎工作正常。在spring-向前过渡的那一天,一天只有23个小时。
roll
方法似乎考虑到了这一点,即使它没有跨越实际的转换(在这个时区接近 2:00 时,时钟跳转到 3:00) .如您所示,它将小时设置为23
,比它应该设置的0
早一小时。在回退过渡的那天,一天有 25 个小时。同样,
roll
方法试图将这一点考虑在内,将小时设置为1
而不是0
,即使它没有跨越实际转换(再次发生接近 2:00 在这个时区,当时钟回到 1:00).
我快速搜索了一下,看看是否已经在任何地方报告过这种情况,但没有找到太多。也许你应该 report it.
我还要补充一点,您可能应该考虑对 Java 8 或更高版本使用 Joda Time for Java 7 or earlier, and java.time
。
正如
- 使用
continent/region
格式的 proper time zone name。避免使用既不唯一也不规范的 3-4 字母代码。 - 使用java.time framework built into Java 8 and later. See Tutorial。避免使用旧的日期时间 类、java.util.Date/.Calendar.
一个Instant
is a moment on the timeline in UTC with nanosecond resolution. Apply a time zone (ZoneId
) to get a 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 );
input: 1457928024812 | instant: 2016-03-14T04:00:24.812Z | zoneId: America/Chicago | zdt: 2016-03-13T23:00:24.812-05:00[America/Chicago] | zdtHourLater: 2016-03-14T00:00:24.812-05:00[America/Chicago]
所以,在 java.time 中没有这样的问题。如预期的那样,从 13 日晚上 11 点到 14 日午夜刚过,增加一个小时。
夏令时 (DST)
正确处理夏令时。
跨夏令时转换
从 1:59 上午增加一小时,时钟上的时间跳到 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[America/Chicago] | zdtBeforeTwoAmPlus: 2016-03-13T03:59-05:00[America/Chicago]
要求凌晨 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[America/Chicago]