27 марта 2012 г.

Новинки Django 1.4, day 8: Timezones

Посвящается часовым поясам без DST.

Это предпоследний пост в серии Django Days. В этот раз будет затронуто одно из самых масштабных изменений в фреймворке: работу с часовыми поясами. Документация по этой теме достаточно подробная, мы попробуем рассказать это более интересно и по возможности сжато.

Что это и зачем
В Django до версии 1.4 все даты хранились и отображались для одного часового пояса, того, который указанны в настройках settings.TIME_ZONE. Теперь же появилась возможность хранить все даты в формате UTC и удобно выводить их с поправкой на часовой пояс пользователя.

Дополнительным плюсом нового подхода к хранению дат является обход проблем с переводом часов на летнее время и обратно, которые могут возникать раз в год. Например, 31 октября 2010 года, когда последний раз переводили стрелки осенью в России, время 02:10 утра по местному времени нельзя однозначно интерпретировать (так как час с 2:00 до 3:00 «проходит» дважды: до и после перевода стрелок). Для биллинговых систем это мелочью не назовёшь, поэтому лучше для машин хранить время в UTC, а выводить для людей с учётом часового пояса. Упомянутые «02:10 31 октября 2010» превратятся в понятные программистам "2010-10-30T22:10:00+04:00" и "2010-10-30T23:10:00+03:00".

Основные понятия
Объекты типа datetime в Python поддерживают указание часовых поясов с помощью атрибута tzinfo. Если атрибут заполнен, дата называется «timezone-aware», в противном случае это «naive» дата.

В новых проектах на Django 1.4, или если вы пожелали воспользоваться новой фичей при обновлении, установив settings.USE_TZ=True, Джанго использует timezone-aware даты.

Часовым поясом по-умолчанию (default timezone) называется пояс, указанный в settings.TIME_ZONE (например, 'Europe/Moscow').
Текущим часовым поясом (current timezone) считается тот, что активирован для пользователя в данный момент, об этом ниже.

Текущий часовой пояс
Чтобы показать пользователю дату и время из его часового пояса, необходимо для начала выяснить откуда он. Можно напрямую спросить пользователя и сохранить timezone в сессии, а можно воспользоваться базами вроде maxmind, угадав пояс по ip, или даже использовать Date.getTimezoneOffset() в js. Так или иначе, на основе этого выбора нам придётся активировать часовой пояс пользователя, чтобы повлиять на отображение дат и времени в шаблонах и формах.

Пример установки текущего часового пояса в middleware (из документации):
from django.utils import timezone

class TimezoneMiddleware(object):
    def process_request(self, request):
        tz = request.session.get('django_timezone')
        if tz:
            timezone.activate(tz)
Необходимая функциональность для работы с датами сосредоточена в модуле django.utils.timezone.


Шаблоны
Работа с датами широко поддерживается не только в питон-коде, но и в шаблонах:
# В данном блоке даты будут сконвертированы в парижский часовой пояс
# (а не текущий)
{% timezone "Europe/Paris" %}
    Paris time: {{ value }}
{% endtimezone %}

# В данном блоке даты будут выведены как они хранятся (UTC)
{% localtime off %}
    {{ value }}
{% endlocaltime %}
Это лишь небольшая демонстрация, в документации более подробно.

Мигрируем
1. Включаем settings.USE_TZ = True
2. pip install pytz (чтобы точней работало, см. ниже)
3. Пользователи PostgreSQL пропускают шаг, остальные конвертируют свои даты в БД из местного часового пояса в UTC (если он отличается, конечно).

Теперь ваш код уже поддерживает часовые пояса. Осталось починить некоторые места, в которых Django исправляет старый код за нас, преобразовывая naive-даты в timezone-aware. В противном случае могут возникать ошибки во время перевода на летнее время.

Для полноценной миграции пригодится уже упомянутый модуль django.utils.timezone и отлов мест, где даты в старом формате сравниваются «новыми».

pytz
Это библиотека, позволяющая удобно работать с часовыми поясами в питоне. Она включает в себя базу таймзон Олсона и неплохой API для различных вычислений с часовыми поясами. Несмотря на то, что она необязательна для работы часовых поясов в Django 1.4, разработчики настоятельно рекомендуют установить её, так как это позволит фреймворку не угадывать часовой пояс по-умолчанию при вычислениях, а также предоставляет список доступных поясов для вывода пользователю.

Заключение
Данный обзор повторяет документацию, фокусируясь на ключевых для понимания вещах, но многое оставляет за кадром. Почитайте доки и особенно FAQ напоследок для закрепления
материала :)

В следующий раз я подведу итог по всей серии постов, ещё раз выделив самые интересные моменты Django 1.4. Вероятно до Moscow Django Meetup №2 в блоге также появится моя презентация на эту же тему.

Комментариев нет:

Отправить комментарий