Сообщить об ошибке.

Класс tzinfo() модуля datetime в Python

Разница между местным временем и UTC

Что такое "Часовой пояс"?

UTC+03:00 будет правильным ответом только на текущий момент времени, но в целом это заявление некорректно. Если вы посмотрите на базу данных часовых поясов, то увидите, к примеру, что Берлин и Вена, несмотря на смещение UTC+01:00, имеют разные часовые пояса 'Europe/Berlin' и 'Europe/Vienna'. Почему так? Причина в том, что они имели разное летнее время (DST) в разные периоды истории.

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

Здравый смысл и рекомендации.

Здравый смысл называется Всемирное координированное время (UTC). UTC - это таймзона без перехода на летнее время и без каких бы то ни было изменений в прошлом.

Главное правило, которое никогда не подведёт:

  • Всегда храните и работайте со временем в UTC. Если вам нужно сохранить оригинальные данные - пишите их отдельно. Никогда не храните локальное время и TZ.

Из UTC вы можете конвертировать время в локальное для любого часового пояса.

Синтаксис:

import datetime

datetime.tzinfo()

Параметры:

  • нет

Описание:

Класс tzinfo() модуля datetime это абстрактный базовый класс, означающий, что этот класс не должен вызываться напрямую. Необходимо определить свой подкласс datetime.tzinfo для сбора информации о конкретном часовом поясе.

Созданный подкласс datetime.tzinfo будет передаваться конструкторам объектов datetime.datetime() и datetime.time(). Последние объекты рассматривают свои атрибуты как находящиеся в местном времени, а объект tzinfo поддерживает методы, показывающие смещение местного времени от UTC, имя часового пояса и смещение DST, и все это относительно переданного им объекта даты или времени.

В общем необходимо создать конкретный подкласс и предоставить реализации стандартных методов класса datetime.tzinfo, которые в последствии будут использоваться.

Модуль datetime предоставляет простой подкласс datetime.timezone(), который может представлять часовые пояса с фиксированным смещением от UTC.

Методы абстрактного базового класса datetime.tzinfo:

Эти методы вызываются объектом datetime.datetime() или datetime.time() в ответ на их методы с такими же именами. Объект datetime.datetime() передает себя в качестве аргумента, а объект datetime.time() передает None в качестве аргумента.

Когда значение None не указано, дизайнер должен выбрать лучший ответ. Например, возвращение None подходит, если класс хочет сказать, что объекты времени не участвуют в протоколах tzinfo. Для utcoffset(None) может быть более полезно возвращать стандартное смещение UTC, так как нет другого соглашения для обнаружения стандартного смещения.

tzinfo.utcoffset(dt):

Метод tzinfo.utcoffset(dt) возвращает смещение местного времени от UTC, как объект datetime.timedelta().

  • Если местное время смещено к востоку от UTC, то значение должно быть положительное.
  • Если местное время смещено к западу от UTC, то значение должно быть отрицательно.

Метод представляет общее смещение от UTC. Например, если объект tzinfo представляет настройки часового пояса и DST, то метод tzinfo.utcoffset() должен вернуть их сумму. Если смещение UTC неизвестно, то должен вернуть None. В противном случае возвращаемое значение должно быть объектом timedelta строго между -timedelta(hours=24) и timedelta(hours=24). То есть величина смещения должна быть меньше одного дня.

Если tzinfo.utcoffset() не возвращает None, то метод tzinfo.dst() также не должен возвращать None. Реализация по умолчанию tzinfo.utcoffset() вызывает исключение NotImplementedError.

tzinfo.dst(dt):

Метод tzinfo.utcoffset(dt) возвращает корректировку перехода на летнее время DST как объект datetime.timedelta() или None, если информация о DST неизвестна.

