永久生效必须修改php.ini中date.timezone="asia/shanghai"并重启服务;其他方式仅当前请求有效,且无法覆盖cli或确保框架加载顺序。

date.timezone 在 php.ini 中设置才能永久生效,其他方式(如 date_default_timezone_set())只对当前请求或脚本生命周期有效。
改 php.ini 是唯一真正“永久”的方式
修改 php.ini 中的 date.timezone = "Asia/Shanghai" 并重启 PHP-FPM 或 Web 服务(Apache/Nginx),这个设置会作用于所有 PHP 进程、所有脚本、所有时间函数(date()、strtotime()、DateTime 构造等),且优先级最高。
- 必须找到**实际被加载的 php.ini**:运行
php --ini(CLI)或phpinfo()(Web)查 “Loaded Configuration File” - 取消注释并写标准 IANA 时区名:
date.timezone = "Asia/Shanghai"(别写GMT+8、PRC或China/Standard_Time) - Windows 下若报错,不是时区名问题,而是 PHP 版本太旧或未启用时区数据库;应升级 PHP,而非妥协用非标名称
- 改完不重启服务?配置完全不生效 —— PHP-FPM 进程、Apache 的 mod_php 模块、Nginx 的 fastcgi_pass 都要重载
date_default_timezone_set() 看似方便,实为“临时补丁”
它在脚本运行时覆盖默认时区,但仅限当前请求,且必须在任何时间函数调用前执行。它不能改变 ini_get('date.timezone') 的返回值,也不影响 CLI 环境下其他未显式调用它的脚本。
- 常见错误:在某个 include 文件里设了,但入口文件已调用过
date()→ 触发警告It is not safe to rely on the system's timezone settings - 返回值必须检查:
if (!date_default_timezone_set('Asia/Shanghai')) { /* 失败处理 */ },否则设错也无感知 - CLI 和 Web 环境可能加载不同 php.ini,导致同一段代码在命令行跑出 UTC 时间,在浏览器却正常 —— 这种差异只能靠统一入口设置来掩盖,无法根治
为什么系统时区和 PHP 时区要分开看?
Linux 系统时区(timedatectl status 输出)只影响 gettimeofday()、microtime(true) 等返回 Unix 时间戳的函数;PHP 的 date()、DateTime 完全无视系统时区,只认 date.timezone 或运行时设置。两者不一致不会报错,但会导致日志时间、缓存键、JWT 过期判断等逻辑出现 8 小时偏差。
立即学习“PHP免费学习笔记(深入)”;
- 不要试图靠
sudo timedatectl set-timezone Asia/Shanghai来“修复 PHP 时间”——它对 PHP 时间函数无效 - 数据库时间字段(如
DATETIME)写入值是否带时区,取决于你用的是now()还是 PHP 生成的字符串;若 PHP 时区设错,date('Y-m-d H:i:s')生成的就是错的时间 - 最稳妥的工程实践:数据库存 UTC,PHP 环境设
Asia/Shanghai作展示默认值,用户侧再按偏好动态转换
.htaccess 和 ini_set() 本质都是“降级兼容方案”
它们只在特定条件下可用,且稳定性远低于 php.ini 修改:
-
.htaccess中写php_value date.timezone "Asia/Shanghai"要求 Apache 启用mod_php(而非 FPM),且AllowOverride允许php_value—— 现代生产环境基本禁用 -
ini_set('date.timezone', 'Asia/Shanghai')受限于disable_functions配置,且效果等同date_default_timezone_set(),并无额外优势 - 二者都无法覆盖 CLI 模式,也无法保证框架自动加载顺序 —— 比如 Laravel 的
bootstrap/app.php执行前,某些扩展可能已触发时间函数
真正的“永久”,只存在于 php.ini + 服务重启这个闭环里。其他所有方式,都是在迁就权限、环境或历史包袱。而最容易被忽略的一点是:哪怕你设对了时区,只要业务逻辑里还混用 time()、date() 和 DateTime,时间依然会悄悄错乱。











