Symfony 1.4/Doctrine:将datetimes存储为UTC,但在生成的admin中显示所有时间为东部时间


Symfony 1.4/Doctrine: store datetimes as UTC, but show all times in generated admin as Eastern Time

我们有一个生成管理应用程序的Symfony项目,我们需要datetime字段显示为东部时间。现在,Symfony被设置为以UTC方式处理所有datetime值。这是经过设计的,因为钩入这个项目的各种其他东西处理UTC日期时间。我们只需要管理员显示和接受datetime输入,就像用户在东部时区一样。

下面是我们为实现这个目标所做的一些事情:

  1. 更改应用程序设置中的default_timezone设置。到"美国/纽约"。首先,它似乎实际上并没有改变datetime字段在admin中显示的方式;它似乎只设置date_default_timezone_set()。其次,我们实际上需要PHP处理UTC的时间,因为我们的一些辅助函数被编写为使用UTC。最后,我们需要将日期时间作为UTC存储在MySQL数据库中,并且因为我们已经将它们全部定义为DATETIME列(而不是时间戳),所以据我所知,它们是无时区的。(虽然我可能错了)

  2. 修改相关的管理表单对象,以便在保存/编辑对象时将日期时间从ET转换为UTC,并创建自定义小部件,以便在编辑表单中预填充相同的值时进行相反的转换。所以这对我们来说是有效的,但是这里有一些重要的问题。首先,我们必须创建自定义小部件;不过,这也没什么大不了的。我们还必须为包含datetime字段的每个类修改管理表单。这有点麻烦。我们还必须向受影响的类添加辅助函数,因为当管理表单将显示正确的值时,如果任何这些日期时间列出现在管理列表中,它们仍然会显示为UTC时间而不是ET时间。然后我们还需要扩展Timestampable行为,因为那些也需要在ET中显示/在UTC中存储。这是一整个大量的工作,只是为了让管理员在ET中显示时间,这不仅是脆弱的,而且如果我们引入带有日期时间字段的新类(或将日期时间字段添加到当前类),还需要添加。

  3. 覆盖受影响类上的save()和load()函数。我们尝试将从admin表单获得的ET日期时间字符串转换为UTC,并使用内置的setter函数更改对象,然后调用父类save()。这很有效。然而,试图反过来对load()函数做同样的事情,却失败了。调用父类load()函数是可以的,但是使用内置setter将时间从UTC更改回ET失败。结果是load()函数实际上根本没有执行,就我们所知;当我们试图为现有对象加载表单时,Var_dump()和exit()命令不会执行。我们仍然不知道为什么这不起作用。

  4. 覆盖受影响类上的preSave()和postLoad()函数。我们遇到了与#4相同的问题:preSave()工作正常,但postLoad()失败。

  5. 在受影响的类上重写preSave()函数,然后重写类中datetime字段的相关getter函数。这是有效的,但只有当我们通过访问对象的内部$_data数组而不是使用getter来获取原始datetime值时。我们需要这样做,因为显然调用任何setter最终也会调用字段的getter,导致我们应用太多的时区转换。所以这是有效的,但是访问$_data数组感觉像是一个巨大的hack,乞求被打破。

除此之外,我们只有尚未研究过的理论方法。例如,我们可以在Symfony的某个地方修改datetime字段本身的定义,无论是在Doctrine还是在其他地方,但我们想不出一个不需要深入挖掘Doctrine核心的地方来做这件事。

在这一点上,我们有点卡住了。所有这一切似乎只是为了让管理员将日期时间显示为ET并将ET日期时间保存为数据库中的UTC,所以我们一定是做错了什么——我们只是不知道我们应该做什么。

我们基本上完成了(2)-数据库和系统时区是UTC,这是正确和自然的,我们已经有了自定义的时区感知日期时间小部件& &;验证器。

我们所做的另一件事是在我们的学说模型基类中添加一个$model->getDateTimeObject($fieldName)方法(参见http://oldforum.symfony-project.org/index.php/m/96066/),它将返回一个具有相关DateTimeZone集的DateTime

,

abstract class ourDoctrineRecord extends sfDoctrineRecord
{
  /**
   * @param string $fieldName
   * @return DateTime
   */
  public function getDateTimeObject($fieldName)
  {
    // chose a relevant timezone based on user/app etc, or throw
  }
}

问题是在Doctrine中,当它使用getter时,它会在默认的symphony时区上获得新的datetime,因此必须通过向ORM添加自定义datetime类型来更改,类似于本教程;你可以用它来设置和获取UTC

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/cookbook/working-with-datetime.html