根本原因是TP5.1对datetime字段默认转为字符串,而模板date修饰器只接受整型时间戳,传入字符串触发警告。解决方法:{$create_time|strtotime|date='Y-m-d'}或查出后在控制器统一转换。

ThinkPHP 5.1.38 在循环中格式化日期时报 A non well formed numeric value encountered,根本原因是数据库 datetime 字段被框架自动转成字符串后,又被模板里的 date 修饰器当作时间戳处理——它只认 int,不认 string。
为什么 foreach 里 {$time|date='Y-m-d'} 总是崩
TP5.1 对 datetime 类型字段默认做字符串转换(比如 '2026-07-09 14:22:33'),但 {$time|date='Y-m-d'} 内部调用的是 PHP 原生 date() 函数,该函数第一个参数必须是整数时间戳。传入字符串会触发警告并返回 false,最终渲染成 1970-01-01 或空值。
- 不是模板语法写错,而是数据类型在进模板前就“不对路”
-
paginate()返回的集合不触发模型获取器,所以即使你写了getCreateTimeAttr,循环里也用不上 - TP5.1 的
$type = ['create_time' => 'datetime']只影响单条模型实例,对select()或分页原始数组无效
直接在模板里修:用管道链补 strtotime
不改模型、不改查询,最快见效的方式是让字符串先转时间戳再格式化:
{$create_time|strtotime|date='Y-m-d H:i:s'}
注意三点:
立即学习“PHP免费学习笔记(深入)”;
-
strtotime必须写在date前面,顺序不能反 -
date='Y-m-d H:i:s'的等号和单引号缺一不可;写成date="Y-m-d"或date=Y-m-d都会解析失败 - 如果
$create_time可能为空(如 NULL),strtotime(null)返回 false,date()会警告——加个兜底:{$create_time|default=''|strtotime|date='Y-m-d'}
更稳的做法:查完就转,别拖到模板
在控制器里把日期字段统一转好,避免模板里重复逻辑和类型风险:
$list = Db::name('article')->select();
foreach ($list as &$item) {
$item['create_time'] = date('Y-m-d H:i:s', (int)strtotime($item['create_time']));
}
$this->assign('list', $list);
或者用 array_map(TP5.1 没 collect()):
$list = array_map(function($item) {
$item['create_time'] = date('Y-m-d', strtotime($item['create_time']));
return $item;
}, $list);
- 别在循环里反复调用
strtotime()——性能差,且每次都要做字符串→时间戳→字符串的来回转换 - 如果字段名不固定(比如有
create_time、update_time、publish_time),提取成公共函数或封装为工具方法 - 注意时区:确保
date_default_timezone_set('Asia/Shanghai')已设置,否则本地开发和线上可能差 8 小时
长期建议:升级或换模型层处理
TP5.1.38 是已知存在类型转换缺陷的老版本(比如 Validate.php 在 PHP 7.4+ 下还会因 protected array $rule 报语法错误)。真正根治这类问题,得跳出“修模板”的思路:
- 升到 TP5.1.42+(官方 LTS 最终版),它修复了部分自动转换逻辑和类型声明兼容性
- 若无法升级,至少把数据库时间字段类型从
DATETIME改成INT,存时间戳——这样{$create_time|date='Y-m-d'}才真正“开箱即用” - 不要依赖模板自动识别类型:
<date></date>标签虽能容错,但在 TP5.1 中支持不完整,容易漏掉时区或空值处理
最易被忽略的一点:这个报错往往只在开启 APP_DEBUG=true 时明显,生产环境可能静默输出错误时间,直到业务逻辑出错才暴露——别等上线才发现。



















