【问题标题】:Doctrine: date timezone not preserved after creating an entity教义:创建实体后不保留日期时区
【发布时间】:2019-01-26 09:50:03
【问题描述】:

我创建了一个实体,其日期时间设置为 08:50:35,时区 "+00:00"。我重新加载实体,日期时间为 08:50:35,时区为 "+02:00"时区已更改!

将 Symfony 3.4 与 Doctrine、PHP 7.2、MySQL 5.7 和 Ubuntu 16.4 或 Debian 8 结合使用。

我使用了一个外部库,它使用来自时间戳指令的 DateTime::createFromFormat。所以保留它会很好。

这是我将问题简化为的代码:

// Create entity with date.
$report = new CronReport();
$date = \DateTime::createFromFormat('U', '1534755035');
var_dump($date); // object(DateTime)#536 (3) {
    // ["date"]=> string(26) "2018-08-20 08:50:35.000000" 
    // ["timezone_type"]=> int(1)
    // ["timezone"]=> string(6) "+00:00" }

$report->setRunAt($date);
$report->setRunTime(0);
$report->setExitCode(0);
$report->setOutput('');
$this->em->persist($report);
$this->em->flush();

// Reload Entity.
$id = $report->getId();
$this->em->detach($report);
$report = this->em->getRepository('CronCronBundle:CronReport')->find($id);

$date = $report->getRunAt();
var_dump($date); // object(DateTime)#550 (3) {
    // ["date"]=> string(26) "2018-08-20 08:50:35.000000"
    // ["timezone_type"]=> int(3)
    // ["timezone"]=> string(12) "Europe/Paris" }

建表声明:

CREATE TABLE `cron_report` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `job_id` int(11) DEFAULT NULL,
  `run_at` datetime NOT NULL,
  `run_time` double NOT NULL,
  `exit_code` int(11) NOT NULL,
  `output` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
  PRIMARY KEY (`id`),
  KEY `IDX_B6C6A7F5BE04EA9` (`job_id`),
  CONSTRAINT `FK_B6C6A7F5BE04EA9` FOREIGN KEY (`job_id`) REFERENCES `cron_job` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

怎么了?如何设置实体日期时间并保留其时区?

【问题讨论】:

  • 我认为这可能是由于mysql 自己的timezone settings。从本质上讲,如果您将日期时间值存储为字符串,则不会发生这种情况,但由于您没有,因此您必须进行设置。

标签: php mysql symfony doctrine timezone


【解决方案1】:

问题似乎是您的 PHP 时区设置为与UTC 不同的值,即Europe/Paris。这可以在您的phi.ini 中配置,也可以在运行时使用date_default_timezone_set("Europe/Paris")ini_set("date.timezone", "Europe/Paris") 设置。您可以通过运行 php -i | grep timezone(用于 CLI)或在运行时通过 var_dump(ini_get("date.timezone")) 检查当前值。

使用 Doctrine 和 MySQL 处理 DateTime 对象是a topic of it’s own。简而言之,您的数据库并不关心存储在 datetime 列中的值的时区。当您将 DateTime 对象传递给 Doctrine 时,它​​将在数据库中存储 time 而不是 timezone。当 Doctrine 再次检索该值时,它会使用数据库中的值创建 DateTime 对象,但在您的 PHP 的默认时区中。

有几种解决方案:

  1. 您可以考虑使用column type with timezone supportdatetimetz。请注意,这不适用于 MySQL 作为底层数据库。

  2. 如果您的应用中只有一个时区,请将其设置为 PHP 中的默认时区,并在持久化之前将所有 DateTime 对象转换为该时区。

  3. 如果您有多个时区,则必须将时区与datetime 值一起存储在数据库中。在这种情况下,您应该将内部时区设置为 UTC,并在持久化之前将 DateTime 对象转换为 UTC。然后,在从实体中检索 DateTime 时,您必须再次设置正确的时区。或者,您可能会发现直接将时间存储为 UNIX 时间戳(根据定义为 UTC)并动态创建 DateTime 实例更容易。取决于您的用例。

阅读 Doctrine 团队撰写的有关 DateTime 处理的文章,了解更多关于利弊的详细讨论:https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/cookbook/working-with-datetime.html#handling-different-timezones-with-the-datetime-type

【讨论】:

  • 你是对的,从一个php日期对象,系统只存储时间而不关心时区。幸运的是,我只使用一个时区“欧洲/巴黎”。最后我在创建对象之前添加了这个:$tz = new \DateTimeZone('Europe/Paris'); $date->setTimezone($tz); 不是很好,但工作已经完成。谢谢。
  • 是的,但无论如何都没有完美的解决方案。所以你只需要选择最不丑的一个。 :) 感谢您的支持/接受!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-03
  • 1970-01-01
  • 2013-09-11
  • 1970-01-01
  • 2013-05-23
相关资源
最近更新 更多