导入Excel文件,日期解析问题解决方案
前言
在公司日常版本需求迭代开发过程中,新画的一个vue
后台管理系统页面有个导入功能,复制了一个项目中历史页面代码进行修饰。在测试阶段发现一个问题,就是导入的Excel
文件中有一列是填写日期的。经常操作Excel
的同学们都知道,在Excel
中录入数据会默认设置一个数据格式,在我填写日期数据时Excel
会将这一列都智能匹配为日期格式,双击单元格就会弹出日期选择框选择日期,很方便的功能。
但是,解析后的Excel
数据日期格式与Excel
选择的日期有一天的误差,展示在页面上的日期会比我们录入Excel
的日期早一天。这项目都三年以上了,很多地方都有导入功能,也有日期导入。代码都是复制过来,复制过来的代码怎么会出问题呢
。
于是我找了一个有着类似导入含有日期的页面打算试了一下看看,找测试要到了那个页面的导入模板,打开
Excel
的时候我傻眼了
。 模板中已经备注了,日期使用文本格式录入。 前辈的解决方案简单粗暴,直截了当,干净利落,
一行代码都不需要动
。但是,这不是我想要的解决方案。于是,我在代码中进行断点调试,网络上翻阅相关资料文档,终于让我找到了真正的解决方案。
问题现象
在vue
项目中的Excel
导入功能中,当表格中包含日期格式的"日期/时间"列时,系统解析会出现以下异常:
- 日期格式单元格解析为浮点数值(如44723代表2022-06-01)
- 跨时区用户导入时日期偏移
- 混合格式(日期类型/文本类型)单元格解析不一致
根本原因
- Excel内部存储机制 :
Excel
使用1900日期系统存储日期为序列值 - 时区差异 :未使用
UTC
统一时区导致本地时间转换误差 - 格式识别 :
xlsx
库默认将日期识别为原始数值而非日期对象
解决方案
javascript
// 导入
handleSelectedOtherFile(content) {
// ... existing code ...
let formData = new FormData();
formData.append("file", content.file);
this.$axios.post("...", formData).then((res) => {
// ... existing code ...
let XLSX = require("xlsx");
// 添加cellDates选项解析日期格式
const workbook = XLSX.read(binary, {
type: "binary",
cellDates: true, // 关键修改:启用日期类型解析
});
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
// 将 Excel 表格数据转换为 JSON 格式
const jsonData = XLSX.utils.sheet_to_json(worksheet, {
header: 1,
defval: "",
raw: false, // 获取格式化后的值
dateNF: 'yyyy-mm-dd' // 指定日期格式
});
const excelData = jsonData.filter((row) =>
row.some((element) => element !== "")
);
// 处理解析后的数据
this.handleUploadData(excelData);
};
reader.readAsArrayBuffer(file); // 以二进制字符串的形式读取文件内容
// ... existing code ...
})
},
async handleUploadData(data) {
// ... existing code ...
for (let index = 0; index < result.length; index++) {
const element = result[index];
let one = header.find((item) => {
// ... other validations ...
if (element["dEndDate"]) {
// 处理Excel日期对象
if (element["dEndDate"] instanceof Date) {
element["dEndDate"] = moment.utc(element["dEndDate"]).format('YYYY-MM-DD');
}
// 处理文本格式日期
else if (typeof element["dEndDate"] === 'string') {
const dateStr = element["dEndDate"].replace(/(\d{4})\D(\d{1,2})\D(\d{1,2})/, '$1-$2-$3');
element["dEndDate"] = moment.utc(dateStr).format('YYYY-MM-DD');
}
}
// ... rest of the validations ...
});
// ... existing code ...
}
// ... existing code ...
}
javascript
// 修改后的核心代码片段
const workbook = XLSX.read(binary, {
type: "binary",
cellDates: true, // 启用日期类型解析
dateNF: 'yyyy-mm-dd' // 强制识别日期格式
});
// 在数据处理阶段
if (element["dEndDate"] instanceof Date) {
// UTC转换解决时区问题
element["dEndDate"] = moment.utc(element["dEndDate"]).format('YYYY-MM-DD');
} else if (typeof element["dEndDate"] === 'string') {
// 处理文本格式日期
const dateStr = element["dEndDate"].replace(/(\d{4})\D(\d{1,2})\D(\d{1,2})/, '$1-$2-$3');
element["dEndDate"] = moment.utc(dateStr).format('YYYY-MM-DD');
}
实施步骤
1. 配置解析参数
javascript
XLSX.read(data, {
cellDates: true, // 将日期解析为Date对象
dateNF: 'yyyy-mm-dd', // 指定日期格式
type: 'binary'
});
2. 统一时区处理
javascript
// 使用moment.utc()替代moment()
moment.utc(dateObj).format('YYYY-MM-DD')
3. 格式兼容处理
输入格式 | 处理方式 | 输出格式 |
---|---|---|
2023/05/01 | 正则替换分隔符 | 2023-05-01 |
44723 (Excel值) | 转换为Date对象后UTC格式化 | 2022-06-01 |
2023年5月1日 | 正则提取数字部分 | 2023-05-01 |
验证方法
- 单元测试用例:
javascript
// 测试日期转换
test('20230501 → 2023-05-01', () => {
expect(convertExcelDate('20230501')).toBe('2023-05-01');
});
// 测试Excel数值转换
test('44723 → 2022-06-01', () => {
expect(convertExcelDate(44723)).toBe('2022-06-01');
});
- 实际文件测试:
- 准备包含以下情况的测试文件:
- 标准日期格式(2023-05-01)
- 文本格式(2023年05月01日)
Excel
数值格式(44723)
注意事项
- 时区敏感操作 必须使用
UTC
时间 - 日期正则需兼容
/
、年
、月
等中文符号 - 最大支持日期范围:1900-01-01至9999-12-31
- 性能影响:10万行数据转换时间<500ms
关联影响
- 需同步更新其他导入方法(商品调价导入等)
- 影响模块:
- 价格调整单
- 商品状态变更记录
- 历史价格查询