Laravel 中的用户时区问题

User timezones issue in Laravel

我目前以 UTC 格式存储所有日期。当用户登录时,它使用 moment.js(moment.tz.guess() 函数)用他们的时区名称填充隐藏的时区输入,例如America/Toronto,然后当他们登录时,它会使用所述时区更新他们在数据库中的记录。

我创建了一个访问器特征,以便显示用户时区中的所有日期。请参阅此代码:

trait Timezone
{
    public function getCreatedAtAttribute($value)
    {
        return $this->timezone($value);
    }

    public function getUpdatedAtAttribute($value)
    {
        return $this->timezone($value);
    }

    public function getDeletedAtAttribute($value)
    {
        return $this->timezone($value);
    }

    // convert date to user timezone
    public function timezone($value)
    {
        $carbon = Carbon::parse($value);

        if (auth()->check()) {
            $carbon->tz(auth()->user()->timezone);
        }

        return $carbon->toDateTimeString();
    }
}

现在我运行遇到了一个小问题。我在应用程序中有一个报告功能,它有一个下拉菜单,用户可以在其中 select 日期范围,例如 This WeekLast Week

我将使用UTC来查询结果,然后将其转换为用户时区。问题是,对于某些用户来说,根据一天中的时间,一周显示为从周二到周一,或周日到周六,而不是周一到周日。

这是我的代码:

public static function dateStartEnd($date_range)
{
    if ($date_range == 'Today') {
        $start_date = Carbon::now()->startOfDay();
        $end_date = Carbon::now()->endOfDay();
    }
    else if ($date_range == 'Yesterday') {
        $start_date = Carbon::now()->subDay()->startOfDay();
        $end_date = Carbon::now()->subDay()->endOfDay();
    }
    else if ($date_range == 'This Week') {
        $start_date = Carbon::now()->startOfWeek();
        $end_date = Carbon::now()->endOfWeek();
    }
    else if ($date_range == 'Last Week') {
        $start_date = Carbon::now()->subWeek()->startOfWeek();
        $end_date = Carbon::now()->subWeek()->endOfWeek();
    }
    else if ($date_range == 'This Month') {
        $start_date = Carbon::now()->startOfMonth();
        $end_date = Carbon::now()->endOfMonth();
    }
    else if ($date_range == 'Last Month') {
        $start_date = Carbon::now()->subMonth()->startOfMonth();
        $end_date = Carbon::now()->subMonth()->endOfMonth();
    }
    else {
        // All Time
        if ($lead = Lead::orderBy('created_at', 'asc')->first()) {
            $start_date = Carbon::parse($lead->created_at);
        }
        else {
            $start_date = Carbon::now()->startOfDay();
        }

        $end_date = Carbon::now()->endOfDay();
    }

    return [
        'start_date' => $start_date,
        'end_date' => $end_date,
    ];
}

我想知道如何正确执行此操作,以便它使用 UTC 查询结果,但在用户时区显示星期一到星期日,无论用户时区与 UTC 相比如何。

您可以使用Laravel 辅助函数配置来设置时区。但是,这只会影响您将收到的请求。

config(['app.timezone' => $timezone]);

如果您的目标是在每次请求时更改一次时区和 运行 那么如何将更改后的时区保存在数据库或文件中呢?然后,在app/config.php中写一个DB查询或读取一个文件,并在一个文件中更改index timezone的值。

参考::

我最终为 localutc 开始和结束日期创建了单独的 return 属性。

这是我的代码现在的样子:

public static function dateStartEnd($date_range, $date_from, $date_to)
{
    if ($date_range == 'Today') {
        $start_local = Carbon::now(auth()->user()->timezone)->startOfDay();
        $end_local = Carbon::now(auth()->user()->timezone)->endOfDay();
    }
    else if ($date_range == 'Yesterday') {
        $start_local = Carbon::now(auth()->user()->timezone)->subDay()->startOfDay();
        $end_local = Carbon::now(auth()->user()->timezone)->subDay()->endOfDay();
    }
    else if ($date_range == 'This Week') {
        $start_local = Carbon::now(auth()->user()->timezone)->startOfWeek();
        $end_local = Carbon::now(auth()->user()->timezone)->endOfWeek();
    }
    else if ($date_range == 'Last Week') {
        $start_local = Carbon::now(auth()->user()->timezone)->subWeek()->startOfWeek();
        $end_local = Carbon::now(auth()->user()->timezone)->subWeek()->endOfWeek();
    }
    else if ($date_range == 'This Month') {
        $start_local = Carbon::now(auth()->user()->timezone)->startOfMonth();
        $end_local = Carbon::now(auth()->user()->timezone)->endOfMonth();
    }
    else if ($date_range == 'Last Month') {
        $start_local = Carbon::now(auth()->user()->timezone)->subMonth()->startOfMonth();
        $end_local = Carbon::now(auth()->user()->timezone)->subMonth()->endOfMonth();
    }
    else if ($date_range == 'Custom') {
        $start_local = Carbon::parse($date_from, auth()->user()->timezone)->startOfDay();
        $end_local = Carbon::parse($date_to, auth()->user()->timezone)->endOfDay();
    }
    else {
        // All Time
        if ($lead = Lead::orderBy('created_at', 'asc')->first()) {
            $start_local = Carbon::parse($lead->created_at, auth()->user()->timezone);
        }
        else {
            $start_local = Carbon::now(auth()->user()->timezone)->startOfDay();
        }

        $end_local = Carbon::now(auth()->user()->timezone)->endOfDay();
    }

    return [
        'start_local' => $start_local,
        'start_utc' => $start_local->copy()->timezone(config('app.timezone')),
        'end_local' => $end_local,
        'end_utc' => $end_local->copy()->timezone(config('app.timezone')),
    ];
}