mysql时区未对齐导致时间错乱,需查global/session时区及utc偏移,配置default-time-zone='+08:00'并重启,统一用datetime避免自动转换。

迁移后时间对不上,八成是 time_zone 没对齐——不是服务器时间不准,而是 MySQL 自己的时区配置被重置了。新实例默认常为 SYSTEM,而旧库可能设的是 +08:00;一旦容器或云环境里系统时区本身不可靠,SYSTEM 就会静默失效。
确认当前生效的时区配置
别猜,直接查数据库实际在用什么:
- 执行
SELECT @@global.time_zone, @@session.time_zone;—— 返回SYSTEM不代表安全,得继续看底层 - 执行
SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP);看偏移量,+08:00才是东八区预期值 - 查系统时区是否靠谱:
timedatectl | grep "Time zone"(Linux);Docker 里常为空或指向/etc/localtime软链失效 - 如果返回
Asia/Shanghai,还要确认时区表是否加载:SELECT COUNT(*) FROM mysql.time_zone_name WHERE Name LIKE '%Shanghai%';,结果为 0 表示没加载,mysql_tzinfo_to_sql没跑过
永久统一服务端时区:改配置 + 重启 + 验证
SET GLOBAL time_zone = '+08:00' 只是临时方案,重启就丢。真正起效必须落盘:
- 编辑
/etc/my.cnf或/etc/mysql/mysql.conf.d/mysqld.cnf,在[mysqld]下加一行:default-time-zone = '+08:00' - 避免用
Asia/Shanghai:它依赖系统tzdata和 MySQL 时区表,Docker 官方镜像默认不带tzdata,容易 fallback 到SYSTEM;+08:00是字面偏移,不依赖外部文件,更稳 - 改完必须重启:
sudo systemctl restart mysql(或对应服务名),否则配置不加载 - 重启后立刻验证:
SELECT @@global.time_zone;必须返回+08:00,不是SYSTEM
检查客户端连接层是否覆盖了服务端设置
即使服务端设对了,JDBC、PyMySQL、PDO 这些驱动仍可能按自己逻辑二次转换,导致写入/读取错乱:
- JDBC 连接串必须显式声明:
?serverTimezone=Asia/Shanghai&useTimezone=true;GMT%2B8在部分驱动里不识别,优先用 IANA 名 - Python PyMySQL 初始化时传参:
timezone='+08:00';别依赖系统本地时区 - PHP PDO 可通过
PDO::MYSQL_ATTR_INIT_COMMAND发送SET time_zone = '+08:00' - 连接后立即查:
SELECT @@session.time_zone;,确保和你预期一致;老连接不会自动更新,只影响新连接
DATETIME 和 TIMESTAMP 的行为差异必须分清
这是“部分时间对得上、部分不对”的根源——它们对 time_zone 的响应完全不同:
-
TIMESTAMP存的是 UTC 时间戳,读写时自动按当前session的time_zone转换;改会话时区,同一条记录查出来字面值就变 -
DATETIME是纯字面值,存啥是啥,time_zone设置对它完全没影响 - 迁移时若混合使用:比如
created_at DATETIME和updated_at TIMESTAMP,同一行数据在不同 session 时区下查出来时间“不一致”,其实是正常行为,不是 bug - 线上系统建议统一用
DATETIME,业务层自己控制时区含义(如全部存北京时间,或全部存 UTC);除非真需要自动转换,否则别碰TIMESTAMP
最易被忽略的是:容器里 default-time-zone = '+08:00' 写对了,但宿主机 /etc/localtime 是空的,或者 Docker 启动没挂载时区文件,MySQL 启动时会静默 fallback 到 SYSTEM,而你根本收不到报错。务必进容器执行 date 和连 MySQL 执行 SELECT NOW() 对比小时数是否一致。











