¿Cómo mostrar una fecha en la zona horaria correcta?

19

Tengo un campo Rango de fecha y hora (field_date) en un tipo de contenido. Una vez que creo mi tipo de contenido configuro la Fecha de inicio como:

2017-02-27 19:30:01

ingrese la descripción de la imagen aquí

Ahora quiero obtener el valor y mostrar la fecha en otro formato, así que trate de usar el siguiente 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 es mi salida:

// 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

Entonces, ¿cómo mostrar una fecha en la zona horaria correcta?

Adrian Cid Almaguer
fuente

Respuestas:

22

Sospecho que el problema tiene que ver con algunos de los matices de strtotime().

strtotime()puede tomar casi cualquier cadena y convertirla en una marca de tiempo de Unix. El problema es que cuando la cadena no contiene una zona horaria explícita, utilizará lo establecido por date_default_timezone_get(). Sin embargo, eso debe establecerse desde el usuario actual (lo establece el proxy de la cuenta). Sin embargo, la cadena que está extrayendo $node->field_date->valueestá implícitamente en UTC. En otras palabras, creo que la cadena se está analizando y strtotime()se interpreta como 'America / New_York' y no 'UTC'.

La buena noticia es que puede simplificar mucho su situación. El elemento de campo para un rango de fechas consta de cuatro partes

  • 'valor' es la fecha de inicio como una cadena en UTC
  • 'start_date' es un objeto DrupalDateTime que representa 'valor'
  • 'end_value' es la fecha de finalización como una cadena en UTC
  • 'end_date' es un objeto DrupalDateTime que representa 'end_value'

Para un campo de fecha y hora simple son

  • 'valor' es la fecha como una cadena en UTC
  • 'date' es un objeto DrupalDateTime que representa 'value'

Entonces, puedes trabajar con el DrupalDateTime()objeto directamente. Además, ese date.formatterservicio debe incluir la zona horaria adecuada que usa el usuario que ve la página de forma predeterminada (además de usar el idioma activo del usuario).

Sospecho que algo como esto 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'
);

Tenga en cuenta que agregué el marcador de posición en formato 'P' para que pueda ver qué zona horaria cree que está usando el sistema.

Asegúrese de tener la zona horaria adecuada configurada en admin / config / regional / settings, y que su zona horaria de usuario es lo que espera. También asegúrese de tener la zona horaria adecuada establecida en su php.ini (es la date.timezoneconfiguración); Suceden cosas extrañas cuando esto no está configurado (Y fuera de mi cabeza, no recuerdo si eso causa una advertencia en el instalador o en el informe de estado cuando no está configurado. Recuerdo el problema, pero no si se produjo comprometido).

mpdonadio
fuente
1
@mpdonadio Gracias por la ayuda! No conocía la datefunción para acceder a DrupalDateTime. Por curiosidad, ¿cómo resolviste esto? Pude darme cuenta de que mi campo era del tipo, DateTimeItempero dentro de eso no parece obvio que haya un date(o value) miembro público para esa clase.
Pzanno
1
@Pzanno Soy el co-mantenedor de Drupal 8 core datetime.module, y también muchas otras cosas relacionadas con la fecha / hora del núcleo vienen en mi camino :) Las propiedades son métodos mágicos. Si observas DateTimeItem::propertyDefinitions(), verás lo que está expuesto. Luego mira DateTimeComputed::getValue()para ver cómo funciona. Todas las clases de elementos se pueden examinar de esa manera, pero solo un puñado usa valores calculados. Desearía que hubiera una mejor manera de exponer esto a la API.
mpdonadio
@mpdonadio gracias por la explicación! Extrañé el hecho de que incluso se definieron en DateTimeItem::propertyDefinitions(). Pensé que estaban sucediendo algunos métodos mágicos, pero quería entender cómo se crearon para poder entender mejor cómo acceder a otros campos también. Como dijiste, sería bueno si la API estuviera expuesta, pero esto ayuda. Gracias de nuevo.
Pzanno
1
En mi caso, tuve que hacer $node->field_date->date, en lugar de $node->field_date->start_date.
Marcos Buarque
2
Apoya esta respuesta, ya que esta es la pieza más informativa de la documentación de Drupal que he encontrado.
Ryan H
6

Basado en el manejo de la conversión de zona horaria con PHP DateTime , modifiqué mi código para esto:

$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);

Y ahora mi fecha de salida está bien:

2017-02-27 19:30:01

Esto funciona. Pero estoy seguro de que hay una manera de hacer esto usando la API de Drupal.

Adrian Cid Almaguer
fuente
1
Eliminé mi comentario anterior. DrupalDateTime no se ajusta a la zona horaria del usuario de Drupal.
Oknate
En mi caso, acabo de usar esto antes de cualquier llamada de función de fecha:date_default_timezone_set(drupal_get_user_timezone());
Roger
Gracias por esto. Realmente me ayudó con drupal.stackexchange.com/q/256490/2089 .
colan
@colan un placer ayudarlo, si cree que mi respuesta es útil, creo que puede verificar la otra respuesta también.
Adrian Cid Almaguer
4

Usualmente lo resuelvo de esta manera .:

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
fuente
2

Esto funcionó para mí:

// 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
fuente
También funciona para mí `$ dDate = DrupalDateTime :: createFromTimestamp ($ form_state-> getValue ('service_date') -> getTimestamp (), 'UTC'); // Usar así. $ dDate-> format ('Ymd \ TH: i: s'); `
Yogesh Kushwaha
1

Este código me está funcionando.

// 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
fuente
0

Ninguna de estas otras respuestas funcionó para mí. Esto es lo que finalmente terminé con:

$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');
usuario1359
fuente
0

Utilizo este fragmento para trabajar con fechas:

<?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());
    }
}

Puede obtener más información sobre el objeto Datetime aquí

Ben Cassinat
fuente