NPOI解析Excel日期出错主因是未识别工作簿的1904纪元(Mac旧版)与1900纪元(Windows)差异,导致日期偏移1462天;需通过workbook.IsDate1904判断纪元,用DateUtil.GetDateTimeFromExcel配合校正,并显式设置CellStyle绑定日期格式。

Excel日期在NPOI里为什么总是错成1900-1-0?
因为NPOI默认按Excel的1900纪元(1900年1月1日为第1天)解析日期,但Excel本身在Windows平台实际使用的是1900纪元,而Mac旧版用1904纪元——NPOI却**不自动识别工作簿的BaseDate**。一旦你读取的Excel是用Mac另存或含1904日期系统,cell.DateCellValue就会偏移1462天(即4年),表现为“1900-1-0”或“1899-12-31”这类非法日期。
实操建议:
- 先检查工作簿是否启用1904日期系统:
workbook.IsDate1904—— 返回true说明是1904纪元,需手动校正 - 不要直接信任
cell.CellType == CellType.Numeric就调cell.DateCellValue,先用cell.GetCellType()确认类型,并结合cell.CachedFormulaResultType判断是否为公式生成的日期 - 对疑似日期的数值型单元格,优先用
cell.NumericCellValue拿到原始double值,再按纪元转换:double raw = cell.NumericCellValue; DateTime dt = workbook.IsDate1904 ? DateUtil.GetDateTimeFromExcel(raw, true) // 第二个参数true表示1904 base : DateUtil.GetDateTimeFromExcel(raw, false);
NPOI读自定义日期格式(如“yyyy年MM月dd日”)会丢格式?
会。NPOI的cell.StringCellValue只返回渲染后的文本,但前提是该单元格**已被Excel引擎计算并缓存了显示值**;如果Excel文件是程序生成、未打开重算过,或单元格格式是自定义但值仍是纯数字(比如输入2023/5/20后设格式为“yyyy年MM月dd日”),StringCellValue可能为空或返回数字原始值。
实操建议:
- 优先走数值路径:用
cell.NumericCellValue+DateUtil.GetDateTimeFromExcel(...)拿到DateTime对象,再用C#格式化:dt.ToString("yyyy年MM月dd日") - 若必须依赖Excel端格式,需确保源文件已触发重算(比如用Excel打开保存一次),或改用
cell.CellStyle.DataFormatString获取格式字符串(注意:该字符串是Excel内部编码,如"yyyy"m"月"dd"日"需手动映射,不可直接传给ToString()) - 避免用
cell.RichStringCellValue.ToString()试图“绕过”——它和StringCellValue底层共享同一缓存,无额外信息
为什么DateUtil.GetDateTimeFromExcel返回null或异常?
常见于三种情况:数值超出Excel日期范围(0 <= value <= 2958465)、传入了非日期型数值(如纯百分比、科学计数)、或value是Excel的错误码(如Double.NaN、Double.PositiveInfinity)。
实操建议:
- 始终做范围校验:
double v = cell.NumericCellValue; if (double.IsNaN(v) || v < 0 || v > 2958465) { // 跳过或记录为无效日期 } - 别假设所有Numeric类型都是日期——Excel里时间、百分比、普通数字都存为double,得结合
cell.CellStyle.DataFormat判断(例如DataFormat == 14大概率是短日期,22是带时间的,但自定义格式时该值可能为0或负数) - 遇到
GetDateTimeFromExcel抛InvalidOperationException,多数是因为传了0且is1904参数与实际工作簿不符,务必核对workbook.IsDate1904
用NPOI写入日期时,为什么Excel打开显示为数字?
因为你只设置了单元格值(cell.SetCellValue(DateTime.Now)),但没指定单元格样式。NPOI不会自动为你套用“日期格式”,写入后Excel默认按“常规”显示,于是呈现为序列号(如45123)。
实操建议:
- 必须显式创建并绑定CellStyle:
ICellStyle dateStyle = workbook.CreateCellStyle(); IDateFormat df = workbook.CreateDataFormat(); dateStyle.DataFormat = df.GetFormat("yyyy-mm-dd"); cell.CellStyle = dateStyle; cell.SetCellValue(DateTime.Now); - 格式字符串要用Excel认可的(不是C#的),
"yyyy-mm-dd"、"h:mm:ss AM/PM"可以,但"yyyy年MM月dd日"在部分NPOI版本中可能不生效,建议先用Excel验证该格式是否能被识别 - 如果写入后仍显示数字,右键单元格→“设置单元格格式”→看“数字”选项卡里是否显示为“自定义”,若是,说明样式已生效,只是Excel当前视图缩放或列宽导致没显示全——这不是NPOI的问题
IsDate1904标志、单元格原始数值的合法性、以及样式是否真正绑定成功。漏掉任意一个,日期就可能无声无息地错掉几年或变成一串数字。


















