Como mostrar uma data no fuso horário correto?

18

Eu tenho um campo Intervalo de data e hora (data_campo) em um tipo de conteúdo. Depois de criar meu tipo de conteúdo, defino a Data de início como:

2017-02-27 19:30:01

insira a descrição da imagem aqui

Agora eu quero obter o valor e mostrar a data em outro formato, então tente usar o seguinte código:

// Loading the node.
$node = Node::load(2100);
// Getting the start date value.
$date = $node->field_date->value;
// Printing to see what is the output.
dpm($node->field_date->value);
$date = strtotime($date);
// Printing my timezone.
dpm(drupal_get_user_timezone());
// Applying my custom format.
$date = \Drupal::service('date.formatter')->format($date, 'custom', 'Y-m-d H:i:s', drupal_get_user_timezone());
// Printing to see the output.
dpm($date);

Esta é a minha saída:

// It's fine.
2017-02-28T00:30:01
// Its fine.
America/New_York
// This is not what I waiting for. I'm waiting here: 2017-02-27 19:30:01
2017-02-28 00:30:01

Então, como mostrar uma data no fuso horário correto?

Adrian Cid Almaguer
fonte

Respostas:

21

Suspeito que o problema tenha a ver com algumas das nuances de strtotime().

strtotime()pode pegar praticamente qualquer string e convertê-la em um carimbo de data / hora do Unix. O problema é que, quando a string não contém um fuso horário explícito, ela usa o que é definido por date_default_timezone_get(). Porém, isso deve ser definido pelo usuário atual (é definido pelo proxy da conta). No entanto, a string da qual você está retirando $node->field_date->valueestá implicitamente no UTC. Em outras palavras, acho que a string que está sendo analisada strtotime()está sendo interpretada como em 'America / New_York' e não em 'UTC'.

A boa notícia é que você pode simplificar bastante sua situação. O item de campo para um período consiste em quatro partes

  • 'value' é a data de início como uma string no UTC
  • 'start_date' é um objeto DrupalDateTime que representa 'value'
  • 'end_value' é a data final como uma string no UTC
  • 'end_date' é um objeto DrupalDateTime que representa 'end_value'

Para um campo simples de data e hora, eles são

  • 'value' é a data como uma string no UTC
  • 'date' é um objeto DrupalDateTime que representa 'value'

Então, você pode trabalhar DrupalDateTime()diretamente com o objeto. Além disso, esse date.formatterserviço deve atrair o fuso horário adequado em uso pelo usuário que visualiza a página por padrão (além de usar o idioma ativo do usuário).

Eu suspeito que algo assim vai funcionar

$node = Node::load(2100);
$start_date = $node->field_date->start_date;
$formatted = \Drupal::service('date.formatter')->format(
  $start_date->getTimestamp(), 'custom', 'Y-m-d H:i:s P'
);

Observe que adicionei o espaço reservado no formato 'P' para que você possa ver qual o fuso horário que o sistema pensa que está sendo usado.

Verifique se o fuso horário está configurado em admin / config / regional / settings e se o fuso horário do usuário é o que você espera. Verifique também se você tem o fuso horário adequado definido no seu php.ini (é a date.timezoneconfiguração); coisas estranhas acontecem quando isso não está definido (e não me lembro se isso causa um aviso no instalador ou no relatório de status quando não está definido. Lembro-me do problema, mas não se ele ficou comprometido).

mpdonadio
fonte
11
@mpdonadio Obrigado pela ajuda! Eu não sabia sobre o daterecurso para acessar o DrupalDateTime. Por curiosidade, como você descobriu isso? Consegui descobrir que meu campo era do tipo, DateTimeItemmas lá dentro não parece óbvio que exista um membro público date(ou value) público para essa classe.
Pzanno
11
@Pzanno Eu sou o co-mantenedor do Drupal 8, datetime.module, e também muitas outras coisas relacionadas a data / hora do núcleo aparecem no meu caminho :) As propriedades são métodos mágicos. Se você olhar DateTimeItem::propertyDefinitions(), verá o que está exposto. Então olhe DateTimeComputed::getValue()para ver como isso funciona. Todas as classes de itens podem ser examinadas dessa maneira, mas apenas um punhado usa valores calculados. Eu gostaria que houvesse uma maneira melhor de expor isso à API.
mpdonadio
@mpdonadio obrigado pela explicação! Perdi o fato de que eles foram definidos DateTimeItem::propertyDefinitions(). Imaginei que havia alguns métodos mágicos acontecendo, mas queria entender como eles foram criados para que eu pudesse entender melhor como acessar outros campos também. Como você disse, seria bom se a API fosse exposta, mas isso ajuda. Obrigado novamente.
Pzanno
11
No meu caso, eu tive que fazer $node->field_date->date, em vez de $node->field_date->start_date.
Marcos Buarque
2
Adereços para esta resposta, pois esta é a parte mais informativa da documentação do Drupal que eu JÁ encontrei.
Ryan H
6

