技术周会上同事分享了关于mysql timestamp
2038年的限制导致的bug,决定再回顾一下以前遇到的关于时间的bug做了记录
先根据错误的原因分个类
- 系统存储本身限制。最经典当属
千年虫
- 时区问题。目前常见的话,使用容器时默认时区是美国的,直接使用会出现。
- 服务器时间未同步。这样的bug,不常见,但项目做多了,还是有几率碰见...
- 开发人员使用不当。 代码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中 timestamp
和datetime
除类型,存储位数不同外,时区也是有区别的。在领导的指点下,当数据库支持时区时,那么做国际化时,用对类型后是可以天然支持多时区的,而不是在代码上做太多逻辑。
服务器时间未同步
当以某个时间节点作为判断依据的时候就一定谨慎了。之前做的项目中有个按天限制的,但因服务器时间和MySQL时间不一致,当在0点前后的几分钟,通过服务器时间去查MySQL返回的数据是一直小于限制的,导致用户可以无限制刷数据!
具体在 https://blog.zero-one.top/2018-07/linux-db-no-sync-time-bug.html
时间对象使用不当
直接上示例代码吧
上面的代码,在比对时间的时候,直接修改了order.useDate
的值,直接导致后续使用此字段的地方展示或计算错误!
上面问题,我认为最好的解决是使用LocalDateTime
这类不可变对象,不仅不会产生这类bug,也保证线程安全。
除此外,在java8之前,我一般是直接转成时间戳来进行比对
Calendar,Date 的接口实在记不住,也用的少...所以还是时间戳操作起来最省事...