最近在写一个功能,使用SheetJS读取Excel表格,在读取日期的时候发现了一个隐藏很深的坑,特此记录一下。
SheetJS读取Excel文件时,可指定参数 cellDates: true
,这样当单元格内存储的为日期时,可以将读取到的值直接转化为Date
对象。
const workbook = XLSX.read(buffer, { type: 'binary',cellDates: true });
但测试时发现,读取到的值和实际时间总有43秒的误差:
经过搜索,发现这是一个历史原因导致的BUG:
在GitHub上也有相关的讨论
GitHub: The exported date is 43 seconds longer
这个Issue里给出了解决方法:
方法1
手动修复 getTimezoneOffset
的精度
import XLSX from 'xlsx';
const basedate = new Date(1899, 11, 30, 0, 0, 0);
const dnthresh = basedate.getTime() + (new Date().getTimezoneOffset() - basedate.getTimezoneOffset()) * 60000;
const day_ms = 24 * 60 * 60 * 1000;
const days_1462_ms = 1462 * day_ms;
function datenum(v: Date, date1904: boolean) {
let epoch = v.getTime();
if (date1904) {
epoch -= days_1462_ms;
}
return (epoch - dnthresh) / day_ms;
}
export function fixImportedDate(date: Date, isDate1904: boolean) {
const parsed = XLSX.SSF.parse_date_code(datenum(date, false), { date1904: isDate1904 });
// return `${parsed.y}-${parsed.m}-${parsed.d}`;
return new Date(parsed.y, parsed.m, parsed.d, parsed.H, parsed.M, parsed.S);
}
方法2
不使用cellDates: true
,直接用SSF
模块转换日期格式单元格里的值
import XLSX from 'xlsx';
export function parseExcelDate(n: number, isDate1904: boolean) {
const parsed = XLSX.SSF.parse_date_code(n, { date1904: isDate1904 });
// return `${parsed.y}-${parsed.m}-${parsed.d}`;
return new Date(parsed.y, parsed.m, parsed.d, parsed.H, parsed.M, parsed.S);
}
后记
真是醉了,写个代码还能学到历史……