应检查 strtotime() 返回值是否为 false,因其对非法输入返回 false 而不报错,易导致 1970-01-01 或警告;需在调用后立即判断并抛异常,同时注意空值、数据库字段类型与模型 $type 声明匹配、时区统一及避免模板中重复解析。

查 strtotime() 返回值是否为 false
ThinkPHP5.1 里循环中时间格式转换报错,十有八九是 strtotime() 拿到非法输入后默默返回 false,后续传给 date() 就输出 1970-01-01 或直接 Warning。它不抛异常,容易被忽略。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 每次调用
strtotime()后立刻加判断:if ($ts === false) { throw new \Exception("时间解析失败: {$input}"); } - 重点检查循环里拼接的字符串,比如
$date . '-01'遇到空值变成'-01',或数据库字段为NULL/ 空字符串时没过滤 - 测试用例要覆盖边界:传
'2024-02-30'、'abc'、''进去,看是否真能捕获
确认数据库字段类型和模型 $type 声明是否匹配
TP5.1 的 paginate() 不触发获取器,但如果你在循环前用了 select(),且模型里没声明 $type,那 create_time 字段读出来就是原始 int 或 string,不是 Carbon 实例,strtotime() 会误判。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 如果数据库存的是
INT时间戳,模型必须写:protected $type = ['create_time' => 'integer']; - 如果存的是
DATETIME字符串(如'2024-05-10 14:30:00'),$type应设为'datetime',否则strtotime('2024-05-10 14:30:00')虽能转,但时区可能错 - 别依赖模板里
date('Y-m-d', $vo['create_time'])—— 若字段实际是字符串但被当时间戳传入,date()会警告
避免在模板循环里反复调用 strtotime()
模板里写 {volist name="list" id="vo"}{php}echo date('Y-m-d', strtotime($vo['create_time']));{/php}{/volist} 是典型反模式:既没做空值判断,又在每次循环都解析同一格式字符串,性能差,还掩盖了源头问题。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 把格式化逻辑收回到控制器或模型里,用
withAttr()一次性处理:->withAttr('create_time', function($v) { return $v ? date('Y-m-d', (int)$v) : ''; }) - 若字段是 DATETIME 类型,直接用 MySQL 的
DATE()函数在查询层转换:->field('id, title, DATE(create_time) as create_date') - 模板只负责展示,不做计算;所有时间转换应在数据到达视图前完成
检查时区是否全局对齐
循环里日期“看起来不对”,比如比预期早/晚 8 小时,大概率是 PHP 默认时区、MySQL 连接时区、数据库字段存储时区三者不一致。TP5.1 不自动做时区转换,strtotime() 全按 date_default_timezone_get() 走。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 在入口文件或配置里统一设:
date_default_timezone_set('Asia/Shanghai'); - MySQL 连接 DSN 加
;timezone=Asia/Shanghai,或配置文件里加'timezone' => 'Asia/Shanghai' - 别信
strtotime('2024-05-10')—— 它默认按本地时区解释,若服务器时区是 UTC,结果就少 8 小时;更稳写法是(new \DateTime('2024-05-10', new \DateTimeZone('Asia/Shanghai')))->getTimestamp()
真正卡住排查的,往往不是语法错,而是某次循环里一个字段为 NULL 却没兜底,或者数据库存了 '0000-00-00' 这种非法值——strtotime() 静默失败,后面全崩。盯住第一条出错记录的原始值,比看报错行数更有效。



















