必须显式设为+08:00,因asia/shanghai依赖未默认加载的时区表且受系统更新影响,导致timestamp解析不可控;而+08:00无歧义、不依赖系统,确保东八区严格一致。

MySQL 的 time_zone 必须显式设为 +08:00(而非 Asia/Shanghai),否则在夏令时切换、系统时区变更或跨服务器迁移时,TIMESTAMP 字段会悄无声息地错乱。
为什么不能用 Asia/Shanghai?
MySQL 对时区名的支持依赖于系统内置的时区表(由 mysql_tzinfo_to_sql 导入),而 Asia/Shanghai 在多数 Linux 发行版中默认未加载;即使手动导入,它仍会跟随系统时区数据库更新——一旦系统升级或容器镜像重建,Asia/Shanghai 可能指向过时规则,导致历史 TIMESTAMP 解析偏差。更关键的是:TIMESTAMP 类型在存储/查询时会按当前 time_zone 值做自动换算,若该值是带名称的时区,MySQL 会查表转换,中间多一层不可控映射。
-
SET time_zone = 'Asia/Shanghai';→ 实际可能映射到+08:00或+09:00(极少数旧数据残留) -
SET time_zone = '+08:00';→ 确保所有TIMESTAMP严格按东八区偏移处理,无歧义 - 验证方式:
SELECT @@time_zone, @@system_time_zone;—— 前者应为+08:00,后者可为系统本地值(不影响业务)
全局设置与会话设置必须同时生效
只改全局变量(SET GLOBAL time_zone = '+08:00';)对已连接客户端无效;只改会话变量(SET time_zone = '+08:00';)在连接断开后丢失。生产环境必须双管齐下:
- 启动时固化:在
my.cnf的[mysqld]段添加default-time-zone = '+08:00'(注意不是time_zone) - 连接后兜底:应用层在每次建连后执行
SET time_zone = '+08:00';(如 JDBC 的connectionInitSql配置) - 避免使用
init_connect设置会话时区:它不作用于 SUPER 权限用户(如备份脚本、DBA 工具),造成部分操作仍走系统默认时区
DATETIME 和 TIMESTAMP 的行为差异必须厘清
DATETIME 是纯字面值,存啥查啥,完全不受 time_zone 影响;TIMESTAMP 才会随 time_zone 自动转换。业务中误用类型是时区问题的主因:
- 日志时间、订单创建时间等需跨时区统一比对的字段,必须用
TIMESTAMP+ 固定+08:00 - 身份证有效期、合同截止日等“绝对日期”应强制用
DATETIME,并在应用层确保输入已是东八区时间 - 执行
SELECT NOW(), SYSDATE(), UTC_TIMESTAMP();对比:前两者受time_zone影响,最后一个始终返回 UTC 时间,可用于调试偏差
真正难的是历史数据迁移——如果旧库用的是 SYSTEM 或 Asia/Shanghai,且已存入大量 TIMESTAMP,直接改 time_zone 会导致所有历史值被重新解释。这种场景必须先导出原始二进制值(用 SELECT HEX(ts_col) FROM t;),再按原时区规则反推真实时间,无法靠配置一劳永逸。











