【问题标题】:Converting Date to LocalDate returning strange results around 200AD将 Date 转换为 LocalDate 会在 200AD 左右返回奇怪的结果
【发布时间】:2015-12-29 04:23:41
【问题描述】:

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

  private LocalDate toLocalDate(Date localDate)
  {
    return LocalDateTime.ofInstant(localDate.toInstant(), ZoneId.systemDefault()).toLocalDate();
  }

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

我对此的预期输出是

0200-01-01
0201-01-01
0200-12-31

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

0199-12-31
0201-01-01
0200-12-31

所以似乎第一个正在略微回滚,可能是与CAT 时区对应的两个小时?但是为什么这只发生在一个案例上呢?用 2000 年做同样的实验不会产生同样的错误。

【问题讨论】:

  • 附注 - 使用 localDate 的名称作为 Date 类型的变量是相当混乱的,因为 LocalDate 类型的存在具有非常不同的含义。
  • 如果不出意外,日期名称就被破坏了 - 如果 200 年 12 月 31 日是星期三,那么 201 年 1 月 1 日怎么会是星期二?
  • @Puce 你意识到时区的概念在公元 200 年并不存在。
  • 请注意,CAT 与 Africa/Harare - CAT 被视为“UTC+2”,而 Africa/Harare 的偏移量为 +02:10 :20 那时。
  • 是的,应该。基本上据我所知,这里有 三个 不好的地方 - 日期名称、CAT == Africa/Harare 的假设以及 Java 8 错误。我正在尝试将其隔离为 just 错误,届时我将发布答案。

标签: java date java-8 java-time


【解决方案1】:

斯蒂芬在评论中提供了解释。基本上,java.util.Date 使用日历系统,它在 1582 年在Julian calendar systemGregorian calendar system 之间切换,跳过 10 天。因此,1582 年或更早的日期会出现差异——但差异的大小会随时间而变化——平均每 400 年相差 3 天。碰巧在公元 200 到 400 年间,您没有看到这一点,因为这对应于差异为 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);
    }
}

我的机器上的输出:

0199-12-31T22:00:00Z
Tue Jan 01 22:00:00 GMT 200

由于假设 CAT 和 Africa/Harare 相同(在那个时间点,Africa/Harare 被认为具有 +02:10 的偏移量)和错误的日期,这一切都变得复杂了您的字符串中的名称 - 但这是导致问题的 Java 中的错误。

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

【讨论】:

  • 最初导致我发现此问题的代码是在尝试更新代码以使用新功能时在DateLocalDate 之间来回移动,因此不幸的是那里没有解析 - 它最终使用Date.from(instant),它使用相同的构造函数。我会寻找另一种转换方式,谢谢。
  • 根据这个答案:stackoverflow.com/questions/22929237/… 这似乎是在日期类型之间转换的标准方法,这很不方便。
  • Java 8 使用没有公历转换的 ISO 日历系统,而 java.util.Date 使用 1582 年 10 天的标准转换。200 年 1 月 1 日在一个额外的闰日之前(200 年 2 月 29 日),提前一天也是如此。
  • @JodaStephen:谢谢——所以如果你比较 java.util.Date 和 java.time.*?会更新。
猜你喜欢
  • 2013-07-31
  • 2018-07-19
  • 2019-12-10
  • 1970-01-01
  • 2020-02-28
  • 2020-09-24
  • 2012-08-08
  • 2012-12-09
  • 1970-01-01
相关资源
最近更新 更多