thinkphp的default_timezone配置不控制数据库写入时间,因php全局时区(date_default_timezone_get)和mysql时区(@@session.time_zone)未同步导致时间偏差;须统一设为+08:00并优先用int型时间戳存储。

ThinkPHP 的 default_timezone 配置不控制数据库写入时间
很多人改了 config/app.php 里的 'default_timezone' => 'Asia/Shanghai',却发现 create_time 字段还是比实际晚 8 小时——这是因为 ThinkPHP 的这个配置只影响框架内部分辅助方法(比如 think\facade\Date),并不改变 date()、strtotime() 或模型自动写入逻辑所依赖的 PHP 全局时区。
真正决定 date('Y-m-d H:i:s') 输出内容的,是 PHP 底层的 date_default_timezone_get() 返回值。如果它仍是 UTC,哪怕框架配置对了,模型里用 date() 格式化时间存进 DATETIME 字段,结果就是 UTC 时间字符串,看起来就“慢 8 小时”。
- 别只查框架配置,运行
dump(date_default_timezone_get())确认真实生效值 - ThinkPHP 6+ 启动时会主动调用
date_default_timezone_set('UTC'),所以必须显式覆盖,不能只靠 php.ini - 若在中间件或模型里重复调用
date_default_timezone_set(),可能被后续逻辑覆盖,应确保只在应用启动早期设一次
MySQL 的 @@session.time_zone 必须与 PHP 一致
PHP 显示正确 ≠ 数据库存的时间正确。常见现象是:控制器里 date('Y-m-d H:i:s') 输出北京时间,但查数据库发现 create_time 是 UTC 时间(比如你 20:00 插入,字段存的是 12:00:00)——这说明 MySQL 服务端把传入的字符串按自己的时区解释了。
执行 SELECT @@global.time_zone, @@session.time_zone;,如果返回 SYSTEM,不代表它等于系统当前时区;很多 XAMPP 或 Docker 环境下,SYSTEM 实际指向的是 UTC。
- 推荐在
my.cnf(或my.ini)中设置default-time-zone = '+08:00',然后重启 MySQL - 避免用
Asia/Shanghai作为 MySQL 时区值,部分旧版 MySQL 不识别,+08:00更稳定 - 不要依赖 PHP 层用
mysqli_query($conn, "SET time_zone = '+08:00'")临时设置——连接池复用时可能失效
模型时间戳字段类型选 int 比 DATETIME 更可靠
用 time() 存整型时间戳(Unix 秒数)天然规避所有时区格式化问题:time() 返回的是 UTC 秒数,但它是纯数字,无时区含义;读取时再用 date('Y-m-d H:i:s', $timestamp) 格式化,只要 PHP 时区设对,显示就准。
而 DATETIME 是字符串类型,MySQL 不做任何时区转换,你传什么它存什么。一旦 PHP 和 MySQL 时区没对齐,或者你在不同环境(CLI/Web)下执行,结果就会漂移。
- ThinkPHP 模型中可显式关闭自动格式化:
protected $type = ['create_time' => 'integer'] - 数据库字段类型对应改为
INT(11),不是TIMESTAMP(后者有 2038 年限制,且受 MySQL 隐式行为影响) - 若必须用
DATETIME,确保全链路(PHP → SQL → MySQL 配置)都统一为+08:00,少一个环节就出问题
DateTime 类构造时带 @ 前缀会强制按 UTC 解析
这是最容易踩的坑之一:写 new DateTime('@' . time()) 或 new DateTime('@1716417600'),哪怕 PHP 时区已是 Asia/Shanghai,这个对象创建出来的时间依然是 UTC —— @ 是硬编码语义,表示“这个数字是 UTC 秒数”,和当前时区无关。
结果就是:你用它 format('Y-m-d H:i:s') 出来的是 UTC 时间,而不是北京时间;再拿去存数据库,又多一层偏差。
- 正确做法一(从当前时间出发):
new DateTime('now', new DateTimeZone('Asia/Shanghai')) - 正确做法二(从时间戳出发):
(new DateTime())->setTimestamp(1716417600)->setTimezone(new DateTimeZone('Asia/Shanghai')) - 永远不要混用
@和setTimezone(),因为前者锁死 UTC,后者只是视图转换,容易误判逻辑
php.ini 可能不是同一份,导致 php think schedule:run 仍按 UTC 执行。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











