Формат даты и времени (РЕШЕНО)
Прислано: 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; } }
Но вот пока не знаю, как использовать полученные знания -\ Поможите, чем можите, пожалуйста.
- ingumsky@drupal.org's blog
- Для комментирования войдите или зарегистрируйтесь
Стандартная функция друпала
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
- Для комментирования войдите или зарегистрируйтесь
Спасибо! Оказывается, всё уже придумано до нас -)))
Я правильно понимаю, если я тащу запросом $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)
И ещё вопрос, зачем тогда нужно поле с офсетом, если используется только часовой пояс?
- Для комментирования войдите или зарегистрируйтесь
Кстати, есть переведеное описалово:
http://api.drupal.ru/api/function/format_date
см. там параметр $timezone. Обратите внимание, что он числовой, поэтому вы должны туда пихать скорее $my_query->field_something_offset, нежели строковый $my_query->field_something_timezone.
- Для комментирования войдите или зарегистрируйтесь
Спасибо. По-русски даже лучше читать.
Да, я уже понял, что $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-де всё правильно разбирает, но оказалось, что со временем и у него нелады, так что он просто выдаёт время, введённое одним пользователем, другому, даже если они находятся в разных часовых поясах. Зато он не указывает время в тех случаях, когда автор ноды его не вводил, ограничиваясь лишь датой.
- Для комментирования войдите или зарегистрируйтесь
Видимо, я что-то не то делаю при вызове 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")
Подскажите, пожалуйста, в чём моя ошибка?
- Для комментирования войдите или зарегистрируйтесь
Сделайте выставление времени обязательным. По-другому точно никак, только если вас устроят погрешности.
- Для комментирования войдите или зарегистрируйтесь
Спасибо, но такой вариант не подойдёт, потому что в базе есть события, для которых узнать время не представляется возможным. Мне важнее всего, чтобы в блоках выводилось верное время — а при просмотре ноды я, в принципе, могу обойтись и без него. А в блоках у меня старые события выводится не будут — только предыдущее и следующее.
А почему по-другому никак? По идее ведь есть все составляющие для того, чтобы правильно вычислять время — плюс можно настроить отлов дат без указания времени, добавив чекбокс, например, или отслеживая пустое поле со временем при сохранении ноды и сохраняя информацию об этом в отдельном поле.
- Для комментирования войдите или зарегистрируйтесь
В принципе, проблему со временем можно было бы решить, если бы пользователь вводил время в своё часовом поясе, а записывалось бы в базу оно в унифицированном формате (как UTC или GMT, например), а уже при отображении можно было бы вытаскивать таймстемп из базы и накладывать на него оффсет в зависимости от часового пояса, указанного пользователем (или заданного программно). Вот только я что-то не помню у datefield подобной настройки. Я полагал, что та, которую я использую в настоящее время, будет хранить данные как раз в унифицированном виде, но оказалось, что это не так.
- Для комментирования войдите или зарегистрируйтесь
Решил свою проблему с выдачей пользователю даты и времени с учётом его (пользователя) часового пояса через использование 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, например, использует в качестве значения текущие дату и время, если поданное модулем значение не проходит проверку. Меня это поставило в тупик, и я долго не мог разобраться с тем, в чём же ошибка.
- Для комментирования войдите или зарегистрируйтесь

Комментарии