【问题标题】:Javas GregorianCalendar.toZonedDateTime() returns different date for dates in the pastJava Gregorian Calendar.to ZonedDateTime() 返回过去日期的不同日期
【发布时间】:2020-03-09 10:20:19
【问题描述】:

对于过去的某个日期,GregorianCalendar.toZonedDateTime() 返回一个休息 1 天的日期。

对于 1893 年 4 月 2 日,toZonedDateTime() 返回相同的日期,对于 1893 年 4 月 1 日,ZonedDateTime 向我显示 1893 年 3 月 31 日,并且“一年中的某天”值也存在差异。在这个“神奇”日期之前的日期总是有一个偏移量。

这里是一些示例代码:

final GregorianCalendar gc = new GregorianCalendar(1893, 0, 1); // Set to 1st January 1893
for(int i = 1; i < 365; i++) {
    gc.set(Calendar.DAY_OF_YEAR, i); // Update day of year
    final ZonedDateTime zdt = gc.toZonedDateTime();

    System.out.println(String.format(
            "GC: %02d.%02d.%d (%d) -> ZDT: %02d.%02d.%d (%d)", 
            gc.get(Calendar.DAY_OF_MONTH),
            gc.get(Calendar.MONTH) + 1, // "+1" is needed, because GregorianCalendar encodes January as 0.
            gc.get(Calendar.YEAR),
            gc.get(Calendar.DAY_OF_YEAR),
            zdt.getDayOfMonth(),
            zdt.getMonthValue(),
            zdt.getYear(),
            zdt.getDayOfYear()
            ));
}

运行代码时,你会得到输出

[...]
GC: 31.03.1893 (90) -> ZDT: 30.03.1893 (89)
GC: 01.04.1893 (91) -> ZDT: 31.03.1893 (90)
GC: 02.04.1893 (92) -> ZDT: 02.04.1893 (92)
GC: 03.04.1893 (93) -> ZDT: 03.04.1893 (93)
[...]

我在这里做错了什么?

提前感谢您的回答!

最好的问候, 马库斯

【问题讨论】:

  • 欧洲/柏林时区的 FWIW 我得到和你一样的结果。当我在欧洲/哥本哈根时区运行您的代码时,我全年都会遇到差异,从 GC: 01.01.1893 (1) -&gt; ZDT: 31.12.1892 (366)GC: 30.12.1893 (364) -&gt; ZDT: 29.12.1893 (363)

标签: java datetime zoneddatetime


【解决方案1】:

您的系统时区是什么?

我怀疑您可能处于观察或观察到 1893 年 4 月 1 日 DST 类型更改的语言环境中。尝试在循环的每次迭代中打印出 ZonedDateTimeoffset 值。

或者可以通过删除时区因素来收集更多信息LocalDateTime.ofInstant(zdt.toInstant(), ZoneOffset.UTC)


似乎这与柏林选择在该日期采用 CET 有关

tzdata 文件 europe 仅包含整个德国的一个区域 Europe/Berlin。

这不是最佳选择,原因如下:

- 柏林在 1893 年才开始 CET,比南部几个州晚。

https://mm.icann.org/pipermail/tz/2011-August/008736.html

【讨论】:

  • 时区始终为“Europe/Berlin”,但在该日期,时区偏移量从“+00:53:28”(4 月 1 日)变为“+01:00”(4 月 2 日)……
  • 谢谢你,ptomli。而已。当设置时间从午夜到中午时,日期是正确的。该死的夏令时...
  • 如前所述,这实际上不是 DST 更改,而是柏林决定采用 CET 的日期。日期/时间历史是一个引人入胜的主题;-)
  • 提醒一下,如果答案正确,请接受,谢谢
【解决方案2】:

来自JavaDocs of GregorianCalendar

public ZonedDateTime toZonedDateTime()

将此对象转换为 ZonedDateTime,它表示时间线上与此 GregorianCalendar 相同的点。

由于该对象支持儒略-格里高利转换日期而 ZonedDateTime 不支持,生成的年、月和日可能具有不同的值。结果将代表 ISO 日历系统中的正确日期,这对于修改儒略日也将是相同的值。

我认为这是某种非预期但预期的行为。

【讨论】:

  • 我认为该评论的目的是考虑到 1582 年 10 月 15 日左右的 Julian Gregorian 切换导致的差异
  • @ptomli 可能,我可能会在发现错误后立即删除此答案;-)
  • 不要删除,它确实会增加价值
【解决方案3】:

我怀疑这是 GregorianCalendar 中的错误。我以前在 Date 中看到过某些时区 1900 年之前日期的错误。我认为ZonedDateTime 和 java.time 中的其他类更可靠,更值得信赖。

1893 年 4 月 1 日 00:00 之前,柏林的偏移量为 +0:53:28。在第 89 年的某一天(为了举例),您的代码给出了 1893-03-29T23:53:28+00:53:28[Europe/Berlin] 的 ZonedDateTime,给出了正确的偏移量。但是您的 GregorianCalendar 和您的 ZonedDateTime 都代表 1893-03-29T23:00:00Z (UTC) 的时刻,因此 GregorianCalendar 似乎假设偏移量为 +01:00,这是错误的。转换正确地转换了时刻(也正确地转换了时区),因此错误地转换了各个日期和时间字段。

资料来源: 关于截至 1893 年的柏林偏移量,请访问 timeanddate.com 上的Time Zone in Berlin, Germany。在“多年来柏林的时间变化”下,在右上角的下拉列表中选择“1850 - 1899”。对于 1850-92 年,您将看到“无变化,UTC +0:53:28 小时”,并在 1893 年 4 月 1 日 00:00 更改为 CET/UTC +1。

这是我在你的循环中尝试过的:

    System.out.println(gc.getTimeZone().getID());
    System.out.println(gc.getTime());
    System.out.println(Instant.ofEpochMilli(gc.getTimeInMillis()));
    System.out.println();

    final ZonedDateTime zdt = gc.toZonedDateTime();

    System.out.println(zdt);
    System.out.println(zdt.toInstant());

一年中某天 (i) 89 的输出为:

Europe/Berlin
Thu Mar 30 00:00:00 CET 1893
1893-03-29T23:00:00Z

1893-03-29T23:53:28+00:53:28[Europe/Berlin]
1893-03-29T23:00:00Z

【讨论】:

    猜你喜欢
    • 2022-09-26
    • 1970-01-01
    • 1970-01-01
    • 2020-09-20
    • 1970-01-01
    • 2012-06-07
    • 2015-06-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多