Laravel Eloquent 日期系列化

Laravel Eloquent 日期系列化成 json,默认系列化格式为:2023-03-08T08:16:02.000000Z
原因是 Laravel 模型基类的 serializeDate() 时间系列化方法调用 Carbon\Traits\Converter::toJSON() 方法,返回的是 ISO-8601 格式的日期。

引起问题的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Illuminate\Database\Eloquent\Concerns\HasAttributes
protected function serializeDate(DateTimeInterface $date)
{
return $date instanceof DateTimeImmutable ?
CarbonImmutable::instance($date)->toJSON() :
Carbon::instance($date)->toJSON();
}

// class Carbon\Traits\Converter
public function toJSON()
{
return $this->toISOString();
}

解决办法是在模型中覆盖 serializeDate 方法,返回 Carbon\Traits\Converter::toDateTimeString() 方法就可以。

如代码,推荐把 serializeDate 方法放到一个 trait 中,在模型类中再 usetrait

1
2
3
4
5
6
7
8
9
10
11
namespace App\Models\traits;

use Illuminate\Support\Carbon;

trait SerializeDate
{
protected function serializeDate(\DateTimeInterface $date): string
{
return Carbon::instance($date)->toDateTimeString();
}
}

添加 casts ‘datetime:Y-m-d H:i:s’ 来解决这个问题是不正确的,因为这样格式化的是转换后的 iso 标准时间,我国时间会有8小时误差。我们用覆盖 serializeDate 方法的方式,不管是用 utc 时间还是本地时间,都不会有问题。
首先你要先设置 Laravel 的时区,在 config/app.php 中把 timezone 设为 PRCAsia/Shanghai,默认是 UTC 时间。

更好的解决方案

如果你的系统100%是给国人用,用上面的方法是没问题的。如果老外也用,你的 Laravel 应该使用 UTC 时间,显示的时候再转换成本地时间。

用js格式化日期

后的返回UTC时间,前端用 JS 格式化日期

1
new Date('2023-03-08T08:16:02.000000Z').toLocaleString()

后端日期转换

如果你不是用前端显示js,而是在 blade 中使用模板视图,可通过前端获取客户端时差,用 cookie 传到服务器。

1
2
<script>
document.cookie = "tzo="+ (- new Date().getTimezoneOffset()/60)

服务器端获取时差,再设置给 Carbon。

1
$user->created_at->addHours($_COOKIE['tzo'])->rawFormat('Y-m-d H:i:s');
作者

CoderPan

发布于

2023-03-08

更新于

2024-02-08

许可协议

评论