问题描述

项目中采用springboot+jpa+mysql来保存数据,在测试环境发现java new Date()保存的数据时间与本地时间相差8小时。

问题分析

首先查看mysql数据库的时间和时区:

查看当前时间:
select now();
返回结果相差8小时

查看mysql时区:
show variables like '%time_zone%';
返回的system_time_zone为UTC

发现时区不对,修改时区:(Mysql通过docker启动)

问题解决

同步服务器的时区(CST)

docker cp /etc/localtime 【MYSQL容器ID或者NAME】:/etc/localtime

然后再重启mysql:
docker restart 【MYSQL容器ID或者NAME】

问题描述

时区修改后,查看修改成功,但是再次插入时,发现差13个小时了

问题分析

上边修改时区为CST,但是CST表示的时区有多个,如下:

美国中部时间:Central Standard Time (USA) UT-6:00
澳大利亚中部时间:Central Standard Time (Australia) UT+9:30
中国标准时间:China Standard Time UT+8:00
古巴标准时间:Cuba Standard Time UT-4:00

上面这个4个地方的标准时间都可以是CST的简写,所以是容易引起混乱的,再次使用select now()查看mysql当前时间已经是正常的,为什么插入的时间却差了13个小时呢?

UT-6 UT+8 不正好14个小时么?那么13个小时怎么来的?原来美国从“3月11日”至“11月7日”实行夏令时,美国中部时间改为 UTC-05:00,与 UTC+08:00 刚好相差 13 小时,那么问题明确了这里肯定还是时区问题。

既然数据库本身的时区问题排除了,那么自然就想到是不是java里日期问题,查看代码java里使用的new Date();取的是服务器的时间,核对了服务器时间是正确的,并且打印出的日期毫秒值转成日期后也是正确的,那么也排除了java里设置的时间问题。

此时mysql端和java端都排除问题,那么唯一可能有问题的就是java与mysql的连接SDK可能有问题(mysql-connector-java),经过查看其它人的博客发现从6版本开始,驱动多了一个cj包。如果未在MYSQL连接url里手动指定时区,那么会去读取数据库设置的时区,而在上边我们已经将时区设置为CST,当他被读取到mysql-connector-java时会被当成美国中部时间的CST,导致new Date()出的中国标准时间会被转换为美国中部时间,最终插入数据库的时间与中国的标准时间差14小时,又因当前为10月美国中部时间改为 UTC-05:00所以实际相差13小时。

问题解决

在mysql的连接url后边加上&serverTimezone=Asia/Shanghai,让其获取到准确的中国时区并进行时间转换

上边还提到mysql-connector-java版本,在6之前是不会去读取数据库的时区,所以如果是5版本,直接设置好项目服务器的时区即可。

另外Spring boot2版本开始默认使用的mysql-connector-java为8版本,此时就需要注意了。

另外这个时间字段的MYSQL类型设置不当也会有时间不一致的问题,详见:

https://dev.mysql.com/doc/refman/5.7/en/datetime.html