laravel - 当时区与 UTC 不同时如何存储日期时间(Laravel)

标签 laravel php-carbon

我的应用时区设置为“美国/蒙特利尔”。
我有两个日期时间字段“开始”和“结束”,每个字段都使用 laravel $casts 属性转换为日期时间:

protected $casts = [
    'start' => 'datetime',
    'end' => 'datetime'
];

当我使用以下数据创建我的模型实例时:

MyModel::create(
                [
                    'start' => "2022-02-08T20:45:58.000Z", // UTC time  
                    'end' => "2022-02-08T20:45:58.000Z",
                ]
            );

创建的模型保持相同的时间 (20:45),但时区设置为美国/蒙特利尔:

 App\MyModel {#4799
     id: 44,
     created_at: "2022-02-08 15:49:02",
     updated_at: "2022-02-08 15:49:02",
     start: 2022-02-08 20:45:58,
     end: 2022-02-08 20:45:58,
   }

当我访问开始和结束属性时,我得到的时间是相同的,但美国/蒙特利尔时区是这样的:

// accessing 'start' attribute of the instance I just created
Illuminate\Support\Carbon @1644371158 {#4708
 date: 2022-02-08 20:45:58.0 America/Montreal (-05:00),

我发现让它正常工作的唯一方法是在保存之前手动设置时区:

    MyModel::create(
                [
                    'start' => Carbon::parse("2022-02-08T20:45:58.000Z")->setTimeZone(config('app.timezone')),, 
                    'end' => Carbon::parse("2022-02-08T20:45:58.000Z")->setTimeZone(config('app.timezone')),,
                ]
            );  

我认为这是重复的,设置应用程序时区还不够吗?有没有更好的方法来做到这一点?我知道我应该将我的应用程序时区设置为 UTC(这是我通常所做的)但是这个项目已经有很多这个时区的数据,我不确定如何转换它。
谢谢。

最佳答案

鉴于 laravel 只会将日期存储在执行 $date->format('Y-m-d H:i:s') 的模型中,该模型将其格式化为当前时区,但不保留时区信息,然后检索它,就好像它在应用程序时区(大部分时间是 utc)中一样,这会产生差异,因为如果您设置的值有与应用不同的时区

基本上就是这样

Carbon\Carbon::parse(Carbon\Carbon::parse('2022-11-08 00:00', 'America/Montreal')->format('Y-m-d H:i:s'), 'UTC');

Carbon\Carbon @1667865600 {#4115
   date: 2022-11-08 00:00:00.0 UTC (+00:00), // As you can see it is UTC,
  // which is ok because the database does not store the timezone information,
  // but the time is 2022-11-08 00:00 and should be 2022-11-08 05:00:00 in UTC
}

// This would yield the correct result
Carbon\Carbon::parse(Carbon\Carbon::parse('2022-11-08 00:00', 'America/Montreal')->setTimezone('UTC')->format('Y-m-d H:i:s'), 'UTC');

您可以创建自己的模型扩展来覆盖 setAttribute 方法并从此类扩展而不是将所有日期自动转换为您的应用时区

<?php

namespace App;

use DateTimeInterface;
use Carbon\CarbonInterface;
use Illuminate\Database\Eloquent\Model as BaseModel;

class Model extends BaseModel
{

    /**
     * Set a given attribute on the model.
     *
     * @param  string  $key
     * @param  mixed  $value
     * @return mixed
     */
    public function setAttribute($key, $value)
    {
        if ($value instanceof CarbonInterface) {
            // Convert all carbon dates to app timezone
            $value = $value->clone()->setTimezone(config('app.timezone'));
        } else if ($value instanceof DateTimeInterface) {
            // Convert all other dates to timestamps
            $value = $value->unix();
        }
        // They will be reconverted to a Carbon instance but with the correct timezone
        return parent::setAttribute($key, $value);
    }
}

关于laravel - 当时区与 UTC 不同时如何存储日期时间(Laravel),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71040870/

相关文章:

php - 两个日期之间的碳差以获得小时费率计算的小数

PHP Carbon diffInMonths

php - 将数据传递给 Laravel 事件

php - 使用 phpunit 进行 Laravel 测试

php - 在 laravel 框架中使用 native PHP 类

Laravel 6 Passport CSRF token 不匹配

laravel - Laravel 中的 Carbon 日期格式

laravel - Chrome 将 .dev 重定向到 https

php - Laravel 5 Carbon 格式

php - Carbon formatLocalized 在 Blade 中不起作用