You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

java.util.Date转LocalDate结果异常:期望0001-01-01却得0000-12-30

Date转LocalDate出现日期偏差的原因与修复方案

问题代码

Date date = new Date();
Calendar cal1 = Calendar.getInstance();

cal1.set(Calendar.YEAR, 0001);
cal1.set(Calendar.DAY_OF_MONTH, 1);
cal1.set(Calendar.MONTH, Calendar.JANUARY);
cal1.set(Calendar.HOUR_OF_DAY, 0);
cal1.set(Calendar.MINUTE, 0);
cal1.set(Calendar.SECOND, 0);
cal1.set(Calendar.MILLISECOND, 0);

date.setTime(cal1.getTimeInMillis());

System.out.println(date); //Sat Jan 01 00:00:00 IST 1

LocalDate localDate = Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();

System.out.println(localDate); //0000-12-30

问题描述

将设置为0001年1月1日的Date对象转换为LocalDate时,期望结果为0001-01-01,但实际输出是0000-12-30。

原因分析

  1. 历法差异:java.util.Calendar(默认实现为GregorianCalendar)在处理公元1年及更早的日期时,采用儒略历规则;而Java 8引入的LocalDate遵循ISO 8601标准的格里高利历。两种历法在公元初年的日期映射存在固定偏移——儒略历的公元1年1月1日,对应格里高利历的0000-12-30。
  2. 旧API设计缺陷:Date/Calendar类本身对早期年份的支持不严谨,加上转换时依赖系统时区(如示例中的IST),进一步暴露了偏差问题。

修复方案

方案一:直接使用Java 8+新日期API(推荐)

完全抛弃旧API,直接用LocalDate创建目标日期,从根源避免问题:

import java.time.LocalDate;
import java.time.Month;

public class DateFix {
    public static void main(String[] args) {
        LocalDate localDate = LocalDate.of(1, Month.JANUARY, 1);
        System.out.println(localDate); // 0001-01-01
    }
}

方案二:从旧API正确转换(兼容场景)

如果必须处理已有的Calendar/Date对象,需明确指定历法规则,确保转换一致性:

import java.time.LocalDate;
import java.util.Date;
import java.util.GregorianCalendar;

public class DateFix {
    public static void main(String[] args) {
        GregorianCalendar cal1 = new GregorianCalendar();
        // 强制Calendar全程使用儒略历,匹配早期日期的处理逻辑
        cal1.setGregorianChange(new Date(Long.MIN_VALUE));
        cal1.set(GregorianCalendar.YEAR, 1);
        cal1.set(GregorianCalendar.DAY_OF_MONTH, 1);
        cal1.set(GregorianCalendar.MONTH, GregorianCalendar.JANUARY);
        cal1.set(GregorianCalendar.HOUR_OF_DAY, 0);
        cal1.set(GregorianCalendar.MINUTE, 0);
        cal1.set(GregorianCalendar.SECOND, 0);
        cal1.set(GregorianCalendar.MILLISECOND, 0);

        // 通过GregorianCalendar的toZonedDateTime方法转换,保持历法一致性
        LocalDate localDate = cal1.toZonedDateTime().toLocalDate();
        System.out.println(localDate); // 0001-01-01
    }
}

内容的提问来源于stack exchange,提问作者Rohit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 16:29:53