Discrepancy when converting ancient dates between java.util.Date and java.time.Instant

时间:2018-09-18 19:47:24

标签: java date java-8

I have legacy code that uses java.util.Date creating an ancient date (30 Nov 0002). I'm trying to update what code I can, but that's necessitating converting between Date and LocalDate, etc. I can't completely get rid of the use of Date or the ancient date choice.

I'm finding what appears to be an error when converting back and forth between Date and Instant with this ancient date, and was hoping someone could explain what is going on.

Here's a sample:

    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);

Which prints the following:

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

So if I create an Instant at 30 Nov 2, then convert to a Date, the Date is 1 Dec 2. If I start with a Date at 30 Nov 2, the Instant is 28 Nov 2. I'm aware that neither Date nor Instant store timezone information, but why are the epochs so different based on whether I started with a Date vs. an Instant? Is there anyway I can work around that? I need to be able to start with either a Date or an Instant, and end up with the same epoch value. It would also be nice to know why the default toString() shows such different dates given the same epoch.

1 个答案:

答案 0 :(得分:7)

差异之处在于DateInstant的实现方式之间的相互影响,其中Date使用公历/朱利安日历,而Instant使用ISO标准的Date,紧随在儒略历切换之前修改了公历。

GregorianCalendar implementation有一个特殊说明:

  

在公历转换之前,GregorianCalendar会执行儒略历。阳历和儒略历之间的唯一区别是the年规则。朱利安历法指定每四年leap年,而公历忽略了不能被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年10月4日00:00:00

     

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

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

  

该标准规定每个日期必须是连续的,因此朱利安历法的使用将与该标准相反(因为在转换日期,日期将不是连续的)。

因此,尽管1582年10月14日实际上并不存在,但按照定义,它存在于ISO时间中,但根据朱利安·日历,它出现在现实世界的1582年10月4日。

由于我假设第一段有额外的leap年漂移,因此朱利安世纪1500、1400、1300、1100、1000、900、700、600、500、300、200、100没有考虑额外的leap日在公历中,我们从+10缓慢移回-1。可以通过以+100为增量调整年份来验证这一点。

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