Формат даты и времени (РЕШЕНО)

Прислано: ingumsky@drupal.org

вс, 11/01/2009 - 10:56

Другие статьи по теме:

Добрый день!

При написании модуля столкнулся с проблемой, которая, наверное, многим покажется надуманной, но решения её я не нашёл. Существует тип контента с полями даты и времени, заданными через cck, для определения каждого значения cck (скорее даже date) использует три поля field_something_value (значение вида 2009-01-10T17:30:00), field_something_timezone (значение вида Europe/Moscow) и field_something_offset (значение вида 10800). Вопрос в следующем, как мне «собрать» из этого «правильные» дату и время для пользователя, у которого стандартными методами в профиле выставлен свой часовой пояс.

Пытался найти решение, исследуя второй views, который с полями даты и времени работает нормально. Нашёл, в частности, вo views/includes/handlers.inc (там целый раздел есть Date helper functions)

/**
 * Figure out what timezone we're in; needed for some date manipulations.
 */
function views_get_timezone() {
  global $user;
  if (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) {
    $timezone = $user->timezone;
  }
  else {
    $timezone = variable_get('date_default_timezone', 0);
  }
 
  // set up the database timezone
  if (in_array($GLOBALS['db_type'], array('mysql', 'mysqli'))) {
    static $already_set = false;
    if (!$already_set) {
      if ($GLOBALS['db_type'] == 'mysqli' || version_compare(mysql_get_server_info(), '4.1.3', '>=')) {
        db_query("SET @@session.time_zone = '+00:00'");
      }
      $already_set = true;
    }
  }
 
  return $timezone;
}
и
/**
 * Helper function to create cross-database SQL dates.
 *
 * @param $field
 *   The real table and field name, like 'tablename.fieldname'.
 * @param $field_type
 *   The type of date field, 'int' or 'datetime'.
 * @param $set_offset
 *   The name of a field that holds the timezone offset or a fixed timezone
 *   offset value. If not provided, the normal Drupal timezone handling
 *   will be used, i.e. $set_offset = 0 will make no timezone adjustment.
 * @return
 *   An appropriate SQL string for the db type and field type.
 */
function views_date_sql_field($field, $field_type = 'int', $set_offset = NULL) {
  $db_type = $GLOBALS['db_type'];
  $offset = $set_offset !== NULL ? $set_offset : views_get_timezone();
  switch ($db_type) {
    case 'mysql':
    case 'mysqli':
      switch ($field_type) {
        case 'int':
          $field = "FROM_UNIXTIME($field)";
          break;
        case 'datetime':
          break;
      }
      if (!empty($offset)) {
        $field = "($field + INTERVAL $offset SECOND)";
      }
      return $field;
    case 'pgsql':
      switch ($field_type) {
        case 'int':
          $field = "$field::ABSTIME";
          break;
        case 'datetime':
          break;
      }
      if (!empty($offset)) {
        $field = "($field + INTERVAL '$offset SECONDS')";
      }
      return $field;
  }
}

Но вот пока не знаю, как использовать полученные знания -\ Поможите, чем можите, пожалуйста.

Комментарии


Настройки просмотра комментариев

Выберите нужный метод показа комментариев и нажмите "Применить"
Опубликовано rodman1980 в вс, 11/01/2009 - 11:41.

Стандартная функция друпала

format_date($timestamp, $type = 'medium', $format = '', $timezone = NULL, $langcode = NULL)

где $timezone = NULL - это оффсет в секундах, но если посмотреть сам код функции то если $timezone при вызове функции явно не задан то используется глобальный юзер и настройка времени для него т.е.

if (!isset($timezone)) {
    global $user;
    if (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) {
      $timezone = $user->timezone;
    }
    else {
      $timezone = variable_get('date_default_timezone', 0);
    }
  }
$timestamp += $timezone;

ссылка http://api.drupal.org/api/function/format_date/6


Опубликовано ingumsky@drupal.org в вс, 11/01/2009 - 12:15.

Спасибо! Оказывается, всё уже придумано до нас -)))

Я правильно понимаю, если я тащу запросом $my_query->field_something_value, $my_query->field_something_timezone и $my_query->field_something_offset, то кормить функцию я должен, например, так:

$my_date = format_date($my_query->field_something_value, $type = 'short', $timezone = $my_query->field_something_timezone)

И ещё вопрос, зачем тогда нужно поле с офсетом, если используется только часовой пояс?


Опубликовано neochief в вс, 11/01/2009 - 12:57.

Кстати, есть переведеное описалово:
http://api.drupal.ru/api/function/format_date

см. там параметр $timezone. Обратите внимание, что он числовой, поэтому вы должны туда пихать скорее $my_query->field_something_offset, нежели строковый $my_query->field_something_timezone.


Опубликовано ingumsky@drupal.org в вс, 11/01/2009 - 14:31.

Спасибо. По-русски даже лучше читать.

