when does `datetime.now(pytz_timezone)` fail?
When does
datetime.now(pytz_timezone)
fail?
As far as I can tell, there are no scenarios where it could fail. datetime.now
invokes the fromutc
function on the tzinfo
instance passed in the parameter. All conversions from UTC to local time are unambiguous, so there are no opportunities for failure.
Also, the original code does not even work.
d = est.normalize(EST)
This would appear to pass a string as the only parameter to normalize
, which is intended to take a datetime
. This gives:
AttributeError: 'str' object has no attribute 'tzinfo'
I believe they meant to write:
d = est.normalize(d.astimezone(est))
That said, I don't think the verbosity of their code adds much value. As you noted, it's just as easy to do this in a single step:
d = datetime.now(est)
Looking at the cpython source code for datetime.now
, I can see that when a tzinfo
object is provided, it calls the fromutc
method on that object.
if (self != NULL && tz != Py_None) {
/* Convert UTC to tzinfo's zone. */
PyObject *temp = self;
self = _PyObject_CallMethodId(tz, &PyId_fromutc, "O", self);
Py_DECREF(temp);
}
Then, in the pytz source, I see that the fromutc
method is implemented differently depending on whether the zone is pytz.UTC
, or an instance of StaticTzInfo
, or DstTzInfo
. In all three cases, the transformation from the input UTC value to the target time zone is unambiguous. Here is the DstTzInfo
implementation, which is the more complex of the three:
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])
This would appear to find the transition from _utc_transition_times
of the time zone, then apply it to the returned datetime
. There are no ambiguities in this direction, so the results will be equivalent.
Also worth noting, in the datetime
docs it says that datetime.now
is equivalent to calling:
tz.fromutc(datetime.utcnow().replace(tzinfo=tz))
Given the source of fromutc
in pytz I showed earlier, I'm not sure that this is any different than just:
tz.fromutc(datetime.utcnow())
But in either case, I don't think localize
and normalize
are necessary.