Как показать дату в правильном часовом поясе?

19

У меня есть поле Datetime Range (field_date) в типе содержимого. Как только я создаю свой тип контента, я устанавливаю дату начала как:

2017-02-27 19:30:01

введите описание изображения здесь

Теперь я хочу получить значение и показать дату в другом формате, поэтому попробуйте использовать следующий код:

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

Это мой вывод:

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

Итак, как показать дату в правильном часовом поясе?

Адриан Сид Альмагуер
источник

Ответы:

22

Я подозреваю, что проблема связана с некоторыми нюансами strtotime().

strtotime()может взять любую строку и преобразовать ее в метку времени Unix. Проблема в том, что когда строка не содержит явного часового пояса, она будет использовать то, что установлено date_default_timezone_get(). Это должно быть установлено от текущего пользователя, хотя (это установлено прокси учетной записи). Однако строка, которую вы извлекаете, $node->field_date->valueнеявно указана в UTC. Другими словами, я думаю, что строка, которую анализируют strtotime(), интерпретируется как «America / New_York», а не «UTC».

Хорошей новостью является то, что вы можете значительно упростить вашу ситуацию. Элемент поля для диапазона дат состоит из четырех частей

  • 'value' - это дата начала в виде строки в формате UTC
  • 'start_date' является объектом DrupalDateTime, представляющим 'значение'
  • 'end_value' - конечная дата в виде строки в UTC
  • end_date - это объект DrupalDateTime, представляющий end_value

Для простого поля даты и времени они

  • «значение» - это дата в виде строки в формате UTC
  • date - это объект DrupalDateTime, представляющий значение

Таким образом, вы можете работать с DrupalDateTime()объектом напрямую. Кроме того, этот date.formatterсервис должен использовать соответствующий часовой пояс, используемый пользователем, просматривающим страницу по умолчанию (в дополнение к использованию активного языка пользователя).

Я подозреваю, что-то подобное будет работать

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

Примечание. Я добавил туда заполнитель формата «P», чтобы вы могли видеть, какой часовой пояс используется системой.

Убедитесь, что вы правильно настроили часовой пояс в admin / config / Regional / settings, и что ваш пользовательский часовой пояс - это то, что вы ожидаете. Также убедитесь, что у вас установлен правильный часовой пояс в php.ini (это date.timezoneнастройка); странные вещи случаются, когда это не установлено (и я не помню, вызывает ли это предупреждение в установщике или в отчете о состоянии, когда не установлено. Я помню проблему, но не знаю, получилось ли это совершенные).

mpdonadio
источник
1
@mpdonadio Спасибо за помощь! Я не знал о dateвозможности доступа к DrupalDateTime. Из любопытства, как ты это понял? Я смог выяснить, что мое поле относится к типу, DateTimeItemно там не кажется очевидным, что для этого класса есть date(или value) открытый член.
Пзанно
1
@Pzanno Я - со-сопровождающий ядра datetime.module в Drupal 8, а также много других важных вещей, связанных с датой / временем. Если вы посмотрите DateTimeItem::propertyDefinitions(), вы увидите, что выставлено. Затем посмотрите, DateTimeComputed::getValue()как это работает. Все классы Предметов могут быть проверены таким образом, но лишь немногие используют вычисленные значения. Хотелось бы, чтобы был лучший способ показать это API.
mpdonadio
@mpdonadio спасибо за объяснение! Я пропустил тот факт, что они даже были определены в DateTimeItem::propertyDefinitions(). Я подумал, что происходит несколько магических методов, но хотел понять, как они были созданы, чтобы я мог лучше понять, как получить доступ к другим полям. Как вы сказали, было бы неплохо, если бы API был выставлен, но это помогает. Еще раз спасибо.
Пзанно
1
В моем случае я должен был сделать $node->field_date->date, а не $node->field_date->start_date.
Маркос
2
Подсказки к этому ответу, так как это самая информативная часть документации Drupal, которую я когда-либо обнаруживал.
Райан Х
6

Основываясь на обработке преобразования часовых поясов с помощью PHP DateTime, я изменил свой код так:

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

И теперь моя дата выхода это хорошо:

2017-02-27 19:30:01

Это работает. Но я уверен, что есть способ сделать это, используя Drupal API.

Адриан Сид Альмагуер
источник
1
Я удалил свой предыдущий комментарий. DrupalDateTime не подстраивается под часовой пояс пользователя Drupal.
oknate
В моем случае я использовал это перед вызовом любой функции даты:date_default_timezone_set(drupal_get_user_timezone());
Roger
Спасибо за это. Это действительно помогло мне с drupal.stackexchange.com/q/256490/2089 .
колан
@Colan приятно помочь вам, если вы считаете, что мой ответ полезен, я думаю, что вы можете проверить и другой ответ.
Адриан Сид Альмагуер
4

Я обычно решаю это так.

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
источник
2

Это сработало для меня:

// 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");
Дэн Поэнару
источник
Это также работает для меня: $ dDate = DrupalDateTime :: createFromTimestamp ($ form_state-> getValue ('service_date') -> getTimestamp (), 'UTC'); // Используйте как это. $ dDate-> format ('Ymd \ TH: i: s'); `
Йогеш Кушваха
1

Этот код работает для меня.

// 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');
Арун Мишра
источник
0

Ни один из этих ответов не помог мне. Это то, что я, наконец, закончил:

$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
источник
0

Я использую этот фрагмент для работы с датами:

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

Вы можете получить больше информации об объекте Datetime здесь

Бен Кассинат
источник