thinkphp5 的 default_timezone 不影响数据库连接,因为它仅控制 php 时间函数和框架内时间戳生成,不参与 pdo 连接建立或修改 mysql 时区;必须通过 pdo::mysql_attr_init_command 设置 set time_zone = '+08:00' 才能校准数据库时区。

ThinkPHP5 本身不接管数据库连接层的时区设置,default_timezone 配置只影响 PHP 时间函数和框架内部时间戳生成(如 auto_timestamp),**不会自动向 MySQL 发送 SET time_zone 命令**。必须手动在 PDO 连接参数中注入初始化指令,否则 PHP 用 Asia/Shanghai 生成的时间字符串,MySQL 却按系统默认时区(常为 SYSTEM 或 UTC)解析,导致 NOW()、CURDATE() 和写入时间错位 8 小时。
为什么 default_timezone 不影响数据库连接
ThinkPHP5 的 default_timezone(位于 config/app.php)仅用于:
- 调用
date()、strtotime()等 PHP 原生时间函数时的上下文 - 模型中
auto_timestamp自动生成的create_time/update_time值 - 日志记录、缓存过期等框架内时间逻辑
它**完全不参与数据库连接建立过程**,也不会修改 PDO 的行为。MySQL 服务端仍按自己的 time_zone 变量解释 SQL 中的时间字面量或函数返回值。
在 database.php 中通过 PDO::MYSQL_ATTR_INIT_COMMAND 设置
这是最直接、最可靠的方式,确保每次连接建立后立即执行时区校准命令。
在 config/database.php 的数据库配置数组中添加 params 子项:
'params' => [
\PDO::MYSQL_ATTR_INIT_COMMAND => "SET time_zone = '+08:00'",
],
注意以下几点:
-
+08:00是固定偏移,比Europe/Madrid或Asia/Shanghai更稳妥——MySQL 对命名时区支持依赖系统时区表是否加载,而偏移量无需额外配置 - 该配置必须放在
params数组内,不能写在顶层配置键里 - 如果已存在
params(如设置了PDO::ATTR_PERSISTENT),直接追加这一项即可,PDO 会合并处理 - 使用命名时区(如
"SET time_zone = 'Asia/Shanghai'")前,请先在 MySQL 中执行SELECT * FROM mysql.time_zone_name LIMIT 1;确认时区表已导入
DSN 方式连接时如何带时区参数
若使用 DSN 字符串方式调用 Db::connect()(例如临时切换库或命令行脚本),需将初始化命令编码进 DSN:
$dsn = 'mysql://root:pass@127.0.0.1:3306/test#utf8?initCommand=SET%20time_zone%20%3D%20%27%2B08%3A00%27'; Db::connect($dsn);
关键点:
-
initCommand是 MySQL PDO DSN 的标准参数名,不是 ThinkPHP 自定义的 - 值必须 URL 编码:
SET time_zone = '+08:00'→SET%20time_zone%20%3D%20%27%2B08%3A00%27 - 该方式只对本次连接生效,不影响全局配置
验证是否生效的简单方法
连接成功后,执行原始 SQL 查询确认 MySQL 当前会话时区:
Db::query("SELECT @@session.time_zone AS tz");
// 返回结果应为 +08:00 或 SYSTEM(若未生效则大概率是 SYSTEM)
更实际的验证是对比:
- PHP 中
date('Y-m-d H:i:s')输出 - SQL 中
SELECT NOW()返回值
二者应完全一致。若仍有 8 小时差,优先检查 params 是否拼写错误、是否被其他配置覆盖,以及 MySQL 服务端是否禁用了 init_command(如 safe_mode 启用时)。
真正起作用的是那行 PDO::MYSQL_ATTR_INIT_COMMAND,而不是 config/app.php 里的 default_timezone —— 后者管 PHP,前者管 MySQL,两者必须各自配对,缺一不可。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











