如何在 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;
}
另见
我有一个 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;
}