Необходимо вернуть timedelta(0), если летнее время не действует. Если действует DST, вернуть смещение как объект datetime.timedelta(). Обратите внимание, что смещение DST уже добавлено к смещению UTC, возвращаемому методом tzinfo.utcoffset(), поэтому нет необходимости обращаться к tzinfo.dst(), если вы не заинтересованы в получении информации о DST отдельно.

Например, datetime.timetuple() вызывает метод tzinfo.dst() своего атрибута .tzinfo, чтобы определить, как должен быть установлен флаг tm_isdst, а tzinfo.fromutc() вызывает метод tzinfo.dst() для учета изменений DST при пересечении часовых поясов.

В этом смысле экземпляр tz подкласса datetime.tzinfo(), который моделирует как стандартное, так и дневное время, должен быть согласованным: tz.utcoffset(dt) - tz.dst(dt) должен возвращать один и тот же результат для каждой dt = datetime.datetime() с помощью dt.tzinfo == tz. Для подклассов tzinfo это выражение выдает "стандартное смещение" часового пояса, которое не должно зависеть от даты или времени, а только от географического местоположения.

Реализация dt.astimezone() полагается на это и не может обнаружить нарушения. Ответственность за обеспечение этого лежит на программисте. Если подкласс tzinfo не может этого гарантировать, он может переопределить реализацию метода tzinfo.fromutc() по умолчанию для правильной работы с dt.astimezone().

Большинство реализаций tzinfo.dst(), вероятно, будут выглядеть так:

def dst(self, dt):
    # a fixed-offset class:  doesn't account for DST
    return timedelta(0)

# или

def dst(self, dt):
    # Code to set dston and dstoff to the time zone's DST
    # transition times based on the input dt.year, and expressed
    # in standard local time.

    if dston <= dt.replace(tzinfo=None) < dstoff:
        return timedelta(hours=1)
    else:
        return timedelta(0)

Реализация tzinfo.dst() по умолчанию вызывает исключение NotImplementedError.

tzinfo.tzname(dt):

Метод tzinfo.tzname(dt) возвращает имя часового пояса в виде строки, соответствующее объекту dt = datetime.datetime(). Возвращает None, если имя строки неизвестно. По умолчанию реализация tzinfo.tzname() вызывает исключение NotImplementedError.

Модуль datetime не определяет конкретные именах строк и не требует, чтобы возвращаемое имя означало что-либо конкретное. Например, "GMT", "UTC", "-500", "-5: 00", "EDT", "America/New York" являются действительными ответами.

Обратите внимание, что tzinfo.utcoffset() это метод, а не фиксированная строка, в первую очередь потому, что некоторые подклассы datetime.tzinfo захотят возвращать разные имена в зависимости от конкретного переданного значения dt, особенно если класс tzinfo учитывает дневное время.

tzinfo.fromutc(dt):

Метод tzinfo.fromutc(dt) вызывается из реализации по умолчанию dt.astimezone().

Когда вызывается dt.astimezone(), то dt.tzinfo является self и данные даты и времени dt должны рассматриваться как выражающие время UTC. Цель метода tzinfo.fromutc() - настроить данные даты и времени, возвращая эквивалентное местное времени.

Большинство подклассов datetime.tzinfo должны иметь возможность наследовать реализацию tzinfo.fromutc() по умолчанию без проблем. Метод должен обрабатывать часовые пояса с фиксированным смещением, а так же часовые пояса, учитывающие как стандартное, так и летнее время, причем последнее, даже если времена перехода на летнее время отличаются в разные годы.

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

Реализация метода tzinfo.fromutc() по умолчанию действует так:

def fromutc(self, dt):
    # raise ValueError error if dt.tzinfo is not self
    dtoff = dt.utcoffset()
    dtdst = dt.dst()
    # raise ValueError if dtoff is None or dtdst is None
    delta = dtoff - dtdst  # this is self's standard offset
    if delta:
        dt += delta   # convert to standard local time
        dtdst = dt.dst()
        # raise ValueError if dtdst is None
    if dtdst:
        return dt + dtdst
    else:
        return dt