将Date转换为LocalDate会在200AD左右返回奇怪的结果

Eva*_*les 7 java date java-8 java-time

在大约200年将Dates 转换为LocalDates时,我得到的结果不一致.使用以下代码进行转换:

  private LocalDate toLocalDate(Date localDate)
  {
    return LocalDateTime.ofInstant(localDate.toInstant(), ZoneId.systemDefault()).toLocalDate();
  }
Run Code Online (Sandbox Code Playgroud)

我的ZoneId.systemDefault()Africa/Harare,它与CAT测试中使用的相匹配.我运行的测试用例是

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US);
String dateString = "Tue Jan 01 00:00:00 CAT 200";
String dateString2 = "Tue Jan 01 00:00:00 CAT 201";
String dateString3 = "Wed Dec 31 00:00:00 CAT 200";

System.out.println(toLocalDate(simpleDateFormat.parse(dateString)));
System.out.println(toLocalDate(simpleDateFormat.parse(dateString2)));
System.out.println(toLocalDate(simpleDateFormat.parse(dateString3)));
Run Code Online (Sandbox Code Playgroud)

我的预期输出是

0200-01-01
0201-01-01
0200-12-31
Run Code Online (Sandbox Code Playgroud)

或者,如果不是这样,至少始终不正确的值.实际结果是

0199-12-31
0201-01-01
0200-12-31
Run Code Online (Sandbox Code Playgroud)

所以似乎第一个稍微回滚,可能是两个小时对应的CAT时区?但为什么这只发生在一个案例上呢?对2000年进行相同的实验不会产生相同的错误.

Jon*_*eet 6

斯蒂芬在评论中提供了解释.基本上,java.util.Date使用日历系统,在1582年切换朱利安日历系统公历系统,跳过10天.因此,1582年或之前的日期将出现差异 - 但差异的大小将随时间变化 - 平均每400年3天.碰巧在200到400AD之间,您没有看到这一点,因为这与差异为0时相对应.

这是一个简短但完整的程序来演示问题:

import java.time.*;
import java.util.*;

public class Test {
    public static void main(String[] args) throws Exception {
        // Value obtained with Noda Time: should be 0199-12-31T22:00:00Z.
        long millis = -55855792800000L;
        Instant instant = Instant.ofEpochMilli(millis);
        Date date = new Date(millis);
        System.out.println(instant);
        System.out.println(date);
    }
}
Run Code Online (Sandbox Code Playgroud)

我机器上的输出:

0199-12-31T22:00:00Z
Tue Jan 01 22:00:00 GMT 200
Run Code Online (Sandbox Code Playgroud)

你的初始代码中假设CAT和非洲/哈拉雷是相同的(在那个时候,非洲/哈拉雷被认为有+02:10的偏移量)以及你的错误日期名称,这一切都变得复杂了.字符串 - 但它是Java中导致此问题的错误.

我建议你使用java.time.format类来执行所有的解析- 然后我希望你不会得到这种不一致.

  • Java 8使用没有Gregorian切换的ISO日历系统,而java.util.Date使用1582年10天的标准切换.1月1日200日是在额外的闰日之前(2月29日200),因此是提前一天. (2认同)
  • @JodaStephen:谢谢 - 所以如果你比较java.util.Date和java.time.*,所以基本上1582之前的日期会逐渐远离彼此.会更新. (2认同)