必须在任何时间函数调用前执行date_default_timezone_set(),否则date()、strtotime()等将按服务器默认时区解析,导致时间显示或计算错误;ci4不自动设时区,需手动在boot文件中调用,且须与数据库、mysql时区对齐。

date_default_timezone_set() 必须在任何时间函数调用前执行,否则 date()、strtotime() 等会按服务器默认时区(通常是 UTC 或系统本地)解析,导致显示或计算结果与预期不符。
CI4 本身不封装时区设置逻辑,它直接依赖 PHP 原生时间函数。这意味着你不能靠配置文件或服务自动设时区 —— 必须手动干预。
- 推荐在
app/Config/Boot/production.php或app/Config/Boot/common.php开头处调用:date_default_timezone_set('Asia/Shanghai'); - 避免在控制器方法里重复调用:既影响性能,又可能被多次执行(比如请求中多个地方调用),造成不可预测行为
- 不要用
ini_set('date.timezone', ...)替代:PHP 官方已明确标注该 ini 设置为“只读”,某些版本会静默失败 - 验证是否生效:可在任意位置加一行
echo date_default_timezone_get();,输出应为你设定的时区标识符(如Asia/Shanghai),而非UTC或空字符串
CI4 的 Model 类启用 $useTimestamps = true 时,created_at 和 updated_at 字段写入数据库前会调用 time() 获取 Unix 时间戳 —— 这个时间戳本身无时区信息,但后续用 date() 格式化展示时,就完全取决于 date_default_timezone_set() 的设置结果。
用 date() 和 strtotime() 处理常见业务时间
date() 是格式化输出的核心,strtotime() 负责把自然语言转成时间戳。二者配合能覆盖大多数场景,但要注意它们的边界。
-
date('Y-m-d H:i:s', strtotime('+7 days'))是安全的,但strtotime('next Monday')在周日执行时可能返回下周而非本周一(PHP 解析规则决定) - 避免直接传用户输入给
strtotime():比如$_GET['date']若为2026-02-30,会返回false,接着date()会回退到当前时间,造成静默错误 - 对日期字段做数据库查询时,别用
date()拼 SQL:应使用 CI4 的 Query Builder 时间函数(如$builder->where('created_at >=', '2026-08-01')),让数据库自己处理时区和精度 - 格式化时优先用
date('c')(ISO 8601)或date('Y-m-d\TH:i:sP'),便于前端 JSnew Date()正确解析;避免用中文字符(如“年”“月”)嵌入格式串,会破坏 JSON 兼容性
CI4 模型时间戳字段写入前的实际行为
当模型设置了 $useTimestamps = true,CI4 并不会在插入/更新前主动调用 date_default_timezone_set()。它只是简单调用 time() 获取秒级时间戳,然后写入数据库。
- 这意味着:如果你没提前设好时区,
time()返回的是服务器本地时间对应的 Unix 时间戳,但这个时间戳本身是 GMT 时间 —— 所以如果服务器时区是UTC,而你要存北京时间(UTC+8),就必须在应用层把时间加 8 小时再转成时间戳,或改用DateTime对象显式处理 -
$createdField和$updatedField字段类型建议设为DATETIME(非TIMESTAMP),因为 MySQL 的TIMESTAMP会自动转成 UTC 存储,读取时再转回系统时区,容易和 PHP 层逻辑冲突 - 若需毫秒级精度(如日志、审计),
time()不够用,得用round(microtime(true) * 1000)自行生成,并关闭$useTimestamps,手动赋值
真正容易被忽略的点是:CI4 的时间戳字段写入行为和数据库时区、PHP 时区、MySQL 全局时区三者必须对齐。哪怕只有一处错位(比如 MySQL time_zone 是 +00:00,而 PHP 设了 Asia/Shanghai),就会导致记录时间比实际晚 8 小时,且无法通过单纯改代码修复 —— 必须查清每一层的时区配置。











