技术周会上同事分享了关于mysql timestamp 2038年的限制导致的bug,决定再回顾一下以前遇到的关于时间的bug做了记录


开发中遇到的时间类型bug_mysql

先根据错误的原因分个类

  1. 系统存储本身限制。最经典当属千年虫
  2. 时区问题。目前常见的话,使用容器时默认时区是美国的,直接使用会出现。
  3. 服务器时间未同步。这样的bug,不常见,但项目做多了,还是有几率碰见...
  4. 开发人员使用不当。 代码bug...

系统设计问题导致

2038举例来说,时间是按照1970年1月1日起至所存储日期过去了多少秒来计算的。这个数值是在系统里用32位来表示的,并且有正负。那么 1970年 + 2^31秒(约68年) 就是这个系统最终支持的时间,超过就变成负数。比特币中的数值无正负,所以能再延长一倍的时间。

按理说,这个时间还早,到时候问题会解决,当历史告诉我们,未必! 例如 2000年。 与之类似的还有 民国百年 ,他们系统年只有2位,当100的时候就无法显示....

以上问题解决也简单,换个存储方式即可,例如MySQL timestamp 更换成datetime ,或使用long存储。 不过要注意时区问题。

我们周会分享这个问题原因是,设置一个时间是2099年的。而``timestamp` 最大就2038年,问题来了!

时区问题

只在容器上碰见过

docker默认时区是美国的,所以有时间相关的项目在做DockerFile时一定要注意,

第一次碰见的时候,以为是数据库时区问题

排查没问题后,又以为是数据库链接的问题

排查再没问题后,以为是使用LocalDateTime的问题,到这也就发现。其实是容器的问题!

tip

在MySQL中 timestampdatetime 除类型,存储位数不同外,时区也是有区别的。在领导的指点下,当数据库支持时区时,那么做国际化时,用对类型后是可以天然支持多时区的,而不是在代码上做太多逻辑。

服务器时间未同步

当以某个时间节点作为判断依据的时候就一定谨慎了。之前做的项目中有个按天限制的,但因服务器时间和MySQL时间不一致,当在0点前后的几分钟,通过服务器时间去查MySQL返回的数据是一直小于限制的,导致用户可以无限制刷数据!

具体在 https://blog.zero-one.top/2018-07/linux-db-no-sync-time-bug.html

时间对象使用不当

直接上示例代码吧

开发中遇到的时间类型bug_服务器_02

上面的代码,在比对时间的时候,直接修改了order.useDate的值,直接导致后续使用此字段的地方展示或计算错误!

上面问题,我认为最好的解决是使用LocalDateTime这类不可变对象,不仅不会产生这类bug,也保证线程安全。

除此外,在java8之前,我一般是直接转成时间戳来进行比对

Calendar,Date 的接口实在记不住,也用的少...所以还是时间戳操作起来最省事...