Java应用连MySQL时间差8小时最直接原因是serverTimezone参数未配置或URL编码错误,须在JDBC连接串中显式设serverTimezone=GMT%2B8且useTimezone=true,同时MySQL服务端配default-time-zone='+08:00'并重启,再确保TIMESTAMP/DATETIME类型语义统一及JVM、Jackson等环节时区对齐。

serverTimezone 参数没配对,或者配了但 URL 编码错误,是 Java 应用连 MySQL 后时间差 8 小时最直接的原因。
MySQL 本身不“错”,Java 也不“错”,错的是两者对同一个时间戳的解释方式不一致——一个按 UTC 解,一个按 CST(东八区)解,中间差了 8 小时。
Java JDBC 连接串必须显式声明 serverTimezone
MySQL 驱动(尤其是 8.0+ 版本)默认把服务端时区当成 UTC,哪怕你数据库已设成 +08:00,JDBC 仍可能无视它。
-
serverTimezone=GMT%2B8是最稳妥写法(注意%2B是+的 URL 编码) -
serverTimezone=Asia/Shanghai可能失败:部分 Docker 镜像缺 tzdata,或 MySQL 未加载时区表 - 必须搭配
useTimezone=true,否则serverTimezone不生效 - 完整示例:
jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useTimezone=true&serverTimezone=GMT%2B8
TIMESTAMP 和 DATETIME 字段行为完全不同
很多人以为改完连接参数就万事大吉,结果发现老数据还是不对——因为字段类型决定是否做时区转换:
-
TIMESTAMP存入时自动转成 UTC,读取时再按当前会话时区转回;它“认时区” -
DATETIME原样存、原样读,完全不碰时区;它只认字面值 - 如果历史数据用的是
DATETIME,且当初是按错误时区插入的(比如 Java 用默认 UTC 插入),那这些值本身就是错的,改配置也救不回来 - 查
TIMESTAMP字段时,NOW()和UTC_TIMESTAMP()返回值不同,别混用
JVM 时区和 Jackson 序列化也会叠加出错
即使 JDBC 连对了,时间在链路后半段还可能被二次扭曲:
-
new Date()依赖 JVM 默认时区,若 JVM 启动没指定-Duser.timezone=GMT+8,它可能用的是 UTC - Spring Boot 返回 JSON 时,Jackson 默认用 GMT 序列化
Date,前端拿到的就是早 8 小时的时间字符串 - 解决办法:
spring.jackson.time-zone=GMT+8或全局设置TimeZone.setDefault(TimeZone.getTimeZone("GMT+8")) - MyBatis 等 ORM 框架若用
java.util.Date映射DATETIME,同样受 JVM 时区影响
MySQL 服务端时区不能只靠 SET GLOBAL
SET GLOBAL time_zone = '+08:00' 看似生效,但重启即丢,而且它只影响新会话,不影响已存在的连接。
- 真正要改,得在
my.cnf的[mysqld]段加default-time-zone='+08:00' - 别用
Asia/Shanghai:Alpine 镜像、精简版容器常缺失时区数据,启动失败 - 改完必须
systemctl restart mysql,不是 reload - 验证命令:
SELECT @@global.time_zone, @@session.time_zone;,两个都该显示+08:00
真正的坑不在某一处,而在 JDBC、JVM、MySQL server、JSON 序列化这四层时区设置没对齐。只要有一层漏掉,8 小时偏差就会冒出来,而且很难一眼定位是哪一层的问题。


