Да, я уже понял, что $my_query->field_something_timezone не подойдёт для указания смещения, но вот в чём закавыка — если я указываю $timezone = $my_query->field_something_offset, у меня почему-то вообще начинают происходить странные вещи - например:
В записи (2009-01-24T21:00:00 (1232820000), Europe/Moscow, 10800) для пользователя с часовым поясом Europe/London, время отображается как 18:00, а для пользователя с часовым поясом Europe/Moscow время указано как 21:00
В записи (2009-01-10T17:30:00 (1231597800), Europe/Moscow, 10800), соответственно, 14:30 и 17:30.

И всё бы ничего, но это совсем не соответствует тому, что должно быть, так как при создании типа контента «матч» (cck+date) было сделано так, чтобы сохранялся часовой пояс пользователя, а сам создатель ноды должен указывать в поле даты и времени начало матча по своему времени. То есть, если матч начинается в 20:00 по Киеву, пользователь пишет именно 20:00, не думая о том, сколько будет на часах судьи, когда он даст свисток. Соответственно, в данном случае человек при создании указывал для второй записи московское время 20:30, а для первой он вообще его не знал и поставил лишь дату — 25 (а не 24!) января. Аналогично в следующей записи (2008-09-19T23:00:00 (1221850800), Europe/London, 3600) пользователь из часового пояса Europe/London указал лишь, что матч состоялся 20 сентября... И как мне разбираться с этим безобразием? -((

PS Хотел написать о том, что cck-де всё правильно разбирает, но оказалось, что со временем и у него нелады, так что он просто выдаёт время, введённое одним пользователем, другому, даже если они находятся в разных часовых поясах. Зато он не указывает время в тех случаях, когда автор ноды его не вводил, ограничиваясь лишь датой.


Опубликовано ingumsky@drupal.org в вс, 11/01/2009 - 14:59.

Видимо, я что-то не то делаю при вызове format_date(), потому что ловлю буквально следующее при дебаге:

Цитата:

$date_before=Вс, 18/01/2009 - 21:00 | $match->offset=10800 | Вс, 18/01/2009 - 21:00

в ответ на следующий код в модуле

Цитата:

$date_before='.format_date($match->my_date, $type = 'medium', $timezone = NULL).' | $match->offset='.$match->offset.' | '.format_date($match->my_date, $type = 'medium', $timezone = "$match->offset")

Подскажите, пожалуйста, в чём моя ошибка?


Опубликовано neochief в вс, 11/01/2009 - 15:06.

Сделайте выставление времени обязательным. По-другому точно никак, только если вас устроят погрешности.


Опубликовано ingumsky@drupal.org в вс, 11/01/2009 - 15:28.

Спасибо, но такой вариант не подойдёт, потому что в базе есть события, для которых узнать время не представляется возможным. Мне важнее всего, чтобы в блоках выводилось верное время — а при просмотре ноды я, в принципе, могу обойтись и без него. А в блоках у меня старые события выводится не будут — только предыдущее и следующее.

А почему по-другому никак? По идее ведь есть все составляющие для того, чтобы правильно вычислять время — плюс можно настроить отлов дат без указания времени, добавив чекбокс, например, или отслеживая пустое поле со временем при сохранении ноды и сохраняя информацию об этом в отдельном поле.


Опубликовано ingumsky@drupal.org в вс, 11/01/2009 - 17:58.

В принципе, проблему со временем можно было бы решить, если бы пользователь вводил время в своё часовом поясе, а записывалось бы в базу оно в унифицированном формате (как UTC или GMT, например), а уже при отображении можно было бы вытаскивать таймстемп из базы и накладывать на него оффсет в зависимости от часового пояса, указанного пользователем (или заданного программно). Вот только я что-то не помню у datefield подобной настройки. Я полагал, что та, которую я использую в настоящее время, будет хранить данные как раз в унифицированном виде, но оказалось, что это не так.


Опубликовано ingumsky@drupal.org в пт, 23/01/2009 - 00:49.

Решил свою проблему с выдачей пользователю даты и времени с учётом его (пользователя) часового пояса через использование Date API. Возможно, кому-нибудь пригодится мой опыт, поэтому расскажу в двух словах. У меня в базе дата и время хранятся в часовом поясе UTC, а при создании блока я провожу следующие операции.
1. Вытаскиваю нужные данные из базы, форматируя вывод поля с датой и временем как "%Y-%m-%d %H:%i:%s";
2. Вытаскиваю $user->timezone_name;
3. Создаю объект даты и времени при помощи date_make_date();
4. Обрабатываю объект date_timezone_set(), чтобы скорректировать часовой пояс под часовой пояс пользователя.
5. Вывожу данные о времени и дате в нужном мне формате через date_format_date().

Смотрю на список шагов, понимаю, что всё просто, но времени на то, чтобы заставить это всё работать корректно, у такого ламера, как я, ушло немало, и главные проблемы в мелочах. Нужно внимательно следить за тем, что передаётся функциям, потому что date_make_date, например, использует в качестве значения текущие дату и время, если поданное модулем значение не проходит проверку. Меня это поставило в тупик, и я долго не мог разобраться с тем, в чём же ошибка.


Новое на сайте