为什么在1582之前将Java日期转换为使用Instant的LocalDate给出不同的日期? [英] Why does converting Java Dates before 1582 to LocalDate with Instant give a different date?

查看:181
本文介绍了为什么在1582之前将Java日期转换为使用Instant的LocalDate给出不同的日期?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下代码:

Date date = new SimpleDateFormat("MMddyyyy").parse("01011500");

LocalDate localDateRight = LocalDate.parse(formatter.format(date), dateFormatter);
LocalDate localDateWrong = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()).toLocalDate();

System.out.println(date);           // Wed Jan 01 00:00:00 EST 1500
System.out.println(localDateRight); // 1500-01-01
System.out.println(localDateWrong); // 1500-01-10

我知道1582是Julian和Gregorian日历之间的截止点。我不知道为什么会发生这种情况,或者如何调整它。

I know that 1582 is the cutoff between the Julian and Gregorian calendars. What I don't know is why this happens, or how to adjust for it.

这是我到目前为止所知道的:

Here's what I've figured out so far:


  • 日期对象的 BaseCalender 设置为 JulianCalendar

  • date.toInstant()只返回 Instant.ofEpochMilli(getTime())

  • date.getTime()返回-14830974000000

  • -14830974000000是星期三, 10 Jan 1500 05:00:00 GMT Gregorian

  • The date Object has a BaseCalender set to JulianCalendar
  • date.toInstant() just returns Instant.ofEpochMilli(getTime())
  • date.getTime() returns -14830974000000
  • -14830974000000 is Wed, 10 Jan 1500 05:00:00 GMT Gregorian

所以看起来像是由<返回的毫秒code> getTime()错误(不太可能)或者与我预期的不同,我需要考虑差异。

So it seems like either the millis returned by getTime() is wrong (unlikely) or just different than I expect and I need to account for the difference.

推荐答案

LocalDate 处理预感格里历日历只。来自其 javadoc


ISO-8601日历系统是现今世界大部分地区使用
的现代民用日历系统。它相当于preleptic
公历系统,其中今天的闰年规则是
所有时间都适用。对于今天编写的大多数应用程序,
ISO-8601规则是完全合适的。但是,任何
使用历史日期并且要求它们准确的应用程序,
会发现ISO-8601方法不合适。

The ISO-8601 calendar system is the modern civil calendar system used today in most of the world. It is equivalent to the proleptic Gregorian calendar system, in which today's rules for leap years are applied for all time. For most applications written today, the ISO-8601 rules are entirely suitable. However, any application that makes use of historical dates, and requires them to be accurate will find the ISO-8601 approach unsuitable.

相比之下,旧的 java.util.GregorianCalendar 类(间接也用于toString() - 输出 java.util.Date )使用可配置的格里高利截止值默认为1582-10-15作为朱利安和格里高利历法规则之间的分隔日期。

In contrast, the old java.util.GregorianCalendar class (which is indirectly also used in toString()-output of java.util.Date) uses a configurable gregorian cut-off defaulting to 1582-10-15 as separation date between julian and gregorian calendar rules.

所以 LocalDate 不适用于任何历史日期。

So LocalDate is not useable for any kind of historical dates.

但请记住,即使<$即使配置了正确区域相关的截止日期,c $ c> java.util.GregorianCalendar 也经常失败。例如,英国在1752年之前的3月25日开始了这一年。许多国家还有更多的历史偏差。在欧洲之外甚至朱利安历法在引入格里高利历之前是不可用的(或者只能从殖民主义的角度来看最好)。

But bear in mind that even java.util.GregorianCalendar often fails even when configured with correct region-dependent cut-off date. For example UK started the year on March 25th before 1752. And there are many more historical deviations in many countries. Outside of Europe even the julian calendar is not useable before introduction of gregorian calendar (or best useable only from a colonialist perspective).

由于评论中的问题而更新:

