mysql 5.7中from_unixtime()返回无时区datetime,依赖会话time_zone;可靠时区转换需用convert_tz()链式处理:from_unixtime→convert_tz→date_format,并注意null安全与±hh:mm格式限制。

MySQL 5.7里FROM_UNIXTIME()默认不处理时区转换
直接用FROM_UNIXTIME(1609459200)返回的是服务器本地时区的时间,不是UTC或你想要的时区。MySQL 5.7没有内置的“带时区的时间戳转字符串”函数,必须手动干预时区上下文。
常见错误是以为加个%T格式就能切换时区——其实格式化和时区是两件事,FROM_UNIXTIME()只负责把秒数转成DATETIME,而这个DATETIME值本身不带时区信息,它依赖当前会话的time_zone设置。
- 查当前会话时区:
SELECT @@session.time_zone;(常见返回SYSTEM或+00:00) - 临时切到目标时区再转换:
SET time_zone = '+08:00'; SELECT FROM_UNIXTIME(1609459200); - 注意:该设置只对当前连接有效,应用层需确保每次查询前已设好,或在连接池初始化时统一配置
用CONVERT_TZ()做显式时区转换(推荐)
CONVERT_TZ()才是MySQL 5.7中真正可靠的时区转换函数,但它要求输入是DATETIME类型,不能直接接时间戳。所以得先转成DATETIME,再转时区,最后格式化。
典型链式写法:DATE_FORMAT(CONVERT_TZ(FROM_UNIXTIME(1609459200), '+00:00', '+08:00'), '%Y-%m-%d %H:%i:%s')
- 第一个参数:
FROM_UNIXTIME(1609459200)→ 默认按会话时区解释,保险起见建议显式指定源时区为'+00:00' - 第二、三个参数:源时区和目标时区,必须用
±HH:MM格式(如'+08:00'),不能用'Asia/Shanghai'(MySQL 5.7不支持命名时区,除非你提前加载了时区表且确认生效) - 性能影响:每次调用都涉及两次时区计算,高并发场景下比纯
FROM_UNIXTIME()略慢,但逻辑清晰、结果确定
避免踩@@global.time_zone的坑
有人想一劳永逸改全局时区:SET GLOBAL time_zone = '+08:00';——这在MySQL 5.7上可能无效或被覆盖。
-
@@global.time_zone初始值通常是SYSTEM,表示继承系统时区;改完后新连接仍可能读到旧值,尤其在mysqld重启后未持久化配置 - 真正生效需同时修改配置文件
my.cnf里的default-time-zone = '+08:00'并重启,但这会影响所有数据库,不灵活 - 更稳妥的做法:应用层在获取连接后立即执行
SET time_zone = '+08:00';,或封装成存储过程统一处理
如果要批量转换字段,别漏掉NULL安全
表里created_at_ts字段可能含NULL,直接套CONVERT_TZ(FROM_UNIXTIME(created_at_ts), ...)会导致整行结果为NULL。
- 加
IFNULL()兜底:IFNULL(DATE_FORMAT(CONVERT_TZ(FROM_UNIXTIME(created_at_ts), '+00:00', '+08:00'), '%Y-%m-%d %H:%i:%s'), '1970-01-01 00:00:00') - 或者用
COALESCE(),语义更明确 - 注意:
FROM_UNIXTIME(NULL)返回NULL,但CONVERT_TZ(NULL, ...)也返回NULL,所以NULL传播链很长,必须在最外层拦截
FROM_UNIXTIME()输出的是无时区DATETIME,而CONVERT_TZ()才是唯一可控的转换入口——所有绕过它的写法,比如靠UNIX_TIMESTAMP()反向推算,最终都会在夏令时或跨年边界出问题。











