以世界标准时间保存日期

时间:2020-03-09 11:00:50

标签: symfony symfony4

我目前正在尝试将日期保存为UTC时区。

要获取用户时区,我有一个JS函数,可以像这样向后端发出AJAX请求:

import $ from 'jquery';
import jstz from 'jstz';

export default function setSessionTimezone(route)
{
    var timezone = jstz.determine();
    $.ajax({
        type:'POST', async:true, cache:false, url:route, data:"timezone="+timezone.name(),
        success:function(data) { if (data.reloadPage) location.reload(); }
    });
}

仅当时区尚未处于会话中时才调用此方法。 所以,现在,我在后端设置了用户时区,这是第一步。

我想将其保存在数据库中。 通过这篇SO帖子,我发现了一些有趣的东西:Symfony buildForm convert datetime to local and back to utc

他们建议对表单使用“ model_timezone”和“ view_timezone”,我也是:

use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\TimeType;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class UtcTypeExtension extends AbstractTypeExtension
{
    /**
     * @var SessionInterface
     */
    private $session;

    public function __construct(SessionInterface $session)
    {
        $this->session = $session;
    }

    /**
     * Return the class of the type being extended.
     */
    public static function getExtendedTypes(): iterable
    {
        return [TimeType::class, DateType::class];
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        parent::configureOptions($resolver);

        $resolver->setDefaults([
            'model_timezone' => 'UTC',
            "view_timezone" => $this->session->get('tools_timezone')
        ]);
    }
}

然后呼啦,它起作用了。 但仅限于表格内。

如果要使用Twig或PHP显示日期,则需要从会话中获取时区并更改DateTime的时区。

所以我搜索了另一个选项。 我发现this on the Doctrine Website直接从教义中更改时区。

这听起来很有趣,但是我可能遗漏了一点,因为即使我添加了以下配置,它也似乎不起作用:

doctrine:
    dbal:
        types:
            datetime: SomeNamespace\DoctrineExtensions\DBAL\Types\UTCDateTimeType

所以我想知道我想做什么甚至可以实现?还是如果我被迫覆盖Twig的“日期”过滤器以使用我的时区?而且,如果我想显示PHP中的日期,是否还必须使用会话中的时区?

1 个答案:

答案 0 :(得分:0)

我发现一些东西似乎可以回答我的问题。

我在原则的事件“ postLoad”中添加了一个侦听器。

有关信息,TimezoneProvider只是从会话或UTC(如果未定义)中返回DateTimezone对象。

use DateTime;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Doctrine\ORM\Mapping\Column;
use SomeNamespace\Provider\TimezoneProvider;
use ReflectionClass;
use ReflectionException;
use Symfony\Component\PropertyAccess\PropertyAccess;

class DateTimeConverterListener
{
    /**
     * @var AnnotationReader
     */
    private $reader;

    /**
     * @var TimezoneProvider
     */
    private $timezoneProvider;

    public function __construct(AnnotationReader $reader, TimezoneProvider $timezoneProvider)
    {
        $this->reader = $reader;
        $this->timezoneProvider = $timezoneProvider;
    }

    public function postLoad(LifecycleEventArgs $args)
    {
        $entity = $args->getObject();
        $propertyAccessor = PropertyAccess::createPropertyAccessor();

        try {
            $reflection = new ReflectionClass(get_class($entity));

            //We search the properties where we need to update the Timezone.
            foreach ($reflection->getProperties() as $property) {
                $annotation = $this->reader->getPropertyAnnotation($property, Column::class);
                if (!$annotation instanceof Column)
                    continue;

                switch ($annotation->type) {
                    case "time":
                    case "date":
                    case "datetime":
                        /** @var DateTime|null $attribute */
                        $attribute = $propertyAccessor->getValue($entity, $property->getName());

                        if (null === $attribute)
                            continue 2;

                        //The getTimezone function returns UTC in case of no session information. And because it's a
                        // DateTime object, we don't need to set the value after the modification
                        $attribute->setTimezone($this->timezoneProvider->getTimezone());
                        break;
                }
            }
        } catch (ReflectionException $e) {
            //Abort the transformation
        }
    }
}

要使用树枝过滤器“ | date”正确显示日期,我还通过事件更新了Twig:

use SomeNamespace\Provider\TimezoneProvider;
use Twig\Environment;
use Twig\Extension\CoreExtension;

class SetupTwigTimezoneListener
{
    /**
     * @var TimezoneProvider
     */
    private $timezoneProvider;
    /**
     * @var Environment
     */
    private $twig;

    public function __construct(TimezoneProvider $timezoneProvider, Environment $twig)
    {
        $this->timezoneProvider = $timezoneProvider;
        $this->twig = $twig;
    }

    public function onKernelRequest()
    {
        //Define the timezone of the application based of the timezone of the user
        $this->twig->getExtension(CoreExtension::class)->setTimezone($this->timezoneProvider->getTimezone()->getName());
    }
}

我不太确定这是一个完美的解决方案,但它似乎可行。