MySQL时区配置需确认实际加载的my.cnf路径,default-time-zone必须置于[mysqld]段并用'+08:00'格式,改后须重启服务;Docker需同步宿主机时区或设TZ;TIMESTAMP自动时区转换而DATETIME不转换;JDBC需指定serverTimezone;验证须用NOW()、UTC_TIMESTAMP()及TIMEDIFF三者闭环。

确认MySQL实际加载的配置文件路径
MySQL启动时只读一次配置,改错文件等于白改。运行 mysqld --verbose --help | grep "Default options" 查看它真正扫描的路径顺序,常见位置有:/etc/my.cnf、/etc/mysql/my.cnf、/usr/etc/my.cnf、~/.my.cnf。别只盯着你“以为”对的那个路径。
在[mysqld]段下写对default-time-zone参数
必须写在 [mysqld] 段内,不是 [client] 或全局段;必须用 default-time-zone = '+08:00',不能写成 timezone、time_zone 或 default_timezone。命名时区如 'Asia/Shanghai' 依赖系统 tzdata 和 MySQL 时区表,容器里容易失败,偏移量写法更稳。
- 改完后必须重启服务:
sudo systemctl restart mysql(或mysqld),仅 kill 进程再手动启不触发配置重载 -
SET GLOBAL time_zone = '+08:00'是临时命令,重启即丢,且普通应用账号通常没权限执行 - 验证是否生效:运行
SELECT @@global.time_zone, @@session.time_zone;,两个都应返回+08:00
Docker环境要同步宿主机时区或显式声明TZ
MySQL 容器默认系统时区是 UTC,即使 my.cnf 写了 default-time-zone,只要 system_time_zone 还是 UTC,SYSTEM 就仍代表 +00:00 —— 配置被绕过。
- 启动时加
-e TZ=Asia/Shanghai并挂载-v /etc/localtime:/etc/localtime:ro - 进容器后执行
date,输出必须带CST或明确显示北京时间,否则时区没生效 - Alpine 类精简镜像需先装 tzdata:
apk add tzdata,再ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
DATETIME和TIMESTAMP字段行为差异会放大时区问题
就算服务端时区全设对了,字段类型选错照样出事:
-
TIMESTAMP存的是 UTC,读取时按当前会话时区自动转——同一行数据,在+08:00会话看到北京时间,在+00:00会话看到早 8 小时的 UTC 值 -
DATETIME不做任何转换,原样存原样取——如果历史数据是用DATETIME存的 UTC 时间,改时区根本救不回来,只能靠DATE_ADD()手动修正 - JDBC 连接字符串必须带
serverTimezone=GMT%2B8,否则驱动可能把传入时间按本地时区二次转换
@@time_zone,必须跑 SELECT NOW(), UTC_TIMESTAMP(), TIMEDIFF(NOW(), UTC_TIMESTAMP); —— 三者时间一致且差值为 08:00:00 才算真正闭环。


















