Java Apache POI实现日期转Excel存储序列号问题求助
解决Java中dd-MM-yyyy日期转Excel日期序列号的问题
问题根源分析
你的代码存在几个关键问题,导致结果和Excel不一致:
- 旧
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日,完全偏离预期的起始日期,直接导致天数计算错误。 - Excel的1900闰年bug:
Excel错误地将1900年判定为闰年,在日期序列中虚拟了一个1900年2月29日,多算了一天。而Java的日期处理遵循真实历法,不会包含该日期,因此直接计算真实天数会和Excel序列产生动态差值(1900年3月1日之前差值为0,之后差值为1)。 - 毫秒差计算的潜在误差:
部分时区存在夏令时调整,导致某一天的毫秒数并非严格的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
相关产品推荐
相关产品推荐

