11

考虑这段代码:

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 年是儒略历和公历之间的分界线。我不知道为什么会发生这种情况,或者如何调整它。

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

  • 日期对象有一个BaseCalender设置为JulianCalendar
  • date.toInstant()刚回来Instant.ofEpochMilli(getTime())
  • date.getTime()返回 -14830974000000
  • -14830974000000 是星期三,1500 年 1 月 10 日 05:00:00 GMT公历

因此,似乎返回的毫秒getTime()数是错误的(不太可能),或者只是与我预期的不同,我需要考虑差异。

4

1 回答 1

19

LocalDate仅处理预测公历。从它的javadoc

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

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

因此LocalDate不适用于任何类型的历史日期。

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

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

为了解释这个值,-14830974000000让我们考虑以下代码及其输出:

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

应该注意的是,由于 和 之间的时区偏移差异,-12219292800000L您之前评论中提到的值与 5 小时不同。所以在 EST 时区(美国/纽约)我们正好有 30228 天的差异。对于所讨论的时间跨度,我们应用儒略历规则,即每四年为闰年。tCutOverAmerica/New_YorkUTC

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

请向我解释为什么您期望的值不同于 -14830974000000 毫秒。它看起来对我来说是正确的,因为它处理您系统的时区偏移、1582 年之前的儒略历规则以及从 1582 年 10 月 4 日到切换日期 1582-10-15 的跳转。所以对我来说,你的问题是“我如何告诉日期对象将 ms 返回到正确的公历日期?” 已经回答 - 无需更正。请记住,这种复杂的东西在生产中使用了相当长的时间,并且可以预期在这么多年后正常工作。

如果您真的想将 JSR-310 用于这些东西,我重申不支持公历截止日期。最好的事情是您可以自己解决问题。

例如,您可能会考虑外部库Threeten-Extra,其中包含自 0.9 版以来的预测儒略历。但是,处理旧儒略历和新公历之间的转换仍将是您的努力。(并且由于新年开始等许多其他原因,不要期望此类库能够处理真实的历史日期。)

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

于 2014-06-01T08:30:48.617 回答