date_default_timezone_set() 必须在所有日期函数前调用,因其设置的是进程级全局状态;若先调用 date() 等函数,PHP 会按未设时区的不安全逻辑解析时间,导致警告和时间错误。

PHP 中 date_default_timezone_set() 必须在所有日期时间函数调用前执行,否则会触发 Warning: date(): It is not safe to rely on the system's timezone settings 警告,且返回时间可能错误。
为什么 date_default_timezone_set() 不能写在 date() 之后
PHP 的时区设置是进程级全局状态,一旦脚本开始解析时间(比如调用 date()、strtotime()、new DateTime()),PHP 就会按当前已生效的时区(或系统默认)做解析。如果此时还没调用 date_default_timezone_set(),它会退回到不安全的系统时区推断逻辑,不仅报 warning,还可能导致缓存时间戳被错误解释。
- 即使只调用一次
date(),也会触发初始化;后续再设时区也改不了已解析的时间逻辑 - CLI 模式下系统时区常为 UTC,Web 服务器(如 Apache)可能继承系统配置,但容器或 Docker 环境中更不可靠
-
ini_set('date.timezone', 'Asia/Shanghai')无效 —— 这个 ini 配置项在运行时不可修改,仅能通过date_default_timezone_set()或 php.ini 设置
推荐的设置位置和常见有效值
应在脚本最顶部(或框架入口文件如 index.php 开头)立即调用,且优先使用 IANA 时区标识符(不是 GMT+8 或 CST 这类缩写)。
- 中国大陆应使用
'Asia/Shanghai'(不是'PRC',后者已废弃且部分 PHP 版本不识别) - 香港、澳门用
'Asia/Hong_Kong',台湾用'Asia/Taipei' - 避免用
'Etc/GMT+8':IANA 规定Etc/GMT+8实际表示 UTC−8(符号反直觉),应改用'Etc/UTC'或直接选城市名 - 可通过
date_default_timezone_get()检查当前生效时区,调试时建议加一行var_dump(date_default_timezone_get());
与 DateTime 类共用时的注意事项
date_default_timezone_set() 对 DateTime 构造函数默认行为有影响,但不强制约束 —— new DateTime() 会使用该全局时区,而 new DateTime('now', new DateTimeZone('UTC')) 则明确覆盖它。
立即学习“PHP免费学习笔记(深入)”;
- 未指定时区的
new DateTime()和date()行为一致,都依赖date_default_timezone_set()结果 - 若代码混用全局函数和
DateTime,且部分地方显式传了DateTimeZone,要注意逻辑一致性:比如日志用date()写本地时间,但数据库存 UTC,就需统一转换逻辑,而非仅靠全局时区 - PHP 8.1+ 开始,未设时区时
new DateTime()会抛ValueError(而非 warning),兼容性更严格
检查是否生效的最小验证代码
别只信 IDE 提示或文档,直接跑一段可验证的代码:
date_default_timezone_set('Asia/Shanghai');
echo date('Y-m-d H:i:s') . "\n"; // 应输出北京时间
echo (new DateTime())->format('Y-m-d H:i:s') . "\n"; // 应与上行一致
var_dump(date_default_timezone_get()); // 输出 string(13) "Asia/Shanghai"
如果第二行时间比第一行慢 8 小时,说明 DateTime 没走全局时区 —— 很可能是前面某处提前调用了 new DateTime() 或 date(),导致时区尚未设置就被初始化了。
时区问题往往藏在自动加载、Composer 自动脚本或框架中间件里,真正难排查的不是怎么设,而是谁在你之前悄悄读了时间。











