记一次JVM时区引起的bug

前两天帮同事解决了一个比较有意思的bug,在这里记录一下。

起因

需求是一个简单的查询,将数据展示到页面,其中时间相关的项要根据用户时区转换。但是展示到页面后,时间那一列却出现了问题,差了一个小时。

但是在本地无论是单元测试还是启动tomcat运行,都无法复现这个问题。

排查

这种问题很常见,一般都是因为服务器跟本地的一些配置不同,或者是并发导致的。

经过初步排查,确定数据库、服务器、用户时区全部一致,都是+8时区,所以应该不是普通的时区转换的问题。

代码反复确认过没有问题,排查无果之下,只好在开发环境上加上一些日志,把这个时间从查出来到展示过程中,涉及到转换的部分都输出到日志中。

紧接着重新部署服务,诡异的是,之前差一小时的时间都变正常了。

重启果然能解决大部分问题。

问题就这么简单的解决了……才怪!

没过两分钟,同事跟我说时间又开始变成差一个小时了。

幸好加的日志还没删除,把日志翻出来,又发现一个诡异的现象,数据库的时间是13:00+8,日志显示查出来的时间是13:00+7,然后用+8的用户时区进行转换之后,变成了14:00+8。

数据库中存的时间是一个时间戳,众所周知,数据库的客户端会根据自己的时区把时间戳转换成一个时间,方便展示。也就是说不管你什么时区怎么转换怎么折腾,变的只是展示的形式,实际上这个时间戳是不会变的。

但是现在的现象是,虽然看上去都是13点,但是时区却变了,这就意味着时间戳发生了变化!

这时我又想起来服务在刚重启的时候,有一段时间内是正常的,我翻出那段时间的日志,发现日志上显示的是13:00+8。

所以基本上可以确定了是这个变化的时区搞的鬼了。

期间为了避免偶然性,我又重启了几次服务器,每次都是刚重启的时候一切正常,过一会就变成差一小时。

解决

“有人在篡改时区,而且是在服务启动几分钟之后。”

这是我得出的结论,仔细一想肯定不会有人这么无聊,那么肯定是程序干的,而且还是个定时任务。

这时突然灵感一闪,想起之前看过的一篇文章,说JVM在没有特别设置的情况下,会默认使用服务器环境变量的时区,如果想自定义时区,可以调用某一个JVM的TimeZone.setDefault()方法来设置时区。

想到这里我全局搜索了一下TimeZone.setDefault()方法,果不其然,有一个高频的定时任务使用了这个方法来更改时区。。

知道真相的我简直哭笑不得,瞬间让我想到一个段子说让返回一天后的时间,直接使用sleep解决的那个大哥。

总结

这明显是用的方法只是知其然而不知其所以然,作为一个程序员,这么做不可取。

这个定时任务加上去有不短的时间了,要不是现在这个同事自测的时候细心,这个bug不知道还要隐藏多久,做开发不求甚解有时候确实挺可怕的。。

话说回来,虽然表面上找到症结所在了,但是我还有一事不解,即便JVM时区变了,那也不应该去篡改查出来的结果。带着疑惑,我去请教了一下架构大佬。

我们公司用的是PostgreSQL,据大佬说,是PostgreSQL的驱动包的缺陷,在服务器时区跟jvm时区不一致的时候可能会出现这种问题。

你可能感兴趣的:(记一次JVM时区引起的bug)