mysql数据库时间

记录MySQL今天又一个新的问题:

场景:nodejs后台+容器部署

问题原因:纯属好心办坏事,由于考虑了时区(现在看来纯属多余),在写入时间时使用了time_str.toLocaleString("chinese", { timeZone: timeZone })方法进行转换,并将该结果写入数据库。之所以出现问题,是因为在本地测试中完全没有问题,但当部署后,写入时间就报了错误。

当时使用的格式化方法是:

// 获取中国时区的时间戳
function getUTC8TimeStamp(time?: string) {
    let time_str = time ? new Date(time) : new Date() // 时间
    const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone // 获取时区
    let temp = time_str.toLocaleString("chinese", { timeZone: timeZone }) // 时区转换
    return temp.replace(/\//g,"-"); // /替换为-
}

报错如下:

Error: update `ac_table_id` set `expire_at` = '11/9/2023, 10:38:12 AM', `rotate` = 1 where `client_id` = 'xxx' - Incorrect datetime value: '11/9/2023, 10:38:12 AM' for column 'expire_at' at row 23
    at Packet.asError (/app/node_modules/mysql2/lib/packets/packet.js:728:17)
    at Query.execute (/app/node_modules/mysql2/lib/commands/command.js:29:26)
    at Connection.handlePacket (/app/node_modules/mysql2/lib/connection.js:478:34)
    at PacketParser.onPacket (/app/node_modules/mysql2/lib/connection.js:97:12)
    at PacketParser.executeStart (/app/node_modules/mysql2/lib/packet_parser.js:75:16)
    at Socket.<anonymous> (/app/node_modules/mysql2/lib/connection.js:104:25)
    at Socket.emit (node:events:512:28)
    at Socket.emit (node:domain:489:12)
    at addChunk (node:internal/streams/readable:324:12)
    at readableAddChunk (node:internal/streams/readable:297:9) {
  code: 'ER_TRUNCATED_WRONG_VALUE',
  errno: 1292,
  sqlState: '22007',
  sqlMessage: "Incorrect datetime value: '11/9/2023, 10:38:12 AM' for column 'expire_at' at row 23",
  sql: "update `ac_table_id` set `expire_at` = '11/9/2023, 10:38:12 AM', `rotate` = 1 where `test_id` = 'xxx'"

很显然,通过上述方法转换后的时间,在服务器上变成了11/9/2023, 10:38:12 AM格式,这里报错就是expire_at格式不正确导致的。

题外话:使用上述方式格式化的时间,在本地调试过程中打印出来全部始终都是2023-11-9 10:38:12,不清楚为什么部署到服务器以后就会变得不一样。

经过一番调试,最后结论是:要么直接返回time_str,或者使用moment格式,返回moment(time_str).format('YYYY-MM-DD HH:mm:ss'),都可以在数据库中成功写入。

比较

在解决问题的过程中,我们主要对数据库中展示的时间、从数据库获取到的时间和使用上述两种方式转换后的时间进行比较,最后得出的结论。

1. 数据库中显示的时间:

在这里插入图片描述

2.通过sql从数据库中查出来的:

在这里插入图片描述
可见,对于MySQL数据库,我们所看到的展示给我们的时间格式是YYYY-MM-DD HH:mm:ss,是数据库根据当地时区进行了转换了展示给我们看的时间,而真实存储的是UTC时间。

3.使用time_str.toLocaleString("chinese", { timeZone: timeZone })方法转换后的时间

其实问题点就出在这个地方,通过这个方法转换后的时间,在本地环境中是YYYY-MM-DD HH:mm:ss这种格式,而在服务端就变成了11/9/2023, 10:38:12 AM这种格式。

4.moment(time_str).format('YYYY-MM-DD HH:mm:ss')转换后

很显然,它是YYYY-MM-DD HH:mm:ss这种格式。

向数据库中写入数据

  1. 写入YYYY-MM-DD HH:mm:ss格式。成功
  2. 写入11/9/2023, 10:38:12 AM报错,错误如上
  3. 写入new Date()格式。成功
  4. 写入2023-11-13T08:28:43.000Z格式。报错,错误如上

综上,对于数据库timestamp格式的字段,来自前端通过各种方式格式化后的时间,服务端可以通过两种方式成功写入:

  1. new Date(time)
  2. 使用moment(time_str).format('YYYY-MM-DD HH:mm:ss')转换

new Date()比较

同样的方法new Date(),在服务端和浏览器的不同表现:

在这里插入图片描述
所以,如果遇到时间格式的问题,应该在浏览器和服务端各自分别测试。

结论:

  1. 向服务器提交时间,一律使用new Date()格式。
  2. 数据库会根据数据库当时所在的地区的时区,自动对时间做转换,只是展示出来的是经过格式化后的时间。所以,向数据库中写入时间,不需要进行格式转换,写入new Date()即可。
  3. 数据库存储时间,可以使用timestamp类型,并且写入时间不需要做转换。
  4. 由于数据库存储的时间是timestamp类型,所以,无论我们看到的是什么格式,通过sql从数据库中获取到的时间,都是2023-11-13T08:28:43.000Z这种格式,前端使用的时候需要进行时区转换,如使用moment()的方法等。
  5. 像需要通过Intl.DateTimeFormat().resolvedOptions().timeZone这种方式获取时区的场景,只适用于需要在两个时区之间进行转换的场景,如东八区到东七区。并且,通过time_str.toLocaleString("chinese", { timeZone: timeZone })这种方式获取到字符串后,写入数据库还需要使用new Date()方法进行转换成timestamp格式。
  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值