报错是这么冒出来的。pandas 2.2 在 Windows 上读一份供应商导出的 xlsx,日期列读出来是 45000 这样的整数,写进 PostgreSQL 后全变成 1900 年。用 Excel 打开显示的是 2023-03-15,同一份文件另存成 csv 再读又是对的,说明问题在读取这一层。
中间走过弯路,把「文件来自第三方导出,日期列没有真正的日期类型」当成主因,改完现象没变。
原因不止一个,得分开看。单元格本身是数字格式,只是显示层套了日期样式。也可能是openpyxl 按值读取,拿到的是序列号不是 datetime。
少数情况下是read_excel 指定了 dtype,把日期强行转成了整型。
先看这一列的原始类型和几个样本值
python -c "import pandas as pd;df=pd.read_excel('supplier.xlsx');print(df['日期'].dtype, df['日期'].head())"
然后按 1899-12-30 起点把序列号转成日期
python -c "import pandas as pd;df=pd.read_excel('supplier.xlsx');df['日期']=pd.to_datetime(df['日期'],unit='D',origin='1899-12-30');print(df['日期'].head())"
入库前统一成 ISO 字符串,避免驱动再猜一次类型
python -c "import pandas as pd;df=pd.read_excel('supplier.xlsx');print(df.assign(d=pd.to_datetime(df['日期'],unit='D',origin='1899-12-30').dt.strftime('%Y-%m-%d')).head())"
等前面都确认过,再把转换写进读取函数,后续文件复用同一个入口
python load_excel.py --file supplier.xlsx --date-cols 日期 --serial-origin 1899-12-30
恢复的判定其实很直接。转换后的列 dtype 是 datetime64,入库的值与 Excel 里显示的日期逐行一致,1900 年附近的脏值不再出现。
这里还有个坑。1900 年 2 月 29 日那个历史 bug 会让序列号在 60 附近偏移一天,跨百年的老数据要单独校验。改动前把原配置备份一份,出问题能马上回滚,这比任何技巧都实用。
