乐于分享
好东西不私藏

Excel、JavaScript、MySQL 的时区战争

Excel、JavaScript、MySQL 的时区战争
引:

最近在调试项目时,我遇到了一个非常经典、但也非常恶心的问题:

Excel 里的日期是:2026-05-01导入系统后却变成了:2026-04-30

而且:

  • Excel 看起来是对的
  • 前端显示有问题
  • 数据库存进去又变了
  • 接口返回还不一样

最离谱的是:

你以为是 Excel 的问题,后来发现是 JS;你以为是 JS 的问题,后来发现 MySQL 也参与了;最后发现——整个链路都有坑。

今天这篇文章,我把这个问题彻底讲明白。

一、
这个Bug到底有多离谱?

举个例子。

Excel 中:

姓名
日期
张三
2026-05-01

前端读取 Excel 后:

console.log(item.date)

输出:

Thu Apr 30 2026 16:00:00 GMT+0000

然后页面显示:

2026-04-30

日期直接少一天。

二、
跟踪Excel的问题?

后来发现:

Excel 根本没有时区。

Excel 内部存储日期,其实是一个数字。

例如:

2026-05-01=45778

它只是:

“从某一天开始累计的天数”

本质上:

Excel 的日期 ≠ JavaScript Date

真正的问题,出现在:

XLSX → JavaScript Date

这一步。

三、
跟踪JavaScript Date的问题?

很多项目都会这样读取 Excel:

const workbook = XLSX.read(fileData, {  cellDatestrue})

或者:

const rows = XLSX.utils.sheet_to_json(ws, {  rawfalse})

然后:

console.log(rows)

得到:

{date: Date(...)}

注意:

这里已经不是“日期”了,而是“时间对象”。

而 JS 的 Date,天生带时区。

四、
老刘犯的错误

老刘这样写的:

new Date("2026-05-01")

看起来没问题。

实际上:

new Date("2026-05-01")

等价于:

new Date("2026-05-01T00:00:00.000Z")

注意最后那个:

Z = UTC 时区

而中国时间是:

UTC+8

所以:

2026-05-01 00:00:00 UTC=2026-05-01 08:00:00 中国时间

接下来如果再经过:

toISOString ()

或者:

JSON.stringify()

就会再次转 UTC。

于是:

2026-05-01UTC时区转换2026-04-30

Bug 出现了。

五、
真正的问题:我把“日期”当成了“时间”

这是整个问题的核心。

很多业务里的字段:

  • 出生日期
  • 开票日期
  • 合同日期
  • 账单日期
  • Excel 导入日期

它们其实都只是:

日期

不是:

时间点

老刘用了这个:

new Date()

于是:

一个“日期”被强行变成了“带时区的时间”

后面所有系统都会开始转换。

六、
追查MySQL

很多数据库字段会这样设计:

create_time timestamp

问题来了。

timestamp 会自动转换时区

例如:

存储时:转 UTC读取时:再转本地时间

于是:

2026-05-01 00:00:00

可能会变成:

2026-04-30 16:00:00 UTC

如果前端再转一次:

日期就少一天。
七、
最正确的解决方案

后来老刘终于意识到:

“纯日期”不要使用 Date

这是核心原则。

正确方案

整个链路:

全部使用 yyyy-MM-dd 字符串

例如:

2026-05-01

不要:

Date

不要:

ISOString

不要:

timestamp
八、
前端、后端、数据库写法

前端Excel读取

不要:

cellDates : true

正确:

const workbook = XLSX.read(data, {  cellDatesfalse})

然后:

const rows = XLSX.utils.sheet_to_json(ws, {  rawfalse})

这样得到的:

{    date:"2026-05-01"}

是字符串。

不是 Date。

后端正确写法

不要:

DateTimestampLocalDateTime

正确:

LocalDate

例如:

@JsonFormat(pattern ="yyyy-MM-dd")private LocalDate billDate;

数据库正确写法

不要:

timestamp

正确:

date

因为你存储的是:

日期

不是:

时间
九、
一句话总结(建议收藏)

日期 ≠ 时间

如果业务只关心:

2026-05-01

那么:

永远使用字符串:yyyy-MM-dd

不要:

  • new Date()
  • toISOString()
  • timestamp
  • UTC 转换

这样可以避免 90% 的日期问题。

结、

以前我一直觉得:

“日期少一天”只是个小 Bug。

后来才发现:

它背后其实是:ExcelJavaScriptJSONMySQL、时区系统共同作用的结果。

而且这个坑:

几乎所有前端和后端都会踩。

如果你也遇到过:

  • 日期少一天
  • 时区错乱
  • Excel 导入异常
  • new Date() 神秘问题

欢迎留言交流。

延续阅读

1.   玩转开发板 | 沁恒CH573蓝牙开发板-TMOS

2.   玩转开发板 | 欣瑞达串口屏幕从0到1

3.   玩转开发板 | nRF52832开发板之环境搭建及烧录等

4.   环境搭建 | Eclipse与ESP-IDF完美结合让ESP32飞起来

5.   环境搭建 | MySQL安装及搭建(9.6安装)

6.   深夜反思:那些被曲解的传统智慧,正悄悄毁掉我们的下一代

7.   STM32G491 硬核驱动 ILI9488:点亮你的彩色世界,性能与效率并存!

8.   三步搞定服务器文件“安全搬运”——WinSCP标准操作指南(附避坑提醒)

9.   揭秘锂电池充电“小能手”:PJ4054B,让你的设备电力十足!

10. 小技巧 | 微信提示“文件已过期或被清理”怎么办?

 欢迎关注我的公众号,可以点击在看、给个小红心点个赞。