背景
项目中采用springboot+jpa+mysql来保存数据,在测试环境发现保存的数据时间都不准确,相差13个小时,故走上探坑之旅。
排查问题过程
日期不准确第一时间想到的肯定是时区问题,和相差8小时一个道理,不过13或者14个小时,一般小伙伴可能并不能第一时间想到这里,话不多说,直接查看Mysql时区:
show variables like "%time_zone%";
如上图,可以看到数据库使用的系统的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的简写,所以是容易引起混乱的,查看了其他环境的时区设置也是CST,但是日期并没有出现问题,说明这里应该是中国标准时间UT+8:00,那么排除数据库时区问题。
但是眼神好的小伙伴应该已经发现了,UT-6 UT+8 不正好14个小时么?那么13个小时怎么来的,原来美国从“3月11日”至“11月7日”实行夏令时,美国中部时间改为 UTC-05:00,与 UTC+08:00 刚好相差 13 小时,那么问题明确了这里肯定还是时区问题。
既然数据库本身的时区问题排除了,那么自然就想到是不是java里日期问题,查看代码java里使用的new Date();取的是服务器的时间,核对了服务器时间是正确的,并且打印出的日期毫秒值转成日期后也是正确的,那么也排除了java里设置的时间问题。
那么最后只能猜想是不是在java 连接数据库的时候发生问题了,这里自然是考虑jdbc连接的问题,于是百度一下url参数,刚好发现一篇文章讲到了mysql-connector-java的8.0后的版本会影响读取到的时区值,果断查看jar包版本:
发现确实是引用的8以后的版本,到这里基本确定问题的根源了。
解决方案
- 在数据库连接url后添加参数 &serverTimezone=Asia/Shanghai 即可。
- 修改数据库时区
show variables like "%time_zone%";
set global time_zone = '+8:00';
flush privileges; #立即生效
- 永久修改数据库时区
# vim /etc/my.cnf ##在[mysqld]区域中加上
default-time_zone = '+8:00'
# /etc/init.d/mysqld restart ##重启mysql使新时区生效
验证
- 使用方案一重新插入一条数据记录,发现日期值已经正常。
- 方法2、3未做验证,但猜想应该可以解决。
参考文章
- 【转贴】一次 JDBC 与 MySQL 因 “CST” 时区协商误解导致时间差了 14 或 13 小时的排错经历
-
Mysql8保存时间到数据库和本地时间相差14小时
注:jar版本为何会影响时区取值这里没有做详细探究,可以参考上面的文章。