分页结果里时间仍是时间戳,是因为paginate()返回Collection而非完整模型实例,获取器不触发;TP6可通过模型$append+getXxxAttr自动格式化,TP5.1需升级并配置with参数。

ThinkPHP 分页本身不处理时间格式转换,所有日期/时间戳转换必须在查询后、分页对象生成前完成,否则分页数据里的日期字段仍是原始值。
为什么分页结果里的时间还是时间戳?
ThinkPHP 的 paginate() 是对查询结果集做切片,它不会主动调用模型的获取器(accessor)或自动转换字段。如果你数据库存的是 int 类型的时间戳(如 create_time),而模型里虽定义了 getCreateTimeAttr,但该获取器仅在模型实例化单条记录时触发——paginate() 返回的是 Collection,内部封装的是原生数组(非完整模型实例),获取器默认不生效。
常见错误现象:var_dump($list) 看到 create_time 仍是 1715823496,而非 "2024-05-16 14:58:16"。
- 确认是否启用了模型的
type转换:在模型中声明protected $type = ['create_time' => 'datetime'];,这会影响select()和paginate()中的类型自动转换(TP6.0+ 有效) - 若用的是 TP5.1,
$type对paginate()无效,必须手动处理 - 避免在模板里用
date()反复格式化——性能差且无法统一时区
在 paginate() 前用 collection->each() 批量转换
最稳妥的做法是查出原始数据后、调用 paginate() 前,先对结果集做一次字段映射。注意:不是对分页对象操作,而是对原始 Query 结果预处理。
立即学习“PHP免费学习笔记(深入)”;
示例(TP6.x):
$data = Db::name('article')
->field('id,title,create_time,update_time')
->order('id desc')
->select(); // 先查出全部(仅用于演示逻辑,实际要配合 limit)
$collection = collect($data)->each(function ($item) {
$item['create_time'] = date('Y-m-d H:i:s', (int)$item['create_time']);
$item['update_time'] = date('Y-m-d', (int)$item['update_time']);
});
// 再转成分页对象(需手动构造 Paginator 实例,或改用模型 + append)
⚠️ 注意:collect() 来自 ThinkPHP 的 think\helper\Str 辅助类(TP6),TP5.1 需用 array_map() 替代;且此方式不适合大数据量,因需先查全表再截取,失去分页意义——所以仅适用于“先查总数+分页偏移”的场景。
用模型的 append + getAttr 统一注入格式化字段
推荐方案:在模型中定义只读字段(append),并在对应 getXxxAttr 方法里做转换。这样无论 select() 还是 paginate(),只要调用 toArray() 或输出为 JSON,都会触发。
示例(Article 模型):
protected $append = ['create_time_formatted', 'update_time_date'];
protected function getCreateTimeFormattedAttr($value, $data)
{
return !empty($data['create_time']) ? date('Y-m-d H:i:s', (int)$data['create_time']) : '';
}
protected function getUpdateTimeDateAttr($value, $data)
{
return !empty($data['update_time']) ? date('Y-m-d', (int)$data['update_time']) : '';
}
控制器中直接用:
$list = ArticleModel::where('status', 1)
->order('id desc')
->paginate(10);
return json($list); // 自动包含 *_formatted 字段
关键点:
-
$append字段名不能与数据库字段重名,否则会覆盖原始值 - TP6 支持在
paginate()中自动识别$append;TP5.1 需升级到 5.1.40+ 并确保开启with参数(paginate(10, false, ['with' => ['append']])) - 如果用了软删除,
delete_time同理可加deleteTimeFormatted
时区与性能容易被忽略的细节
PHP 默认时区影响所有 date()、strtotime() 行为。线上环境常因服务器时区为 UTC,导致格式化结果比本地晚 8 小时。
解决方案:
- 统一在入口文件或
common.php加date_default_timezone_set('Asia/Shanghai'); - 避免在循环里多次调用
date():提前算好时间戳区间,用数据库函数(如 MySQL 的FROM_UNIXTIME())直接查出格式化值(适合只读列表) - 若字段已设为
datetime类型,就别再存时间戳——TP 对datetime字段的$type转换更稳定,也省去手动强转(int)的风险
真正麻烦的不是怎么转,而是忘了时间戳可能为 0、null 或字符串形式混入——每次转换前加 is_numeric() 判断比硬转更安全。



















