Java JDBC处理MySQL时区错乱的核心是切断JVM与MySQL双重时区转换链,须在连接串显式设serverTimezone=Asia/Shanghai且useTimezone=true,禁用GMT%2B8或CST;MySQL服务端应配default-time-zone='+08:00';优先用DATETIME类型并统一语义;验证用UNIX_TIMESTAMP比对底层时间戳。

Java 中 JDBC 处理 MySQL 时区错乱,核心不是“修时间”,而是切断双重转换链——避免 JVM 本地时区和 MySQL 服务端时区各自按自己理解解释同一段时间值。
必须在连接串里显式声明 serverTimezone
JDBC 8.0+ 驱动默认不信任 MySQL 声称的时区(尤其是返回 SYSTEM 或 CST 时),会自行按 JVM 本地时区解析时间,再发给 MySQL;而 MySQL 若设为 SYSTEM,又按系统时区再转一次,结果偏移 8 小时甚至 13–14 小时。
- 正确写法:
?serverTimezone=Asia/Shanghai&useTimezone=true - 不要用
GMT%2B8或CST——前者部分驱动不识别,后者有歧义(美国中部/澳大利亚/中国都叫 CST) - Spring Boot 用户:该参数必须写在
spring.datasource.url里,spring.jackson.time-zone对数据库读写完全无效
服务端时区要真正对齐,不能只靠连接串
仅配 JDBC 参数,治标不治本。如果 MySQL 服务端实际时区是 UTC,但业务约定所有 DATETIME 都按北京时间存,那 Java 写入 2026-07-22 09:00:00,MySQL 会当成 UTC 存,查出来就变成北京时间 2026-07-22 17:00:00。
- 查清真实状态:
SELECT @@global.time_zone, @@session.time_zone, @@system_time_zone;和终端执行timedatectl | grep "Time zone" - 永久生效:在
/etc/my.cnf的[mysqld]段加default-time-zone = '+08:00',然后重启 MySQL - 不推荐
Asia/Shanghai除非已运行mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql并确认time_zone_name表中有该条目
DATETIME 和 TIMESTAMP 必须分清,选错类型等于埋雷
这是最容易被忽视、却影响最深的一环。两者对时区的敏感度完全不同:
立即学习“Java免费学习笔记(深入)”;
-
DATETIME:纯字面值,存啥查啥,
time_zone设置对它完全没作用 -
TIMESTAMP:内部存 UTC,读写时自动按当前会话
time_zone转换——同一个字段,在 Navicat、Java 应用、命令行里查出的字符串可能各不相同 - 线上强烈建议统一用 DATETIME,并在应用层明确约定语义(例如“全部按 Asia/Shanghai 存”或“全部按 UTC 存”)
- 若历史用了 TIMESTAMP 又未统一会话时区,修复成本极高,需批量调整所有客户端连接的
SET time_zone
验证闭环:用 UNIX_TIMESTAMP 看真实存储值
别只信 NOW() 或格式化后的字符串。真正可靠的方式是验证时间值的底层数字是否一致:
- Java 写入一个时间点后,立即执行:
SELECT UNIX_TIMESTAMP(created_at) FROM table WHERE id = ?; - 对比 Java 端
date.getTime() / 1000是否相等 —— 相等说明存储无偏差,问题出在显示层 - 如果不等,说明 JDBC 或 MySQL 层发生了隐式转换,需回头检查
serverTimezone和default-time-zone是否匹配


















