whitecube/laravel-timezones

在数据库中存储UTC日期,并在应用程序中使用自定义时区。

v1.2.2 2024-07-26 10:45 UTC

This package is auto-updated.

Last update: 2024-08-26 10:58:16 UTC


README

处理时区可能会令人沮丧。这里是一个让您的日子变得明亮的一些尝试。

问题:人们普遍认为日期应以UTC日期时间存储在数据库中,这通常意味着它们还需要在处理或显示之前进行本地时区的适配。Laravel提供了app.timezone配置,使得处理时区成为可能。然而,更改该配置将影响存储和处理日期的时区。这个包试图通过提供一个时区转换机制来解决这个问题,该机制应该能够自动执行大多数重复的时区配置。

// Model:
protected $casts = [
    'occurred_at' => TimezonedDatetime::class,
];

// Set a custom timezone
Timezone::set('Europe/Brussels');

// Display dates stored as UTC in the app's timezone:
// (database value: 2022-12-13 09:00:00)
echo $model->occurred_at->format('d.m.Y H:i'); // Output: 13.12.2022 10:00

// Store dates using automatic UTC conversion:
$model->occurred_at = '2022-12-13 20:00:00';
$model->save(); // Database value: 2022-12-13 19:00:00

安装

composer require whitecube/laravel-timezones

入门

app.timezone配置设置必须设置为在数据库中保存日期时应使用的时区。我们强烈建议将其设置为UTC,因为它是日期存储的全球标准。

对于应用程序内的日期处理和显示,人们期望有更多的灵活性。这就是为什么可以通过更新timezone单例实例来动态设置应用程序的时区。根据应用程序的上下文,请选择最适合您情况的选项

1. 使用中间件

当应用程序的时区应该由用户的设置设置时很有用。

namespace App\Http\Middleware;
 
use Closure;
use Whitecube\LaravelTimezones\Facades\Timezone;
 
class DefineApplicationTimezone
{
    public function handle($request, Closure $next)
    {
        Timezone::set($request->user()->timezone ?? 'Europe/Brussels');
 
        return $next($request);
    }
}

2. 使用服务提供者

当应用程序的时区应该由应用程序本身设置时很有用。例如,在App\Providers\AppServiceProvider

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Whitecube\LaravelTimezones\Facades\Timezone;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Timezone::set('America/Toronto');
    }
}

用法

一切设置完毕后,处理配置为应用程序当前时区的日期的最简单方法是使用模型上的TimezonedDatetimeImmutableTimezonedDatetime类型。

use Whitecube\LaravelTimezones\Casts\TimezonedDatetime;
use Whitecube\LaravelTimezones\Casts\ImmutableTimezonedDatetime;

/**
 * The attributes that should be cast.
 *
 * @var array
 */
protected $casts = [
    'published_at' => TimezonedDatetime::class,
    'birthday' => ImmutableTimezonedDatetime::class . ':Y-m-d',
];

在其他场景中,请随时直接使用Timezone外观来获得更多便利。

use Carbon\Carbon;
use Whitecube\LaravelTimezones\Facades\Timezone;

// Get the current date configured with the current timezone:
$now = Timezone::now();

// Create a date using the current timezone:
$date = Timezone::date('2023-01-01 00:00:00');
// Alternatively, set the timezone manually on a Carbon instance:
$date = new Carbon('2023-01-01 00:00:00', Timezone::current());


// Convert a date to the current timezone:
$date = Timezone::date(new Carbon('2023-01-01 00:00:00', 'UTC'));
// Alternatively, set the application timezone yourself:
$date = (new Carbon('2023-01-01 00:00:00', 'UTC'))->setTimezone(Timezone::current());

// Convert a date to the storage timezone:
$date = Timezone::store(new Carbon('2023-01-01 00:00:00', 'Europe/Brussels'));
// Alternatively, set the storage timezone yourself:
$date = (new Carbon('2023-01-01 00:00:00', 'Europe/Brussels'))->setTimezone(Timezone::storage());

分配值到转换属性

许多开发者习惯于将Carbon实例分配给日期属性

$model->published_at = Carbon::create($request->published_at);

这可能会导致意外的行为,因为分配的Carbon实例将默认为UTC时区,而提供的值可能是指另一个时区。日期时间字符串将原样存储,而不会相应地调整其时区。

为了防止这种情况,建议让转换完成大部分工作

$model->published_at = $request->published_at;

包现在将使用正确的时区(例如,Europe/Brussels)处理提供的日期时间字符串,并将正确的UTC值存储在数据库中。

一个更详细的(但也是正确的)方法是通过Timezone外观创建Carbon实例

$model->published_at = Carbon::create($request->published_at, Timezone::current());
// Or, shorthand:
$model->published_at = Timezone::date($request->published_at);

这不是一个错误,这是预期行为,因为应该在分配之前完全了解Carbon实例的时区。

边缘情况

如果您需要在默认时间戳列(created_at和/或updated_at)上使用TimezonedDatetimeImmutableTimezonedDatetime转换,并且您期望处理除UTC或您使用Timezone::set()定义的时区之外的时区,则需要在您的模型上应用Whitecube\LaravelTimezones\Concerns\HasTimezonedTimestamps特质。

这是必要的,以防止Laravel对这些属性进行转换,这将导致丢失时区信息,阻止我们的转换正常工作。

这是一个需要使用特质的示例

Timezone::set('Europe/Brussels');

$model->created_at = new Carbon('2022-12-15 09:00:00', 'Asia/Taipei');

🔥 赞助

如果您在生产应用程序中依赖于此包,请考虑赞助我们!这是帮助我们继续做我们热爱的事情:制作优秀的开源软件的最好方式。

贡献

请随意提出更改建议,请求新功能或自行修复错误。我们相信还有很多可以改进的地方,我们非常愿意合并有用的拉取请求。谢谢!

用❤️为开源制作

在Whitecube(https://www.whitecube.be)我们使用许多开源软件作为我们日常工作的部分。所以当我们有机会回馈的时候,我们非常兴奋!

希望您会喜欢我们从我们这里的小贡献,如果您在项目中发现它有用,我们非常乐意听到您的反馈。关注我们的Twitter获取更多更新!