要解释值 -14830974000000 让我们考虑以下代码及其输出:

To explain the value -14830974000000 let's consider following code and its output:

SimpleDateFormat format = new SimpleDateFormat("MMddyyyy", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("America/New_York"));
Date d = format.parse("01011500");

long t1500 = d.getTime();
long tCutOver = format.parse("10151582").getTime(); 
System.out.println(t1500); // -14830974000000
System.out.println(tCutOver); // default gregorian cut off day in "epoch millis"
System.out.println((tCutOver - t1500) / 1000); // output: 2611699200 = 30228 * 86400

应该注意的是值 America / New_York之间的时区偏移差异,您之前评论中提到的> -12219292800000L 与 tCutOver 相差5小时和 UTC 。因此,在时区EST(America / New_York),我们有30228天的差异。对于有问题的时间跨度,我们应用julian日历的规则,即每四年是闰年。

It should be noted that the value -12219292800000L mentioned in your earlier comment is different by 5 hours from tCutOver due to timezone offset difference between America/New_York and UTC. So in timezone EST (America/New_York) we have exactly 30228 days difference. For the timespan in question we apply the rules of julian calendar that is every fourth year is a leap year.

在1500和1582之间我们有82 * 365天+ 21跳跃天。然后我们还要在1582-01-01和1582-10-01之间添加273天,最后4天直到切换(记住10月4日之后是10月15日)。总计:82 * 365 + 21 + 273 + 4 = 30228(需要证明的内容)。

Between 1500 and 1582 we have 82 * 365 days + 21 leap days. Then we have also to add 273 days between 1582-01-01 and 1582-10-01, finally 4 days until cut-over (remember 4th of Oct is followed by 15th of Oct). At total: 82 * 365 + 21 + 273 + 4 = 30228 (what was to be proved).

请向我解释为什么你预期价值不同于 - 14830974000000毫秒它看起来对我来说是正确的,因为它处理你的系统的时区偏移,1562年之前的朱利安日历规则以及从1582年10月4日到1582-10-15的截止日期的跳跃。所以对我来说你的问题如何告诉日期对象将ms返回到正确的格里高利日期?已经回答 - 无需更正。请记住,这些复杂的东西在生产中使用相当长,并且可以在这么多年后正常工作。

Please explain to me why you have expected a value different from -14830974000000 ms. It looks correct for me since it handles the timezone offset of your system, the julian calendar rules before 1582 and the jump from 4th of Oct 1582 to cut-over date 1582-10-15. So for me your question "how do I tell the date object to return the ms to the correct Gregorian date?" is already answered - no correction needed. Keep in mind that this complex stuff is a pretty long time in production use and can be expected to work correctly after so many years.

如果你真的想使用JSR-对于那些东西,我再说一遍,没有支持格里高利切换日期。最好的事情是你可以自己动手。

If you really want to use JSR-310 for that stuff I repeat that there is no support for gregorian cut-over date. The best thing is that you might do your own work-around.

例如你可以考虑外部库 Threeten-Extra ,其中包含自0.9版以来的预感朱利安日历。但它仍然是你努力处理旧朱利安日历和新格里高利历之间的切换。 (并且由于新年开始等许多其他原因,不要指望这些图书馆能够处理真实的历史日期。)

For example you might consider the external library Threeten-Extra which contains a proleptic julian calendar since release 0.9. But it will still be your effort to handle the cut-over between old julian calendar and new gregorian calendar. (And don't expect such libraries to be capable of handling REAL historic dates due to many other reasons like new year start etc.)

2017年更新:另一个更强大的选择是使用 HistoricCalendar 我的图书馆 Time4J ,它不仅仅处理julian / gregorian-cutover。

Update in year 2017: Another more powerful option would be using HistoricCalendar of my library Time4J which handles much more than just julian/gregorian-cutover.

这篇关于为什么在1582之前将Java日期转换为使用Instant的LocalDate给出不同的日期?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