Com base na manipulação de conversão de fuso horário com PHP DateTime , modifiquei meu código para isso:

$node = Node::load(2100);
$userTimezone = new DateTimeZone(drupal_get_user_timezone());
$gmtTimezone = new DateTimeZone('GMT');
$myDateTime = new DateTime($node->field_date->value, $gmtTimezone);
$offset = $userTimezone->getOffset($myDateTime);
$myInterval = DateInterval::createFromDateString((string)$offset . 'seconds');
$myDateTime->add($myInterval);
$result = $myDateTime->format('Y-m-d H:i:s');
dpm($result);

E agora minha data de saída está boa:

2017-02-27 19:30:01

Isso funciona. Mas tenho certeza de que existe uma maneira de fazer isso usando a API Drupal.

Adrian Cid Almaguer
fonte
11
Eu removi meu comentário anterior. DrupalDateTime não se ajusta ao fuso horário do usuário do Drupal.
oknate
No meu caso, eu apenas usei isso antes de qualquer chamada de função de data:date_default_timezone_set(drupal_get_user_timezone());
Roger
Obrigado por isso. Realmente me ajudou com drupal.stackexchange.com/q/256490/2089 .
colan
Colan @ um prazer ajudá-lo, se você acha que a minha resposta é útil, acho que você pode verificar a outra resposta também.
Adrian Cid Almaguer
4

Eu costumo resolver desta maneira:

node = Node::load(1);    
$date_original= new DrupalDateTime( $node->field_date->value , 'UTC' );     
$result = \Drupal::service('date.formatter')->format( $date_original->getTimestamp(), 'custom', 'Y-m-d H:i:s'  );    
dpm( $result );
Ytacol
fonte
2

Isso funcionou para mim:

// Take the timestamp.
$timestamp = $form_state->getValue($form_field)->getTimestamp();
// Convert to UTC - the way its saved in database.
$date = DrupalDateTime::createFromTimestamp($timestamp, 'UTC');
// Add on the current entity.
$this->entity->{$db_field}->value = $date->format("Y-m-d\TH:i:s");
Dan Poenaru
fonte
Funciona para mim também: $ dDate = DrupalDateTime :: createFromTimestamp ($ form_state-> getValue ('service_date') -> getTimestamp (), 'UTC'); // Use assim. $ dDate-> format ('Ymd \ TH: i: s'); `
Yogesh Kushwaha
1

Este código está funcionando para mim.

// set use timezone
userTimezone = new DateTimeZone(drupal_get_user_timezone());
// set gmt timezone
$gmtTimezone = new DateTimeZone('GMT');
// Take the timestamp.
$start_date = $val['field_start_time_value'];    
$startDateTime = new DateTime($start_date, $gmtTimezone);
$offset = $userTimezone->getOffset($startDateTime);
$startInterval = DateInterval::createFromDateString((string)$offset . 'seconds');
$startDateTime->add($startInterval);
$result = $startDateTime->format('Y-m-d H:i:s');
Arun Mishra
fonte
0

Nenhuma dessas outras respostas funcionou para mim. Foi assim que finalmente acabei:

$range_start = DrupalDatetime::createFromTimestamp((int)$start_date); // unix timestamp
$range_start->setTimezone(new \Datetimezone('EST'));
$node->field_date_range->value = format_date(
  $range_start->getTimestamp() , 'custom', 'Y-m-d\TH:i:s', 'EST');
user1359
fonte
0

Eu uso esse snippet para trabalhar com datas:

<?php
class MyApp extends ControllerBase
{
    public function output() 
    {
        $dtime = DateTime::createFromFormat("d/m/Y - H:I:s", "22/12/1999 - 22:55:12", $this->getDateTimeZone());
        $time = $dtime->format('r');
        // My code goes here
    }

    private function getDateTimeZone()
    {
        return new DateTimeZone(drupal_get_user_timezone());
    }
}

Você pode obter mais informações sobre o objeto Datetime aqui

Ben Cassinat
fonte