laravel 多时区处理困扰:探索多种方案仍无法彻底解决 | laravel | laravel china 社区-380玩彩网官网入口
laravel 作为一个优雅的框架,开发体验令人愉悦。然而,多时区处理却屡屡让我感到困惑和头疼。我花了好几天尝试各种方案,但问题仍未完全解决。
有没有人遇到多时区处理的需求呢?我搜索了整个中文互联网社区,几乎没有找到相关讨论(包括 380玩彩网官网入口 本身也没有实现多时区)。一开始,我以为是自己对 laravel 的时区机制理解不足,经过反复研究,我确认问题确实存在于 laravel 的实现方式上。
我的需求是:
- 数据库中的
created_at
等字段要始终以 utc 存储。 - 用户界面中则以 asia/shanghai(上海时间)显示。
方案一:修改 config/app.php
中的时区
我将 config/app.php
文件中的 timezone
修改为 asia/shanghai
,但结果发现:数据库中新建记录的 created_at
字段也变成了上海时间,这违背了我希望数据库字段使用 utc 的初衷。
结果:方案失败
方案二:保留默认 utc 时区,界面手动转换
我将 config/app.php
中的 timezone
保持为默认的 utc。然后,在用户界面中使用:
{{ $post->created_at->settimezone($client_timezone) }}
这样,created_at
可以成功按用户时区显示。但是问题出现在用户手动输入的字段 expired_at
上。用户在表单中输入的时间,如 2025-01-01 10:30:00(上海时间),laravel 会原样存入数据库。这不符合我的需求,我希望存储成 utc 时间。
结果:方案失败
方案三:模型中使用 saving
钩子自动转换
基于方案二,我在模型中添加了以下代码,尝试在保存时将用户输入的时间转换为 utc:
protected $casts = [
'edit_at' => 'datetime',
'expired_at' => 'datetime',
];
protected static function booted()
{
static::saving(function ($model) {
// 遍历所有声明为 datetime 类型的字段
foreach ($model->getcasts() as $field => $type) {
if ($type === 'datetime' && $model->$field) {
$v = carbon::createfromformat(
'y-m-d h:i:s',
$model->$field->todatetimestring(),
'asia/shanghai'
)->utc();
$model->$field = $v;
}
}
});
}
在这种方案下,用户输入的 expired_at
能够成功存储为 utc 时间,但新的问题出现了:
edit_at
字段保存的是now()
的时间。然而,由于now()
默认是 utc 时间,saving
钩子会将它错误地当作上海时间处理并再次转换为 utc,导致数据库存储的时间比真实 utc 时间慢了 8 小时。
结果:方案失败
附言
// 在 `app/providers/appserviceprovider.php` 中执行 `config(['app.timezone' => $client_timezone]);` 可以让 filament admin 的数据表格和表单中的日期时间字段正确显示为本地时区的时间。不会影响入库的日期时间字段以及 blade 模板中的 `{{ $post->some_at }}` 其仍然会以 `config/app.php` 中的时区为准。要在 blade 模板中展示用户时区的时间,需要手动转换,比如:`{{ $post->some_at->settimezone($client_timezone) }}`
config(['app.timezone' => $client_timezone]);
另:laravel vs. django 的多时区方案
django 的多时区处理更为优雅。无论 mysite/settings.py
中设置的时区是什么,django 都会始终以 utc 存储 datetime 字段,并且在 orm 读取时自动转换为设置时区的时间。这种设计极大地减少了开发中的时间转换问题。
但是 laravel 无法做到这样,希望 laravel 能尽早完善对多时区的处理方案。
希望能在这里获得大家的建议和帮助,谢谢!
推荐文章: