时区差异从哪来
时间函数的输出取决于会话时区:云数据库与容器镜像常默认 UTC,业务按东八区理解就会出现固定的 8 小时偏差。更隐蔽的是 TIMESTAMP 与 DATETIME 的差异:TIMESTAMP 底层存 UTC、读取时按会话时区换算,DATETIME 则原样存取不换算,两者混用最容易出问题。
基本信息
| 适用 | MySQL 5.7 / 8.0 |
|---|---|
| 查看 | SELECT @@global.time_zone, @@session.time_zone; |
| 取值方式 | 固定偏移如 +08:00,或命名时区如 Asia/Shanghai(需加载时区表) |
| 持久化 | my.cnf 的 [mysqld] 段设置 default-time-zone |
查看与设置
查看与设置
SELECT NOW(), UTC_TIMESTAMP(), @@global.time_zone, @@session.time_zone; # 对比本地时间与 UTC 时间
SET GLOBAL time_zone = '+08:00'; # 新连接生效
SET time_zone = '+08:00'; # 仅当前会话
SELECT @@system_time_zone; # 操作系统时区,作为参考
SHOW VARIABLES LIKE 'time_zone'; # 交叉验证当前取值加载时区表与持久化配置
加载时区表与持久化配置
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -uroot -p mysql # 导入时区表,命名时区才可用
# my.cnf 持久化:
# [mysqld]
# default-time-zone = '+08:00'
systemctl restart mysqld # 或用 SET GLOBAL 临时生效,重启后按配置为准
mysql -e "SELECT @@global.time_zone, @@session.time_zone;" # 变更后核对两种时间类型对比
| 类型 | 存储方式 | 行为差异 |
|---|---|---|
| TIMESTAMP | 底层存 UTC | 显示时按会话时区换算,取值范围到 2038 年 |
| DATETIME | 原样存储 | 不随会话时区变化,跨时区显示会出现偏差 |
| CURRENT_TIMESTAMP 默认值 | 写入时取当前时刻 | 受会话时区影响 |
| UNIX_TIMESTAMP() | 按会话时区解释字符串 | 与时间戳互转时要确认时区 |
| NOW() | 返回会话时区时间 | 与 UTC_TIMESTAMP() 结果不同属正常 |
典型使用场景
- 跨国业务统一按 UTC 存储,展示层再做本地化转换
- 排查「时间差 8 小时」类问题,先核对会话时区
- 数据迁移前后确认时区设置,避免时间字段整体偏移
- 容器化部署时统一镜像与数据库时区配置
- 日志与业务时间对不上,检查应用与数据库两侧时区
跨时区系统建议统一用 UTC 存储、在展示层转换;如果必须存本地时间,就统一用 DATETIME 并明确写入时区,不要两种类型混用。
修改时区后历史 TIMESTAMP 数据的显示会变化,但底层值并未改变;若把 DATETIME 与 TIMESTAMP 混在同一业务里,很容易出现难以解释的时间错位。
排错顺序
- 先看系统时区与会话时区,确认差值来源是系统设置还是连接设置
- 再核对应用连接初始化语句是否显式设置了时区
- 最后确认时区表是否已加载,命名时区能否正常使用