【问题标题】:Discrepancy when converting ancient dates between java.util.Date and java.time.Instant在 java.util.Date 和 java.time.Instant 之间转换古代日期时的差异
【发布时间】:2018-09-18 19:47:24
【问题描述】:

我有使用 java.util.Date 创建古代日期(0002 年 11 月 30 日)的旧代码。我正在尝试更新我可以更新的代码,但这需要在 Date 和 LocalDate 等之间进行转换。我无法完全摆脱 Date 的使用或古老的日期选择。

我发现使用这个古老的日期在 Date 和 Instant 之间来回转换时出现错误,希望有人能解释发生了什么。

这是一个示例:

    Date date = new Date();
    Instant instant = date.toInstant();
    System.out.println("Current:");
    System.out.println("Date: "+date);
    System.out.println("Instant: "+instant);
    System.out.println("Date epoch:    "+date.getTime());
    System.out.println("Instant epoch: "+instant.getEpochSecond()*1000);

    System.out.println("\nAncient from Date:");
    Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("MST"));
    cal.set(2, Calendar.NOVEMBER, 30, 0, 0, 0);
    date = cal.getTime();
    instant = date.toInstant();
    System.out.println("Date: "+date);
    System.out.println("Instant: "+instant);
    System.out.println("Date epoch:    "+date.getTime());
    System.out.println("Instant epoch: "+instant.getEpochSecond()*1000);

    System.out.println("\nAncient from Instant:");
    instant = Instant.parse("0002-11-30T00:00:00Z");
    date = Date.from(instant);
    System.out.println("Date: "+date);
    System.out.println("Instant: "+instant);
    System.out.println("Date epoch:    "+date.getTime());
    System.out.println("Instant epoch: "+instant.getEpochSecond()*1000);

打印以下内容:

Current:
Date: Tue Sep 18 12:34:27 MST 2018
Instant: 2018-09-18T19:34:27.177Z
Date epoch:    1537299267177
Instant epoch: 1537299267000

Ancient from Date:
Date: Thu Nov 30 00:00:00 MST 2
Instant: 0002-11-28T07:00:00.247Z
Date epoch:    -62075437199753
Instant epoch: -62075437200000

Ancient from Instant:
Date: Fri Dec 01 17:00:00 MST 2
Instant: 0002-11-30T00:00:00Z
Date epoch:    -62075289600000
Instant epoch: -62075289600000

因此,如果我在 21 月 30 日创建即时消息,然后转换为日期,则日期为 2 月 1 日。如果我从 11 月 30 日的日期开始,即时消息为 21 月 28 日。我知道Date 和 Instant 都不存储时区信息,但为什么根据我是从 Date 还是 Instant 开始,时代如此不同?无论如何我可以解决这个问题吗?我需要能够从 Date 或 Instant 开始,并以相同的 epoch 值结束。也很高兴知道为什么默认 toString() 在相​​同的纪元下显示如此不同的日期。

【问题讨论】:

  • 你有一个舍入错误。不要使用instant.getEpochSecond()*1000,使用instant.toEpochMilli()
  • 这两个框架并没有 100% 同意关于闰年的细节,因为它可以追溯到这么久远的历史。所以你需要明确你的要求。如果你有一个Date 在纪元前的一定天数,你是否需要一个Instant 在纪元前的相同天数?或者,如果您的日期是 11 月 2 日,您是否需要 11 月 2 日的 Instant?不一样,抱歉。
  • @teppic,谢谢,我没有注意到那里的方法。出于问题的目的,我认为毫秒值并不重要,尽管我可以看到这使示例变得混乱。
  • @OleV.V.我正在处理的代码是使用一个神奇的日期(让这对你来说是一个教训,孩子们),所以我想有一个即时日期和 11 月 2 日相同的日期,尽管我想我可以做任何一个这些选项中的一个。诀窍是有时我从 Instant 开始,有时我从 Date 开始。根据您的评论,我认为闰年让我感到悲伤......

标签: java date java-8


【解决方案1】:

差异在于DateInstant 的实现如何在其实现方面相互交互,其中 Date 使用公历/儒略历,而 Instant 使用 ISO 标准的 Date,它们遵循修改后的公历到儒略历切换。

GregorianCalendarimplementation 有一个特别说明:

在公历转换之前,GregorianCalendar 实现了儒略历。公历和儒略历的唯一区别是闰年规则。儒略历每四年指定闰年,而公历省略不能被 400 整除的世纪年。

嗯,是的,从技术上讲。但是对于这个问题,我们相当不会遇到这种情况。

cal.set(1582, Calendar.OCTOBER, 4, 0, 0, 0);

正如预期的那样,这会产生一个日期,即 1582 年 10 月 4 日。

cal.set(1582, Calendar.OCTOBER, 5, 0, 0, 0);

这会产生一个日期,即 1582 年 10 月 15

这是什么魔法,蝙蝠侠?

嗯,这不是编码错误,它实际上是 GregorianCalendar 的实现。

然而,今年公历开始转换,当时被称为 Inter gravissimas 的教皇诏书引入了公历,从一开始就被西班牙、葡萄牙、波兰立陶宛联邦和当今意大利的大部分地区采用.在这些国家/地区,这一年一直持续到10 月 4 日星期四。但是,第二天变成了 10 月 15 日星期五(就像从星期五开始的普通年份一样),

来自Wikipedia on 1582

当我们检查 1582 年 10 月 4 日时,会发生以下情况:

日期:1582-Oct-04 00:00:00

即时:1582-10-14T00:00:00Z

这里有 10 天的间隔,而“技术上不存在的日期”这一瞬间存在的原因是 ISO instant dates 的定义。

标准规定每个日期都必须是连续的,因此使用儒略历会违反标准(因为在切换日期,日期不会是连续的)。

所以,虽然 1582 年 10 月 14 日在现实中从未存在,但根据定义,它存在于 ISO 时间,但根据儒略历,它发生在现实世界的 1582 年 10 月 4 日。

由于我假设与第一段相比有额外的闰年漂移,其中儒略世纪 1500、1400、1300、1100、1000、900、700、600、500、300、200、100 没有额外的闰日因为在公历中,我们慢慢地从 +10 偏移到 -1 偏移。这可以通过以 +100 为增量调整年份来验证。

如果您要显示历史事件日期,最好使用DateJulianCalendar DateFormatter 来显示正确正确的历史日期,因为它实际发生在历史中。打印出历史时期的 ISO 时间可能显得荒谬或不准确,但以这种格式存储时间仍然有效。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-05-30
    • 2012-02-04
    • 2012-06-26
    • 1970-01-01
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多