Comment afficher une date dans le fuseau horaire correct?

19

J'ai un champ de plage de dates (field_date) dans un type de contenu. Une fois que j'ai créé mon type de contenu, j'ai défini la date de début comme suit:

2017-02-27 19:30:01

entrez la description de l'image ici

Maintenant, je veux obtenir la valeur et afficher la date dans un autre format, essayez donc d'utiliser le code suivant:

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

Voici ma sortie:

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

Alors, comment afficher une date dans le fuseau horaire correct?

Adrian Cid Almaguer
la source

Réponses:

22

Je soupçonne que le problème a à voir avec certaines des nuances de strtotime().

strtotime()peut prendre à peu près n'importe quelle chaîne et la convertir en horodatage Unix. Le problème est que lorsque la chaîne ne contient pas de fuseau horaire explicite, elle utilise ce qui est défini par date_default_timezone_get(). Cela doit être défini à partir de l'utilisateur actuel, cependant (il est défini par le proxy de compte). Cependant, la chaîne que vous extrayez $node->field_date->valueest implicitement en UTC. En d'autres termes, je pense que la chaîne en cours d'analyse strtotime()est interprétée comme dans 'America / New_York' et non dans 'UTC'.

La bonne nouvelle est que vous pouvez beaucoup simplifier votre situation. L'élément de champ pour une plage de dates se compose de quatre parties

  • 'valeur' ​​est la date de début sous forme de chaîne en UTC
  • 'start_date' est un objet DrupalDateTime représentant 'value'
  • 'end_value' est la date de fin sous forme de chaîne en UTC
  • 'end_date' est un objet DrupalDateTime représentant 'end_value'

Pour un champ date / heure simple, ils sont

  • 'valeur' ​​est la date sous forme de chaîne en UTC
  • 'date' est un objet DrupalDateTime représentant 'value'

Vous pouvez donc travailler DrupalDateTime()directement avec l' objet. En outre, ce date.formatterservice doit extraire le fuseau horaire approprié utilisé par l'utilisateur qui affiche la page par défaut (en plus d'utiliser la langue active de l'utilisateur).

Je soupçonne que quelque chose comme ça fonctionnera

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

Remarque J'ai ajouté un espace réservé au format «P» pour que vous puissiez voir quel fuseau horaire le système pense être utilisé.

Assurez-vous que le fuseau horaire approprié est configuré dans admin / config / regional / settings et que le fuseau horaire de votre utilisateur est celui que vous attendez. Assurez-vous également que le fuseau horaire approprié est défini dans votre php.ini (c'est le date.timezoneparamètre); des choses étranges se produisent lorsque ce n'est pas défini (Et du haut de ma tête, je ne me souviens pas si cela provoque un avertissement dans l'installateur ou sur le rapport d'état lorsqu'il n'est pas défini. Je me souviens du problème, mais pas s'il s'est engagé).

mpdonadio
la source
1
@mpdonadio Merci pour l'aide! Je ne connaissais pas la datefonctionnalité pour accéder à DrupalDateTime. Par curiosité, comment avez-vous compris cela? J'ai pu comprendre que mon domaine était du type DateTimeItemmais à l'intérieur, il ne semble pas évident qu'il y ait un date(ou value) membre public pour cette classe.
Pzanno
1
@Pzanno Je suis le co-mainteneur de datup.module de base de Drupal 8, et aussi beaucoup d'autres choses liées à la date / heure de base viennent à ma rencontre :) Les propriétés sont des méthodes magiques. Si vous regardez DateTimeItem::propertyDefinitions(), vous verrez ce qui est exposé. Ensuite, regardez DateTimeComputed::getValue()pour voir comment cela fonctionne. Toutes les classes d'objets peuvent être examinées de cette façon, mais seule une poignée utilise des valeurs calculées. Je souhaite qu'il y ait une meilleure façon d'exposer cela à l'API.
mpdonadio
@mpdonadio merci pour l'explication! J'ai raté le fait qu'ils étaient même définis dans DateTimeItem::propertyDefinitions(). J'ai pensé qu'il y avait des méthodes magiques, mais je voulais comprendre comment elles ont été créées afin que je puisse mieux comprendre comment accéder à d'autres champs également. Comme vous l'avez dit, ce serait bien si l'API était exposée, mais cela aide. Merci encore.
Pzanno
1
Dans mon cas, je devais faire $node->field_date->date, au lieu de $node->field_date->start_date.
Marcos Buarque
2
Props à cette réponse car c'est la pièce la plus informative de la documentation Drupal que j'ai jamais trouvée.
Ryan H
6

Basé sur la gestion de la conversion du fuseau horaire avec PHP DateTime, j'ai modifié mon code en ceci:

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

Et maintenant ma date de sortie c'est bien:

2017-02-27 19:30:01

Cela marche. Mais je suis sûr qu'il existe un moyen de le faire en utilisant l'API Drupal.

Adrian Cid Almaguer
la source
1
J'ai supprimé mon commentaire précédent. DrupalDateTime ne s'adapte pas au fuseau horaire de l'utilisateur Drupal.
Oknate
Dans mon cas, je viens de l'utiliser avant tout appel de fonction de date:date_default_timezone_set(drupal_get_user_timezone());
Roger
Merci pour cela. Cela m'a vraiment aidé avec drupal.stackexchange.com/q/256490/2089 .
colan
@colan un plaisir de vous aider, si vous pensez que ma réponse est utile, je pense que vous pouvez également vérifier l'autre réponse.
Adrian Cid Almaguer
4

Je le résout généralement de cette façon:

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
la source
2

Cela a fonctionné pour moi:

// 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
la source
Cela fonctionne aussi pour moi `$ dDate = DrupalDateTime :: createFromTimestamp ($ form_state-> getValue ('service_date') -> getTimestamp (), 'UTC'); // Utilisez comme ça. $ dDate-> format ('Ymd \ TH: i: s'); `
Yogesh Kushwaha
1

Ce code fonctionne pour moi.

// 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
la source
0

Aucune de ces autres réponses n'a fonctionné pour moi. C'est ce que j'ai finalement fini avec:

$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
la source
0

J'utilise cet extrait pour travailler avec des dates:

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

Vous pouvez obtenir plus d'informations sur l'objet Datetime ici

Ben Cassinat
la source