必须同步PHP运行时、MySQL会话、Linux系统三处时区,缺一不可;PHP需在入口顶部调用date_default_timezone_set('Asia/Shanghai'),MySQL需会话级SET time_zone='+08:00',Linux需配置TZ环境变量。

必须同步 PHP 运行时、MySQL 会话、Linux 系统三处时区,只改一处等于没改。
PHP 层必须在入口顶部调用 date_default_timezone_set()
这个函数不是“设一次就全局生效”的配置,而是运行时状态。一旦第一个 date() 或 new DateTime() 执行了,再设就晚了。
- 放在
public/index.php(ThinkPHP/Laravel)或index.php(原生)最顶部,require 框架前 - 值必须是
Asia/Shanghai,别用PRC或GMT+8—— 后者 PHP 不识别,PRC在部分版本已弃用 - 检查是否生效:
echo date_default_timezone_get();必须输出Asia/Shanghai,不是空字符串、UTC或警告 - 框架项目(如 Laravel)优先改
config/app.php的'timezone' => 'Asia/Shanghai',而不是硬写date_default_timezone_set(),否则和 Carbon 行为冲突
MySQL 会话级 time_zone 必须显式设置
PHP 连上 MySQL 后,默认不会把 PHP 的时区“告诉”数据库。NOW()、CURDATE() 等函数仍按 MySQL 自己的 time_zone 走,常为 SYSTEM 或 +00:00。
- 查当前会话时区:
SELECT @@session.time_zone;—— 如果返回SYSTEM,得再确认系统时区是否真为东八区;如果返回+00:00,就是问题根源 - PDO 初始化后立即执行:
$pdo->exec("SET time_zone = '+08:00'"); - mysqli 初始化后执行:
$mysqli->query("SET time_zone = '+08:00'"); - Laravel 配置中加:
'options' => [PDO::MYSQL_ATTR_INIT_COMMAND => "SET time_zone = '+08:00'"] - 别依赖连接池自动继承 —— 不同连接可能来自不同初始化上下文,时区不一致
DATETIME 和 TIMESTAMP 字段行为完全不同
很多人以为改了时区,所有时间字段就“自动对齐”了,其实 DATETIME 根本不参与时区转换,而 TIMESTAMP 会强制转成 UTC 存储再转回,极易在跨时区部署时翻车。
立即学习“PHP免费学习笔记(深入)”;
-
DATETIME:存什么就是什么,MySQL 完全不解释时区 —— ThinkPHP 写入'2026-10-12 13:37:00',它就当本地时间存进去,若 MySQL 实际时区是 UTC,就少 8 小时 -
TIMESTAMP:入库时按当前time_zone转成 UTC,读取时再按当前time_zone转回来 —— 看似智能,但换服务器时区一变,同一字段值含义就乱了 - 推荐统一用
TIMESTAMP+ 全局设time_zone = '+08:00',或更稳妥地:全部改用DATETIME,并在业务层确保写入/读取都基于明确时区(如用DateTime构造后格式化) - 模型自动填充(如
create_time)若依赖NOW(),必须确认该函数行为和你的 PHP 时间逻辑一致,否则字段值和代码里生成的时间对象永远差 8 小时
CLI 环境和 Web 环境时区经常不一致
你在浏览器里 date() 显示正确,但 php artisan schedule:run 或 crontab 脚本却差 8 小时?大概率 CLI 模式根本没读到你设的时区。
- 查 CLI 当前时区:
php -r "echo date_default_timezone_get();" - 查 Web 当前时区:写个
phpinfo();页面看 Date 小节 - CLI 模式不依赖 Web 的 php.ini,常加载独立的配置文件 —— 运行
php --ini看 Loaded Configuration File,确认该文件里有date.timezone = Asia/Shanghai - 最可靠做法:所有可独立执行的脚本(包括定时任务、队列 worker、导入脚本)开头都加一次
date_default_timezone_set('Asia/Shanghai'),不赌配置文件有没有漏 - Docker 环境尤其要注意:基础镜像默认 UTC,得在
Dockerfile里加ENV TZ=Asia/Shanghai并重建软链
真正麻烦的不是某一行代码写错了,而是三套时间系统(PHP 运行时、MySQL 会话、Linux 系统)各自为政。哪怕只有一处没对齐,比如 CLI 脚本忘了设时区,或者某个中间件悄悄覆盖了 date_default_timezone_set(),时间错乱就会在最意想不到的地方爆发。



















