如何在 CakePHP 中使用时区保留 DatetTime

How to persist DatetTime with time zone in CakePHP

我有一个 MySQL 使用 UTC 时区并尝试将 CakePHP 实体保存到数据库中的 datettime 列。

当 PHP DatetTime 对象具有时区(不同于 UTC)时,CakePHP 在保存期间不会将值转换为 UTC。

我写了一个简短的测试来演示这个问题。

public function testTryCreateReservation(): void
{
    $e = $this->Reservations->newEmptyEntity();
    $now = new \DateTime();
    $start = new \DateTime('now', new \DateTimeZone('Europe/Vienna')); 
    $start->setTimestamp($now->getTimestamp());
    $end = new \DateTime('now'); 
    $end->setTimestamp($now->getTimestamp());
    $e->start_time = $start;

    $a = $this->Reservations->get($a->id);
    $this->assertEquals($end, $a->end_time);
    $this->assertEquals($start, $a->start_time); // this fails
}

这是 phpunit 输出

Failed asserting that two DateTime objects are equal.
--- Expected
+++ Actual
@@ @@
-2020-09-02T07:54:45.000000+0200
+2020-09-02T07:54:45.000000+0000

CakePHP 不应该转换值吗?

启用时区转换

ORM 将在列为 datetime 类型时转换时区,并且您在相应的数据库类型实例 (\Cake\Database\Type\DateTimeType) 上配置数据库时区。

您可以在 bootstrap.php 中执行此操作,底部有一个部分展示了如何构建和配置数据库类型:

\Cake\Database\TypeFactory::build('datetime')->setDatabaseTimezone('UTC');

还应注意,这也将启用相反的转换,即从数据库时区到应用程序的默认时区 (App.defaultTimezone)!如果你不想这样,你可以通过 setKeepDatabaseTimezone() 方法禁用它:

\Cake\Database\TypeFactory::build('datetime')->setKeepDatabaseTimezone(true);

然后将使用(转换为)为数据库类型配置的时区创建对象。

我想如果在 Cookbook 中的某处有一个关于数据库时区转换的专门部分不会有什么坏处。

仅将时区转换应用于特定列

请注意,您还可以映射一个新类型并将其分配给特定的 table 列,以防您不希望它全局应用:

// in your bootstrap.php
\Cake\Database\TypeFactory::map('customDateTime', \Cake\Database\Type\DateTimeType::class);
\Cake\Database\TypeFactory::build('customDateTime')->setDatabaseTimezone('UTC');

// in the table class where you want this type to apply to a specific column
protected function _initializeSchema(\Cake\Database\Schema\TableSchemaInterface $schema): \Cake\Database\Schema\TableSchemaInterface
{
    $schema->setColumnType('column_name', 'customDateTime');

    return $schema;
}

另见