有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

为什么使用Instant将1582之前的Java日期转换为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年是儒略历和格里高利历的分界点。我不知道为什么会发生这种情况,或者如何适应它

以下是我到目前为止得出的结论:

  • 日期对象的BaseCalender设置为JulianCalendar
  • date.toInstant()只返回Instant.ofEpochMilli(getTime())
  • date.getTime()返回-1483097400000
  • -1483097400000是星期三,1月10日下午5:00:00 GMT格里高利

所以看起来getTime()返回的毫秒要么是错误的(不太可能),要么只是与我预期的不同,我需要解释差异


共 (1) 个答案

  1. # 1 楼答案

    LocalDate只处理公历前的。从它的javadoc

    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类(在java.util.Date的toString()输出中也间接使用)使用了一个可配置的公历截止日期,默认为1582-10-15,作为朱利安和公历规则之间的分离日期

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

    但请记住,即使配置了依赖于区域的正确的截止日期,即使java.util.GregorianCalendar也经常失败。例如,英国在1752年之前的3月25日开始了这一年。许多国家还存在更多的历史偏差。在引入公历之前,欧洲以外甚至连儒略历都不可用(或者只能从殖民主义的角度使用)

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

    < >解释值{{CD6>},让我们考虑下面的代码及其输出:

    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_YorkUTC之间的时区偏移差异,您之前的评论中提到的-12219292800000L值与tCutOver值相差5小时。所以在美国东部时区(美国/纽约),我们正好有30228天的差异。对于所讨论的时间跨度,我们应用朱利安历法的规则,即每四年是闰年

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

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

    如果你真的想用JSR-310来做这件事,我重复一遍,格雷戈里的转换日期是不受支持的。最好的事情是,你可以做自己的工作

    例如,您可以考虑外部库{A2},它包含从发布0.9起的一个前置朱利安日历。但是,处理旧儒略历和新公历之间的转换仍然是你的努力。(由于新年开始等许多其他原因,不要指望这样的图书馆能够处理真正的历史日期。)

    2017年更新:另一个更强大的选项是使用我的库HistoricCalendar中的Time4J,它处理的不仅仅是julian/gregorian切换