Шаг 60.
Python: сборник рецептов.
Числа, даты и время. Манипулирование датами с учетом временных зон

    На этом шаге мы рассмотрим основные принципы работы с временными зонами.

Задача

    У вас назначена телефонная конференция на 21 декабря 2012 года в 9:30 утра в Чикаго. В какое местное время ваш друг из индийского города Бангалор должен выйти на связь?

Решение

    Для практически любых задач, связанных с временными зонами, вы можете использовать модуль pytz.


https://pypi.org/project/pytz/.

    Этот пакет предоставляет базу временных зон Олсона (tz databaze), которая является стандартом де-факто для многих языков программирования и операционных систем.

    Большая часть случаев использования pytz приходится на приведение к локальному времени дат, созданных с помощью библиотеки datetime. Например, вот как вы могли бы представить дату с чикагским местным временем:

>>> from datetime import datetime
>>> from pytz import timezone
>>> d = datetime(2012, 12, 21, 9, 30, 0)
>>> print(d)
2012-12-21 09:30:00
>>>
>>> # Локализуем дату для Чикаго 
>>> central = timezone('US/Central')
>>> loc_d = central.localize(d)
>>> print(loc_d)
2012-12-21 09:30:00-06:00
>>>

    Когда дата локализована, ее можно конвертировать в другие временные зоны. Чтобы найти бангалорское время, вы можете сделать так:

>>> # Преобразуем во время по Бангалору
>>> bang_d = loc_d.astimezone(timezone('Asia/Kolkata'))
>>> print(bang_d)
2012-12-21 21:00:00+05:30
>>>

    Если вы собираетесь выполнять арифметические операции над локализованными датами, вам нужно знать о переводах времени с летнего на зимнее и прочих подобных деталях. Например, в 2013 году стандартное летнее время США началось 13 марта в 2:00 ночи по местному времени городов (время было переведено на час вперед). Если бы провели стандартную арифметическую операцию над датами, то получили бы неверный результат. Например:

>>> d = datetime(2013, 3, 10, 1, 45)
>>> loc_d = central.localize(d)
>>> print(loc_d)
2013-03-10 01:45:00-06:00
>>> later = loc_d + timedelta(minutes=30)
>>> print(later) # НЕВЕРНО! НЕВЕРНО!
2013-03-10 02:15:00-06:00 
>>>

    Ответ получается неверным, поскольку он не учитывает перевод местного времени на один час. Чтобы исправить это, используйте метод временных зон normalize(). Например:

>>> from datetime import timedelta
>>> later = central.normalize(loc_d + timedelta(minutes=30))
>>> print(later)
2013-03-10 03:15:00-05:00
>>>

Обсуждение

    Чтобы предотвратить взрыв головы, используйте обычную стратегию работы с локальным временем: преобразование всех дат в UTC и использование уже их для хранения и обработки. Например:

>>> print(loc_d)
2013-03-10 01:45:00-06:00
>>> utc_d = loc_d.astimezone(pytz.utc)
>>> print(utc_d)
2013-03-10 07:45:00+00:00 
>>>

    Если время уже в UTC, вы можете не волноваться по поводу проблем, связанных с переходом на летнее время, а также прочих подобных вещах. Вы свободно можете выполнять арифметические операции с датами. Если же вы хотите вывести дату в локальном времени, просто сконвертируйте в нужную временную зону. Например:

>>> later_utc = utc_d + timedelta(minutes=30)
>>> print(later_utc.astimezone(central))
2013-03-10 03:15:00-05:00
>>>

    С использованием временных зон есть одна проблема: какие имена временных зон использовать? Например, в этом рецепте мы как-то узнали, что "Asia/ Kolkata" - это правильное название временной зоны для Индии. Чтобы узнать название нужной зоны, поищите в словаре pytz.country_timezones, указывая в качестве ключа код страны по ISO 3166. Например:

>>> print(pytz.country_timezones['IN'])
['Asia/Kolkata']
>>>
Архив с приведенными примерами можно взять здесь.

    Со следующего шага мы начнем рассматривать итераторы и генераторы.




Предыдущий шаг Содержание Следующий шаг