Excel日期序列号是自1900年1月1日起的天数(如44197对应2021年1月1日),PHP读取时若未转换会直接返回该数字而非格式化日期,需用excelToDateTimeObject等方法正确解析。

Excel日期序列号是什么,为什么PHP直接读出来是数字
Excel内部用“从1900年1月1日起的天数”表示日期,比如44197对应2021年1月1日——这不是Unix时间戳,也不是毫秒数,而是纯整数序列号。PHP用PhpSpreadsheet或PHPExcel读取单元格时,如果单元格格式是日期但未显式转为字符串,getCell()->getValue()返回的就是这个原始数字,不是你预期的"2021-01-01"。
常见错误现象:
– 导入后显示44197而不是日期
– 用date('Y-m-d', $val)得到1970年某天(因为把序列号当成了秒级时间戳)
– 手动加减86400没用,因为单位根本不是秒
- Excel的起始基准是
1900-01-01(注意:它错误地把1900年当作闰年,所以1900-02-29被当成有效日期;但对2000年以后的日期影响可忽略) - PHP的
strtotime()、DateTime等函数完全不认这个序列号,必须手动转换 - 如果你用的是
PhpSpreadsheet,推荐优先调用getCell()->getFormattedValue(),它会按Excel单元格格式自动转成字符串(但依赖本地locale和格式设置,不稳定)
标准转换公式:Excel序列号 → PHP DateTime对象
最可靠的方式是手动计算:把序列号换算成距Unix纪元(1970-01-01)的天数,再转为时间戳。关键偏移量是25569——这是Excel序列号中1970-01-01对应的值(Excel从1900-01-01起算第1天,Unix从1970-01-01起算第0秒,两者相差25569天)。
所以转换逻辑是:$timestamp = ($excelDate - 25569) * 86400,再用date('Y-m-d H:i:s', $timestamp)或new DateTime("@$timestamp")构造对象。
立即学习“PHP免费学习笔记(深入)”;
诊断并修复涉及公式、命名范围、Power Query 刷新、数据透视表、VBA/宏保存等问题的 Microsoft Excel XLSX 工作簿
- 必须检查
$excelDate是否为数值类型,空单元格可能返回NULL或0,0会变成1900-01-00,PHP会 silently 转成1899-12-31 - Excel序列号含小数部分表示时间(如
44197.5= 中午12:00),所以别用(int)强制截断 - 如果Excel文件是在Mac上创建的(旧版),可能用1904年历法(起始为1904-01-01),此时基准值应为
24107;但现代PhpSpreadsheet已自动识别1904模式,你只需确认$reader->getWorksheetIterator()->current()->getParent()->isDate1904(),一般不用手动处理
用PhpSpreadsheet安全读取日期列的实操建议
不要依赖getValue(),也不要无条件用getFormattedValue()。稳妥做法是:先判断单元格是否为日期类型,再决定如何解析。
- 用
$cell->getDataType() === \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC初步筛选 - 再用
\PhpOffice\PhpSpreadsheet\Shared\Date::isDateTime($cell)确认是否为日期格式(它会检查数字格式代码,如"yyyy-mm-dd"或"h:mm:ss") - 确认是日期后,调用
\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($excelDate)——这是官方封装的安全转换函数,内部已处理1900/1904历法、闰年、小数时间等细节 - 如果该函数报错(如传入非数字),说明上游数据异常,应记录日志并跳过,而非静默返回
1970-01-01
示例片段:
if (\PhpOffice\PhpSpreadsheet\Shared\Date::isDateTime($cell)) {
try {
$dt = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($cell->getValue());
$dateStr = $dt->format('Y-m-d H:i:s');
} catch (Exception $e) {
// 记录 $cell->getValue() 原始值用于排查
$dateStr = null;
}
}导入时遇到“1900-01-00”或“1899-12-31”怎么快速定位
这几乎肯定是序列号为0或负数导致的。Excel里空白日期单元格有时会存为0,而0 - 25569 = -25569天,PHPDateTime会回绕到19世纪末。
- 在转换前加守卫:
if ($excelDate <= 1) { return null; }(1对应1900-01-01,小于等于1基本可判定为空或错误) - 批量导入时,在日志里打印出问题行号 + 列名 +
$cell->getValue()原始值,比只打1899-12-31有用得多 - 前端Excel模板可预先设置日期列为“自定义格式”,内容为空时显示
-而非留白,减少0值写入 - 如果业务允许,数据库日期字段设为
NULLABLE,PHP层转不出有效日期就存NULL,别硬塞默认值
真正麻烦的不是公式本身,而是Excel文件来源不可控:用户手填、其他系统导出、甚至复制粘贴进来的“假日期”——它们看起来像日期,实际只是文本。这种情况下,isDateTime()会返回false,你得 fallback 到正则匹配/^\d{4}-\d{2}-\d{2}/或strtotime()兜底,但务必加严格校验(比如拒绝2025-02-30这种非法日期)。


















