Когда выполняется `datetime.now(pytz_timezone)` fail?

delorean docs показать этот способ, чтобы получить текущее время в заданном часовом поясе с помощью datetime:

from datetime import datetime
from pytz import timezone

EST = "US/Eastern"
UTC = "UTC"

d = datetime.utcnow()
utc = timezone(UTC)
est = timezone(EST)
d = utc.localize(d)
d = est.normalize(EST)

и сравните его с кодом на основе delorian:

from delorean import Delorean

EST = "US/Eastern"

d = Delorean(timezone=EST)

Я считаю, пример datetime должен быть записан как:

from datetime import datetime
import pytz

eastern_timezone = pytz.timezone("US/Eastern")
d = datetime.now(eastern_timezone)

что является более кратким.

Есть ли случаи, когда последний пример кода не работает, пока первый продолжает работать?


Обновление: текущий пример:

from datetime import datetime
import pytz

d = datetime.utcnow()
d = pytz.utc.localize(d)

est = pytz.timezone('US/Eastern')
d = est.normalize(d)
return d

который все еще слишком многословен.

Столбец вопроса: вам нужна явная обратная связь через utc и tz.normalize() или вы можете использовать datetime.now(tz) вместо этого?

Ответ 1

Когда выполняется datetime.now(pytz_timezone)?

Насколько я могу судить, сценариев, где он может потерпеть неудачу, не существует. datetime.now вызывает функцию fromutc на экземпляре tzinfo, переданном в параметре. Все конверсии с UTC по местному времени однозначны, поэтому нет возможности для отказа.

Кроме того, исходный код даже не работает.

d = est.normalize(EST)

Кажется, что строка передается как единственный параметр normalize, который предназначен для принятия datetime. Это дает:

AttributeError: 'str' object has no attribute 'tzinfo'

Я думаю, они хотели написать:

d = est.normalize(d.astimezone(est))

Тем не менее, я не думаю, что многословие их кода добавляет большую ценность. Как вы отметили, так же легко сделать это за один шаг:

d = datetime.now(est)

Глядя на исходный код cpython для datetime.now, я вижу, что когда объект tzinfo предоставляется, он вызывает fromutc на этом объекте.

if (self != NULL && tz != Py_None) {
    /* Convert UTC to tzinfo zone. */
    PyObject *temp = self;

    self = _PyObject_CallMethodId(tz, &PyId_fromutc, "O", self);
    Py_DECREF(temp);
}

Затем в источнике pytz я вижу, что метод fromutc реализуется по-разному в зависимости от того, является ли зона pytz.UTC или экземпляром StaticTzInfo или DstTzInfo. Во всех трех случаях преобразование из входного значения UTC в целевой часовой пояс однозначно. Вот реализация DstTzInfo, которая является более сложной из трех:

def fromutc(self, dt):
    '''See datetime.tzinfo.fromutc'''
    if (dt.tzinfo is not None
        and getattr(dt.tzinfo, '_tzinfos', None) is not self._tzinfos):
        raise ValueError('fromutc: dt.tzinfo is not self')
    dt = dt.replace(tzinfo=None)
    idx = max(0, bisect_right(self._utc_transition_times, dt) - 1)
    inf = self._transition_info[idx]
    return (dt + inf[0]).replace(tzinfo=self._tzinfos[inf])

Казалось бы, это найти переход от _utc_transition_times часового пояса, а затем применить его к возвращенному datetime. В этом направлении нет двусмысленностей, поэтому результаты будут эквивалентны.

Также стоит отметить, что в datetime docs говорится, что datetime.now эквивалентно вызову:

tz.fromutc(datetime.utcnow().replace(tzinfo=tz))

Учитывая источник fromutc в pytz, который я показал ранее, я не уверен, что это не что иное, как просто:

tz.fromutc(datetime.utcnow())

Но в любом случае я не думаю, что localize и normalize необходимы.