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

Java Apache POI实现日期转Excel存储序列号问题求助

解决Java中dd-MM-yyyy日期转Excel日期序列号的问题

问题根源分析

你的代码存在几个关键问题,导致结果和Excel不一致:

  1. 旧java.util.Date构造器参数错误:
    旧Date的Date(int year, int month, int day)构造器中,year是相对于1900的偏移量(输入1900实际代表3800年),month是0-based(0对应1月,1对应2月)。你写的new Date(1900,1,1)实际指向的是3800年2月1日,完全偏离预期的起始日期,直接导致天数计算错误。
  2. Excel的1900闰年bug:
    Excel错误地将1900年判定为闰年,在日期序列中虚拟了一个1900年2月29日,多算了一天。而Java的日期处理遵循真实历法,不会包含该日期,因此直接计算真实天数会和Excel序列产生动态差值(1900年3月1日之前差值为0,之后差值为1)。
  3. 毫秒差计算的潜在误差:
    部分时区存在夏令时调整,导致某一天的毫秒数并非严格的86400*1000,直接整除会出现计算偏差。

正确解决方案

方案1:使用Apache POI自带工具类(推荐)

Apache POI提供了DateUtil类,专门处理Excel日期与Java日期的转换,已封装了1900闰年bug的处理逻辑,无需手动计算。

代码示例(处理dd-MM-yyyy格式字符串):

import org.apache.poi.ss.usermodel.DateUtil;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ExcelDateConverter {
    public static int convertToExcelSerial(String ddMmYyyyDate) throws Exception {
        // 解析dd-MM-yyyy格式的日期字符串
        SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");
        Date date = sdf.parse(ddMmYyyyDate);
        
        // 获取Excel日期序列号(自动处理1900闰年bug)
        double excelSerial = DateUtil.getExcelDate(date);
        return (int) excelSerial;
    }

    public static void main(String[] args) throws Exception {
        // 测试2022年12月31日,输出44926,与Excel一致
        System.out.println(convertToExcelSerial("31-12-2022"));
    }
}

如果使用Java 8+的新日期API(LocalDate),可以这样转换:

import org.apache.poi.ss.usermodel.DateUtil;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.time.ZoneId;

public class ExcelDateConverter {
    public static int convertToExcelSerial(String ddMmYyyyDate) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
        LocalDate localDate = LocalDate.parse(ddMmYyyyDate, formatter);
        
        // 将LocalDate转为Date
        Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
        double excelSerial = DateUtil.getExcelDate(date);
        return (int) excelSerial;
    }
}

方案2:手动计算(仅用于理解原理,不推荐生产环境)

如果需要手动实现,必须处理Excel的1900闰年bug,同时使用可靠的日期差计算方式:

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public class ExcelDateConverter {
    public static int convertToExcelSerial(String ddMmYyyyDate) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
        LocalDate targetDate = LocalDate.parse(ddMmYyyyDate, formatter);
        LocalDate excelEpoch = LocalDate.of(1900, 1, 1);
        
        // 计算从1900-01-01到目标日期的天数(Excel序列号从1开始,需加1)
        long days = ChronoUnit.DAYS.between(excelEpoch, targetDate);
        long excelSerial = days + 1;
        
        // 处理Excel的1900闰年bug:1900年3月1日及之后的日期加1
        if (targetDate.isAfter(LocalDate.of(1900, 2, 28))) {
            excelSerial += 1;
        }
        
        return (int) excelSerial;
    }

    public static void main(String[] args) {
        System.out.println(convertToExcelSerial("01-01-1900")); // 输出1,与Excel一致
        System.out.println(convertToExcelSerial("28-02-1900")); // 输出59,与Excel一致
        System.out.println(convertToExcelSerial("01-03-1900")); // 输出61,与Excel一致
        System.out.println(convertToExcelSerial("31-12-2022")); // 输出44926,与Excel一致
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 05:50:27