自动时间戳存为int更可靠,因其存储Unix时间戳(秒数)且与时区无关;而datetime/timestamp字段受MySQL服务端时区影响,易导致前后端时间偏差8小时。

为什么自动时间戳存成 int 类型更可靠
因为 int 存的是 Unix 时间戳(秒数),它本身与时区无关;而 datetime 或 timestamp 字段在 MySQL 里受服务端时区影响,一旦 @@global.time_zone 不是 'SYSTEM' 或系统时区不是 Asia/Shanghai,就容易出现前后端差 8 小时的问题。
常见错误现象:数据库里 create_time 显示为 2026-04-30 10:14:22,但你实际插入时间是下午 6 点——这就是 MySQL 按 UTC 解析了 datetime 值。
- 模型字段类型设为
int,数据库字段类型也必须是INT(11)(非BIGINT也够用) - 全局配置保持
'auto_timestamp' => true,不加引号、不写字符串 - 模型中无需额外设置
$autoWriteTimestamp,默认就会按int处理 - 如果已用
datetime字段上线,别直接改字段类型,先用 SQL 迁移:UPDATE user SET create_time = UNIX_TIMESTAMP(create_time);
再把字段类型改成INT
如何确认当前用的是 Unix 时间戳写入
不能只看数据库字段类型,得看写入逻辑是否真用了 time()。最直接的验证方式是在控制器里加一行:
dump(date_default_timezone_get(), date('Y-m-d H:i:s'), time());
输出应类似:"Asia/Shanghai"、"2026-04-30 18:14:22"、1746036862(三者时间要能对上)。
立即学习“PHP免费学习笔记(深入)”;
- 如果
date_default_timezone_get()返回空或UTC,说明 PHP 时区没设,需在php.ini加date.timezone = Asia/Shanghai,或在入口文件开头调用date_default_timezone_set('Asia/Shanghai') - 如果
time()值比当前 Unix 时间小 28800(8 小时秒数),说明系统时区仍是 UTC - 模型中若手动调用
date('Y-m-d H:i:s')再塞进int字段,会出错——date()返回字符串,强转int得到 0
model 中 $autoWriteTimestamp 设为 'datetime' 的陷阱
这个值看似只是“指定类型”,实则会绕过 time(),改用 date($this->dateFormat) 格式化后写入——哪怕字段是 INT 类型,也会把字符串如 "2026-04-30 18:14:22" 强转成 0。
- 错误配置示例:
protected $autoWriteTimestamp = 'datetime';(即使数据库字段是INT,也会失效) - 正确做法:留空或设为
true,让框架走默认int路径;字段类型不匹配时,框架会报错,而不是静默写 0 - 如果你真要用
datetime字段,MySQL 必须设SET GLOBAL time_zone = '+08:00';,且应用层不能依赖date()函数做任何中间转换
模板里显示时间戳字段时的隐式转换
ThinkPHP 默认会对 int 类型时间戳字段自动调用 date($format, $value),所以模板里写 {data.create_time} 能直接显示“2026-04-30 18:14:22”。但这依赖配置项 'datetime_format' => 'Y-m-d H:i:s'。
- 如果关闭了自动格式化(
'datetime_format' => false),模板输出就是纯数字1746036862,需要手动{|date='Y-m-d H:i:s',###} - 修改器(accessor)里不要重复格式化:比如定义了
getCreateTimeAttr,又在里面调用date(),会导致双重格式化或时区错乱 - API 接口返回 JSON 时,时间戳字段会原样输出数字,前端自己处理——这才是 Unix 时间戳该有的样子
真正容易被忽略的点是:MySQL 的 timestamp 类型字段自带时区转换逻辑,哪怕你存的是 int,只要字段类型是 timestamp,服务端仍可能按系统时区重解释。所以字段类型、PHP 时区、MySQL 时区,三个必须一致,缺一不可。



















