服务器时区与数据库时区不一致导致时间bug记录

1、背景

一个活动,需要按照自然月刷新,每月一期,以活动开始当月作为第一期,每期可配置不同数据。问题出现在:活动开始时间为本月,但是查询用户数据发现当前为第二期,反复查看代码,确定计算期数逻辑无问题,十分诡异。期数计算代码如下:

java 复制代码
protected int getPeriod(Date begin, Date now) {
        // DateUtil为工具类,format方法将时间转化为 yyyy-MM-dd格式
        String beginTime = DateUtil.format(begin, DateUtil.FORMATTER_YYYYMMDD_STR);
        String nowTime = DateUtil.format(now, DateUtil.FORMATTER_YYYYMMDD_STR);
        int beginYear = Integer.valueOf(beginTime.substring(0, 4));
        int nowYear = Integer.valueOf(nowTime.substring(0, 4));
        int beginMonth = (beginTime.charAt(5) - '0') * 10 + (beginTime.charAt(6) - '0');
        int nowMonth = (nowTime.charAt(5) - '0') * 10 + (nowTime.charAt(6) - '0');
        // 计算期数(可能出现跨年,需要考虑年份)
        return nowMonth + 12 * (nowYear - beginYear) - beginMonth + 1;
    }

2、排查

服务为分布式架构,有多个节点,发现只有少数节点会产生异常数据(本次活动只配置了一期,计算结果为第二期时会因拿不到配置数据而空指针,根据报错日志判断)。查看配置数据,发现活动开始时间为本月1日00点00分00秒,因此数个小时的时差即会导致月份出现偏差,猜测服务器时区问题,date -R检查服务器时区,正常节点与异常节点时区一致,暂时不考虑时区问题。

配置数据会加载进内存,当修改配置数据时,会修改有改动表的版本号,定时任务根据表版本号去刷新配置。考虑是配置人员中途改过数据,定时任务出现问题导致配置没有更新。观察日志,发现刷新配置的时间异常,时区与机器时区不一致。

date -R 结果(+0900 东九区):

Wed, 12 Jun 2024 22:59:25 +0900

服务器日志时间:

java 复制代码
2024-06-12T01:17:22.506+0000

3、结论

出现错误的原因为:进程时区与数据库时区没有保持一致,导致进程内时间与实际要配置的时间出现偏差,最终导致计算出错。

4、总结

(1)临界点时间(跨天、跨月、跨年)极易受时区影响导致极大误差,出现时间问题时可第一时间查看时区问题

(2)机器时区与进程时区并不总是一致,需要摆脱这个惯性思维,用其他方式(如日志)确定进程时区。

相关推荐
飞翔的佩奇9 分钟前
Java项目:基于SSM框架实现的游戏攻略网站系统分前后台【ssm+B/S架构+源码+数据库+毕业论文+任务书】
java·数据库·spring·游戏·架构·maven·ssm框架
Tcoder-l3est20 分钟前
【论文阅读】XuanYuan: An AI-Native Database
数据库·论文阅读·ai-native
长亭外的少年26 分钟前
ClickHouse 介绍:深度解析高性能列式数据库的核心优势
java·数据库·clickhouse
醇氧43 分钟前
【postgresql】模式(SCHEMA)
数据库·sql·postgresql
zengson_g1 小时前
如何在 PostgreSQL 中实现数据的去重操作,尤其是对于复杂的数据结构?
数据库·postgresql
zengson_g1 小时前
如何优化 PostgreSQL 中对于树形结构数据的查询?
数据库·postgresql
鞥牧魂人1 小时前
数据库——事务管理
服务器·数据库·oracle
冯宝宝^1 小时前
图书管理系统
服务器·数据库·vue.js·spring boot·后端
失眠的稻草人2592 小时前
【高阶数据结构】B-数、B+树、B*树的原理
数据结构·数据库·b树
令人着迷2 小时前
Redis核心问题总结(一)
数据库·redis·缓存