最近在写一个功能,使用SheetJS读取Excel表格,在读取日期的时候发现了一个隐藏很深的坑,特此记录一下。
SheetJS读取Excel文件时,可指定参数 cellDates: true
,这样当单元格内存储的为日期时,可以将读取到的值直接转化为Date
对象。
const workbook = XLSX.read(buffer, { type: 'binary',cellDates: true });
但测试时发现,读取到的值和实际时间总有43秒的误差:
经过搜索,发现这是一个历史原因导致的BUG:
在当地时间1901-01-01 00:00:00,上海时区由LMT(Local Mean Time)切换为CST (China Standard Time),与GMT的时差由+8:05:43调整为+8:00:00。
https://zhuanlan.zhihu.com/p/89914219
在GitHub上也有相关的讨论
GitHub: The exported date is 43 seconds longer
SheetJS用 getTimezoneOffset
来解决这个问题,但 getTimezoneOffset
获取到的时差只精确到分钟,所以就产生了43秒的误差
这个Issue里给出了解决方法
方法1
手动修复误差
function getTimezoneOffsetMS(date) {
var time = date.getTime();
var utcTime = Date.UTC(date.getFullYear(),
date.getMonth(),
date.getDate(),
date.getHours(),
date.getMinutes(),
date.getSeconds(),
date.getMilliseconds());
return time - utcTime;
}
const importBugHotfixDiff = (function () {
const basedate = new Date(1899, 11, 30, 0, 0, 0);
const dnthreshAsIs = (new Date().getTimezoneOffset() - basedate.getTimezoneOffset()) * 60000;
const dnthreshToBe = getTimezoneOffsetMS(new Date()) - getTimezoneOffsetMS(basedate);
return dnthreshAsIs - dnthreshToBe;
}());
function fixPrecisionLoss(date) {
return (new Date(date.getTime() - importBugHotfixDiff));
}
方法2
用 SSF
模块对 Date
对象进行转换
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 new Date(parsed.y, parsed.m, parsed.d, parsed.H, parsed.M, parsed.S);
}
其他方法
不使用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 new Date(parsed.y, parsed.m, parsed.d, parsed.H, parsed.M, parsed.S);
}
后记
真是醉了,写个代码还能学到历史……
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)