【BUG】由于url设置东八区,而数据库实际为UTC导致的时间显示错误问题
今天发现了一个诡异的BUG。
从Linux服务器的数据库上将数据导入到本地后,发现显示的时间格式不一样。
我瞬间就明白过来,肯定是时区问题,但是为什么会这样呢?
查看一下数据库时区:
show variables like'%time_zone';
可以看到,服务器的数据库时区为UTC,而本地数据库安装在windows上,是东八区的时间。
可是,时区是怎么影响时间格式显示的呢?
因为我记得,MySQL时区的不同,只会影响日期相关函数,比如 now(),sysdate(),
unix_timestamp(‘2021-12-29 15:50:20’)的返回结果,对数据表里的日期数据插入、查询是没有影响的。
快递时间的信息是快递100通过接口传给我的,它是在json中传过来的。
而json中是没有时间这种数据格式的,时间在json是以字符串的形式保存的
。
以 2029-12-30 13:20:25
举例,首先以东八区为例
/**
* 日期型字符串转化为日期 格式
*/
public static Date parseDate(Object str){
if (str == null){
return null;
}
try{
return parseDate(str.toString(), parsePatterns);
}catch (ParseException e){
return null;
}
}
public static void main(String[] args) {
System.out.println(DateUtils.parseDate("2029-12-30 13:20:25"));
System.out.println(TimeZone.getDefault().getID());
}
parseDate(str.toString(), parsePatterns)
调用的是apache下lang3包里的封装方法。
我把电脑时区分别设置为UTC和东八区,然后运行main方法。
发现结果让我很惊讶
转换后的时间格式,几乎毫无区别,唯一的区别是 一个带有UTC, 一个带有CST
经过三个小时的百度,以及掉了73根头发,我忽然虎躯一震,明白了其中的关键。
原来最大的区别,就是一个带有UTC, 一个带有CST。
为什么呢?
且听我缓缓道来。
因为我的数据库连接的url中是这样写的。
可以看到,我在数据库的url中明确指定了时区为东八区。
不幸的时,linux服务器上的时区是UTC,MySQL使用的是系统时区,也就是UTC。
因此在快递100传入Json数据的时候,里面的时间格式字符串会把转化成UTC的时间格式,而我指定的是东八区格式。
但是在parseDate(str.toString(), parsePatterns)
,jvm会调用系统的时区进行转化,也就是说,会转化成Sun Dec 30 13:20:25 CST 2029
.
mybatis在封装数据的时候,它一看自己设置的是东八区,而你给我传的UTC时间。
得,我知道怎么做,东八区和UTC相差八个小时,我给你加上八个小时,妥妥得。
于是,存到数据库中得时间就变成了2029-12-30 21:20:25
.
所以,最后会出现传入的时间,和数据库实际显示的时间不一样。
BUG修复
- 把linux服务器和数据库都改为东八区(服务器运行很久,不确定这样改,有没有影响,舍弃)
- 将url中的时区设置为0时区(方法可行,成本低)
- 如果仅针对快递100来说,应该定义一个方法,将转化的时间减去8个小时,这样在mybatis转化后,会加上8个小时,等于毫无变动(方法可行,成本低)
经过讨论,决定采用第2种方法。
不过,随之而来,另外一个问题,数据库种每条记录都有个create_time,它在插入的时候默认使用sysdate(),这就导致了它总会比实际时间晚上8个小时。
即便采用第三种方法,也会出现这个问题。
后续解决方法:
以后所有的create_time字段都不获取数据库的sysdate(),而是使用new Date()即可。
这样即便服务器是UTC时区,而连接是东八区时间,最后数据库中依然显示的是东八区时间。