数据库时区与Laravel配置不一致会导致时间错乱,表现为存取时间不符、“今天”范围不一致等,根本原因是PHP、MySQL、Laravel三者时间解释不统一;须统一设为UTC并前端转换展示。

数据库时区与 Laravel 配置不一致会导致时间错乱
最直接的表现是:存进去的时间和查出来的时间对不上,或者“今天”的范围在不同地方算出不同结果。根本原因在于 PHP、MySQL 和 Laravel 三者对时间的解释不统一。
MySQL server_time_zone 和 Laravel timezone 不匹配的典型现象
当你执行 SELECT NOW(),看到的是 MySQL 当前认为的“现在”,但它可能和 Laravel 的 Carbon::now() 差好几个小时。常见错误包括:
- 用户下午 3 点提交表单,数据库存成凌晨 3 点(比如 MySQL 设为
SYSTEM,而系统时区是 CST,Laravel 却设为UTC) -
whereDate('created_at', today())查不到当天数据,因为today()按 Laravel 时区算,但数据库字段实际存储的是另一个时区的值 - 定时任务(如
Schedule::command('foo')->dailyAt('09:00'))在错误时间触发,因为调度器依赖 PHP 时间,而数据库条件又按另一套时区解析
PHP date.timezone、MySQL time_zone、Laravel config/app.php timezone 三者必须协同
不是“随便设一个能用就行”,而是要明确分工:
-
php.ini中的date.timezone建议设为UTC(避免系统级偏差) - MySQL 启动时应显式设置
default-time-zone='+00:00'或default-time-zone='UTC',不要依赖SYSTEM - Laravel 的
config/app.php中timezone也设为'UTC'—— 这是关键:统一存储基准 - 前端展示或用户本地逻辑,再用
Carbon::now('Asia/Shanghai')->format(...)转换,而不是在数据库层硬调
容易被忽略的迁移/查询陷阱
即使配置全对,下面这些操作仍会绕过时区控制,导致隐性偏差:
-
DB::raw("NOW()")直接调用 MySQL 函数,走的是 MySQL 自身时区,和 Laravel 无关 - 使用
$model->created_at->format('Y-m-d')时,若模型没声明$dates或用了自定义访问器但没返回 Carbon 实例,format可能作用于字符串而非时区感知对象 - 数据库字段类型是
DATE而非DATETIME或TIMESTAMP,会丢失时区上下文,Laravel 无法自动转换
真正麻烦的从来不是“设不对”,而是“设了一半”——比如只改了 Laravel 配置,忘了 MySQL 启动参数,或者用 raw 表达式绕过了 ORM 的时区处理链。


















