A necessidade de localização temporária do produto surge quando o produto cresce em uma escala que requer trabalho em diferentes fusos horários (evidência). Eu gostaria de descrever uma variante de uma idéia simples para resolver este caso.O pano de fundo é o seguinte: desenvolvemos um sistema de CRM / ERP de nicho e nos disseram que amanhã eles trabalhariam com esse sistema em uma franquia de Vladivostok a Kaliningrado. Infelizmente, esse cenário não foi originalmente pensado e começamos a aprender como fazer isso com custo mínimo e conveniência máxima.No total, três tarefas acabaram sendo ampliadas: como produzimos os dados e como entramos, e entre elas armazenamos a tarefa. Como o tempo, como você sabe, é relativamente literal e figurativamente, foi decidido armazenar o tempo como antes no UTC + 3 de Moscou, mas processá-lo na entrada e na saída (e lembre-se de que o ponto de referência é UTC + 3). Obviamente, entendemos que existem outras soluções nesta e em outras direções. Você pode converter todas as entradas existentes para UTC + -0, além de usar tipos especializados no DBMS que armazenam o fuso horário, você mesmo pode escrever esse tipo personalizado, se de repente o banco de dados não suportar totalmente esses recursos. Mas guiados pelo princípio da simplicidade, seguimos o caminho proposto, ainda mais, à primeira vista, ele não perdeu muito para o resto,e a lógica para determinar o fuso horário desejado era bastante simples.Depois que Moscou se tornou um ponto de referência, adicionamos um parâmetro de fuso horário personalizado a cada usuário, além de várias entidades relacionadas (organização, cidade, aplicativos, transações, etc.). Em seguida, foi possível estabelecer de forma inequívoca em qual fuso horário o usuário ou entidade com a qual ele trabalha. A lógica é padrão e, com frequência, específica para os projetos. Envolvemos essa lógica no serviço e obtivemos um fuso horário no qual você precisa$localizationService->getTimezone();
A decisão de localizar datas nos modelos foi a seguinte: ao inicializar as extensões do Twig, o fuso horário foi alterado para o desejado:function __construct(Environment $twig, LocalizationService $localizationService) {
$twig->getExtension('Twig_Extension_Core')->setTimezone($localizationService->getTimezone());
}
Nossa situação ficou ainda mais complicada pelo fato de que, após a exibição de qualquer data e hora, é necessário criar um subscrito "01.01.2020 12:30 (Moscou)", para que, por exemplo, em uma ordem / tarefa / transação condicional vinculada ao fuso horário, informações sobre a hora cinto. Por motivos práticos, isso é necessário para que um único call center possa trabalhar confortavelmente com diferentes fusos horários na estrutura de uma tarefa / aplicativo / transação.Toda a lógica para determinar a prioridade dos fusos horários foi conectada ao getTimezone acima mencionado.Além disso, fomos confrontados com o fato de que, se você criar seu filtro ou função de galho, precisará alterar vários modelos, mas deseja evitar isso. Portanto, após algumas perguntas, decidimos redefinir a data padrão do filtro de galho...
new TwigFilter('date', [$this, 'date'], ['needs_environment' => true]),
...
function date(Twig_Environment $env, $date, $format = null, $timezone = null)
{
$appendix = '';
if (format && strpos($format, 'H:i') !== false)
$appendix = ' ('.DateTimeFunctions::getRussianAbbrev($this->localizationService->getTimezone()).')';
...
...
return $result.$appendix;
}
Além disso, desde que adotamos o filtro padrão, a versão antiga foi redefinida:...
new TwigFilter('native_date', [$this, 'nativeDate'], [ 'needs_environment' => true]),
...
public function nativeDate(Twig_Environment $env, $date, $format = null, $timezone = null)
{
}
O código de filtro de data padrão pode ser encontrado em / twig / twig / lib / Twig / Extension / Core :: twig_date_format_filter. Embora, na verdade, na maioria dos casos, uma opção simples, não muito diferente, faça:$date->setTimeZone($timezone)
$result = $date->format($format);
Obviamente, você também pode bifurcar ou redefinir a parte mais substancial do Twig, mas se a funcionalidade do filtro padrão for adequada para você, basta retirá-lo separadamente e não perder nada.Resta resolver o problema de inserir a data e hora. Uma solução:private function getOffsetHours()
{
if (!$this->isInit)
$this->init();
$local = new \DateTime('now', new \DateTimeZone($this->getTimezone()));
$user = new \DateTime('now');
$localOffset = $local->getOffset() / 3600;
$globalOffset = $user->getOffset() / 3600;
$diff = $globalOffset - $localOffset;
return $diff;
}
public function toGlobalTime(\DateTimeInterface $dateTime): \DateTimeInterface
{
if (!$this->isInit)
$this->init();
$offsetHours = $this->getOffsetHours();
if ($offsetHours > 0) {
return $dateTime->modify('+ '.$offsetHours.' hours');
} else if($offsetHours < 0){
return $dateTime->modify($offsetHours.' hours');
}
return $dateTime;
}
Em seguida, ligue antes de salvar a data e a hora, por exemplo, nos ouvintes. Essa opção veio até nós, já que basicamente a hora e a data no projeto são fixadas para determinados eventos e não inseridas manualmente. Para outro caso extremo, onde o tempo é constantemente introduzido através de formulários, a solução pode não ser ótima.Como um bônus. O projeto usa o Omines datatables-bundle para gerar tabelas. Lá a solução foi ainda mais fácil. Em vez de DateTimeColumn para localização, foi utilizado o seguinte:class CustomDateTimeColumn extends DateTimeColumn
{
private $localizationService;
private $timeZone;
public function __construct(LocalizationService $localizationService)
{
$this->localizationService = $localizationService;
$this->timeZone = $localizationService->getTimezoneObject();
}
public function normalize($value)
{
$value->setTimeZone($this->timeZone);
return parent::normalize($value);
}
}
Obrigado pelo seu tempo. Se alguém ajudar a melhorar as coisas básicas da solução, ficarei muito agradecido. Estamos falando de códigos básicos, pois é claro que o código é vazio e, na realidade, possui uma DI muito maior e todo tipo de brindes para uso interno no projeto.Em suma. É apresentada a idéia de uma solução simples para rápida localização temporária do projeto. Não depende de versões ou, se depender, é fraco. Esta solução foi migrada com sucesso do Symfony 4.2 para 5.