yii2时间戳偏差需统一php、框架、mysql三层时区为asia/shanghai:一、入口首行设date_default_timezone_set('asia/shanghai'),cli需配php.ini;二、配置'timezone'=>'asia/shanghai'及formatter默认时区;三、dsn加servertimezone=asia/shanghai,字段优选datetime;四、多角度验证并修正历史数据。

Yii2 应用时间戳偏差,大概率是框架、PHP 运行时、MySQL 三层时区没对齐,不是单改一个配置就能解决。关键要统一锚点,推荐全部按 Asia/Shanghai(东八区)处理,避免混用 UTC 和本地时间引发隐式转换。
一、确认并设置 PHP 全局时区
Yii2 依赖 PHP 的 date_default_timezone_get(),如果没设或设错,date()、DateTime 构造、日志时间都会偏移。
- 在应用入口文件
web/index.php最顶部第一行加:date_default_timezone_set('Asia/Shanghai'); - 不要用
PRC或GMT+8—— 已废弃,PHP 8.1+ 会警告;必须用 IANA 标准名 - 检查 CLI 场景(如
yii migrate):运行php -r "echo date_default_timezone_get();",若非Asia/Shanghai,需单独修改 CLI 对应的php.ini中date.timezone = Asia/Shanghai
二、配置 Yii2 框架自身时区
Yii2 的 'timeZone' 配置只影响部分组件(如 Formatter 的日期格式化),不改变底层时间生成逻辑,但必须与 PHP 全局时区一致,否则显示和存储语义分裂。
- 打开
config/web.php或config/main.php,确保包含:'timeZone' => 'Asia/Shanghai', - 同时检查
'formatter'组件是否设置了'defaultTimeZone',建议显式设为相同值:'defaultTimeZone' => 'Asia/Shanghai' - 该配置不影响数据库读写,仅用于
Yii::$app->formatter->asDatetime()等展示层
三、修正数据库连接与字段行为
Yii2 默认使用 PDO 连接 MySQL,但时区仍由 MySQL 服务端和 JDBC/驱动参数共同决定——即使框架设对了,数据库层错仍会导致 ActiveRecord::find()->one() 返回的时间偏差。
- 检查 MySQL 服务端实际时区:
执行SELECT @@global.time_zone, @@session.time_zone;,若返回SYSTEM,再查系统时区:timedatectl | grep "Time zone";如系统是UTC,就必然差 8 小时 - 在
config/db.php的 DSN 后追加时区参数(适用于 MySQL 5.7+ / 8.0+):'dsn' => 'mysql:host=localhost;dbname=test;charset=utf8mb4;serverTimezone=Asia/Shanghai' - 字段类型务必明确:
• 若用TIMESTAMP,MySQL 会自动转 UTC 存储,读取时再按会话时区转回 → 容易因会话时区波动出错
• 强烈建议改用DATETIME,它原样存储、原样返回,Yii2 插入前用new DateTime('now', new DateTimeZone('Asia/Shanghai'))保证源头一致
四、验证与兜底处理
修复后别只看页面显示,要用多角度交叉验证,尤其注意历史数据是否受影响。
- 在控制器中加测试代码:
var_dump(date('Y-m-d H:i:s'), (new \DateTime())->format('Y-m-d H:i:s'));
两个输出应完全一致 - 查一条含时间字段的记录:
var_dump($model->created_at instanceof \DateTime ? $model->created_at->format('c') : $model->created_at);
确认格式中时区标识是+08:00而非Z或+00:00 - 若已有历史数据因旧时区错存(如 DATETIME 字段存了 UTC 时间但当北京时间用),不可直接改全局配置,应写迁移脚本用
DATE_ADD(created_at, INTERVAL 8 HOUR)批量修正











