我想知道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());
YIELS 输出如下:
Sun Mar 13 23:00:24 CDT 2016
Sun Mar 13 23:00:24 CDT 2016
2016 年 3 月 13 日凌晨 2 点的夏令时更改(从 CST 更改为 CDT)。滚动的javadoc指出roll"增加了一个时间单位",这里没有添加时间单位。这是此方法的预期行为吗?
编辑:
我将此报告为错误。有关更多信息,这里是相应 OpenJDK 票证的链接:https://bugs.openjdk.java.net/browse/JDK-8152077
似乎是roll
方法中的一个实际错误。 不错的发现!
几点注意事项:
我不得不使用
SimpleDateFormat
来获得您显示的确切结果,因为只需调用getTime
就会给出一个以本地时区打印的Date
对象。最好使用
America/Chicago
而不是CST
,但这不是造成这种情况的原因。对于任何常规日期,它滚动第 23 小时应在同一天转到第 0 小时。 如果您只想递增一小时,请使用
add
而不是roll
。 请参阅添加与滚动。add
方法似乎工作正常。在春季过渡的那一天,一天只有 23 小时。
roll
方法似乎考虑到了这一点,即使它没有越过实际的转换(在这个时区接近 2:00 时,时钟跳到 3:00 时)。 如您所展示的,它将小时设置为23
,比它应该设置的0
早一小时。在回退转换当天,一天有 25 小时。 同样,
roll
方法尝试考虑到这一点,将小时设置为1
而不是0
,即使它没有越过实际的过渡(此时,此时再次发生在接近 2:00 时,时钟移回 1:00)。
我做了一个快速搜索,看看这是否已经在任何地方报告过,但没有找到太多。 也许你应该报告它。
我还要补充一点,您可能应该考虑在Java 7或更早版本中使用Joda Time,在Java 8或更高版本中使用java.time
Joda Time。
正如Matt Johnson的回答所说:
- 使用
continent/region
格式的正确时区名称。避免使用既不唯一也不标准化的 3-4 个字母代码。 - 使用内置于 Java 8 及更高版本中的 java.time 框架。请参阅教程。避免使用旧的日期时间类 java.util.Date/。日历。
Instant
是时间轴上以纳秒分辨率表示的时刻。应用时区 ( 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 |区域 ID: 美国/芝加哥 |ZDT: 2016-03-13T23:00:24.812-05:00[美国/芝加哥] |zdtHour稍后: 2016-03-14T00:00:24.812-05:00[美国/芝加哥]
所以,在java.time中没有这样的问题。增加一个小时如预期的那样在午夜移动,从 13 日晚上 11 点到 14 日午夜刚过。
夏令时 (DST)
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 );
zdt之前凌晨两点: 2016-03-13T01:59-06:00[美国/芝加哥] | zdt之前两个AmPlus: 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[美国/芝加哥]