ThinkPHP 的 default_timezone 不影响数据库连接,仅控制 PHP 时间函数和模型时间戳;需在 PDO 连接参数中设置 SET time_zone = '+08:00',并同步配置 MySQL 全局时区。

ThinkPHP 的 default_timezone 不影响数据库连接
很多人以为改了 config/app.php 里的 default_timezone 就能同步数据库时区,其实它只控制 PHP 时间函数(如 date()、strtotime())和模型自动时间戳(create_time)的生成逻辑,**完全不干预 PDO 连接行为**。MySQL 仍按自己的 time_zone 变量解析 NOW()、CURDATE() 或你传入的 '2024-05-21 14:00:00' 字符串——如果 MySQL 是 UTC,而 PHP 是 Asia/Shanghai,写进去就天然差 8 小时。
必须在 PDO 连接参数中设置 time_zone
ThinkPHP 5/6 都需要手动注入初始化命令,让每次连接建立后立即校准时区:
- 在
config/database.php的数据库配置数组里加params子项 - 用
PDO::MYSQL_ATTR_INIT_COMMAND发送SET time_zone = '+08:00' - 别写
'Asia/Shanghai'—— MySQL 要求系统已加载时区表,而很多云服务器或 Docker 环境没导入,+08:00更稳妥 - 如果已有
params(比如开了长连接),直接追加这一项即可,PDO 会合并处理
'params' => [
\PDO::MYSQL_ATTR_INIT_COMMAND => "SET time_zone = '+08:00'",
],
MySQL 服务端时区也要设为 +08:00
光改 PHP 连接还不够,MySQL 全局时区可能仍是 SYSTEM 或 UTC,导致 NOW() 返回值本身就不对:
- 执行
SELECT @@global.time_zone, @@session.time_zone;查当前值 - 临时生效:运行
SET GLOBAL time_zone = '+08:00';(重启失效) - 永久生效:在
/etc/mysql/my.cnf的[mysqld]段下加default-time-zone = '+08:00',然后重启mysqld - 注意:历史数据不会被转换,只影响后续读写
避免在模型里用 date() 格式化时间存入 DATETIME 字段
模型中若手动写 date('Y-m-d H:i:s') 再塞进 DATETIME 字段,本质是把一个带时区含义的字符串交给 MySQL 解析——而 MySQL 解析时只认自己当前 time_zone,不看 PHP 是什么时区。容易踩的坑:
立即学习“PHP免费学习笔记(深入)”;
- 确保
date_default_timezone_get()返回的是Asia/Shanghai,否则date()输出就是错的 - 更稳的做法:模型时间戳字段类型设为
int,直接存time()返回的 Unix 时间戳(无时区歧义) - 如果非要用
DATETIME,就别混用date()和NOW();统一走连接层SET time_zone+ PHP 显式格式化
最常被忽略的一点:CLI 环境(比如 php think schedule:run)和 Web 请求可能加载不同的 php.ini,导致时区不一致;务必用 php --ini 和 phpinfo() 分别确认两处的 date.timezone 是否都设成了 Asia/Shanghai。



















