有没有一刻,突然平静地想离职(VUE中使用XLSX插件导入Excel文件,日期解析存在误差)

导入Excel文件,日期解析问题解决方案

前言

在公司日常版本需求迭代开发过程中,新画的一个vue后台管理系统页面有个导入功能,复制了一个项目中历史页面代码进行修饰。在测试阶段发现一个问题,就是导入的Excel文件中有一列是填写日期的。经常操作Excel的同学们都知道,在Excel中录入数据会默认设置一个数据格式,在我填写日期数据时Excel会将这一列都智能匹配为日期格式,双击单元格就会弹出日期选择框选择日期,很方便的功能。

但是,解析后的Excel数据日期格式与Excel选择的日期有一天的误差,展示在页面上的日期会比我们录入Excel的日期早一天。这项目都三年以上了,很多地方都有导入功能,也有日期导入。代码都是复制过来,复制过来的代码怎么会出问题呢

于是我找了一个有着类似导入含有日期的页面打算试了一下看看,找测试要到了那个页面的导入模板,打开Excel的时候我傻眼了 模板中已经备注了,日期使用文本格式录入。 前辈的解决方案简单粗暴,直截了当,干净利落,一行代码都不需要动。但是,这不是我想要的解决方案。于是,我在代码中进行断点调试,网络上翻阅相关资料文档,终于让我找到了真正的解决方案。

问题现象

vue项目中的Excel导入功能中,当表格中包含日期格式的"日期/时间"列时,系统解析会出现以下异常:

  1. 日期格式单元格解析为浮点数值(如44723代表2022-06-01)
  2. 跨时区用户导入时日期偏移
  3. 混合格式(日期类型/文本类型)单元格解析不一致

根本原因

  1. Excel内部存储机制Excel使用1900日期系统存储日期为序列值
  2. 时区差异 :未使用UTC统一时区导致本地时间转换误差
  3. 格式识别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

验证方法

  1. 单元测试用例:
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');
});
  1. 实际文件测试:
  • 准备包含以下情况的测试文件:
    • 标准日期格式(2023-05-01)
    • 文本格式(2023年05月01日)
    • Excel数值格式(44723)

注意事项

  1. 时区敏感操作 必须使用UTC时间
  2. 日期正则需兼容/等中文符号
  3. 最大支持日期范围:1900-01-01至9999-12-31
  4. 性能影响:10万行数据转换时间<500ms

关联影响

  1. 需同步更新其他导入方法(商品调价导入等)
  2. 影响模块:
    • 价格调整单
    • 商品状态变更记录
    • 历史价格查询

扩展阅读

MS Excel日期系统说明 | Moment.js UTC文档

相关推荐
知否技术2 小时前
面试官最爱问的Vue3响应式原理:我给你讲明白了!
前端·vue.js
小周同学:2 小时前
vue将页面导出成word
前端·vue.js·word
wordbaby3 小时前
Vue3滚动轮播组件实现超标信息自动滚动
前端·vue.js
_xaboy3 小时前
基于Vue的低代码可视化表单设计器 FcDesigner 3.2.11更新说明
前端·vue.js·低代码·开源·表单设计器
云浮万里_14 小时前
保姆级教程:Vue3 + Django + MySQL 前后端联调(PyCharm+VSCode版)
vue.js·vscode·mysql·pycharm·django
程序员小刚4 小时前
基于SpringBoot + Vue 的考勤管理系统
vue.js·spring boot·后端
code bean6 小时前
【C#】`Task.Factory.StartNew` 和 `Task.Run` 区别
前端·vue.js·c#
倔强青铜三6 小时前
WXT浏览器插件开发中文教程(9)----WXT配置详解之Vite配置
前端·javascript·vue.js
倔强青铜三6 小时前
WXT浏览器插件开发中文教程(10)----WXT配置详解之构建模式
前端·javascript·vue.js
倔强青铜三6 小时前
WXT浏览器插件开发中文教程(8)----WXT配置详解之运行时配置
前端·javascript·vue.js