I assume you have these questions:
- why does the first function work for UTC timezone?
- why does it fail for
'US/Eastern'
timezone (DstTzInfo
instance)? - why does the second function work for all provided examples?
The first function is incorrect because it uses d.replace(tzinfo=dsttzinfo_instance)
instead of dsttzinfo_instance.localize(d)
.
The second function is correct most of the time except during ambiguous or non-existing times e.g., during DST transitions — you can change the behaviour by passing is_dst
parameter to .localize()
: False
(default)/True
/None
(raise an exception).
The first function works for UTC timezone because it has a fixed utc offset (zero) for any date. Other timezones such as America/New_York
may have different utc offsets at different times (Daylight saving time, war time, any time that some local politician might think is a good idea — it can be anything — the tz database works in most cases). To implement tzinfo.utcoffset(dt)
, tzinfo.tzname(dt)
, tzinfo.dst(dt)
methods pytz
uses a collection of DstTzInfo
instances each with a different set of (_tzname, _utcoffset, _dst)
attributes. Given dt
(date/time) and is_dst
, .localize()
method chooses an appropriate (in most cases but not always) DstTzInfo
instance from the collection. pytz.timezone('America/New_York')
returns a DstTzInfo
instance with (_tzname, _utcoffset, _dst)
attributes that correspond to some undocumented moment in time (different pytz
versions may return different values — the current version may return tzinfo
instance that corresponds to the earliest date for which zoneinfo is available — you don’t want this value most of the time: I think the motivation behind the choice of the default value is to highlight the error (passing pytz.timezone
to datetime
constructor or .replace()
method).
To summarize: .localize()
selects appropriate utcoffset, tzname, dst values, .replace()
uses the default (inappropriate) value. UTC has only one set of utcoffset, tzname, dst therefore the default value may be used and .replace()
method works with UTC timezone. You need to pass a datetime object and is_dst
parameter to select appropriate values for other timezones such as 'America/New_York'
.
In principle, pytz
could have called localize()
method to implement utcoffset()
, tzname()
, dst()
methods even if dt.tzinfo == self
: it would make these methods O(log n) in time where n
is number of intervals with different (utcoffset, tzname, dst) values but datetime
constructor and .replace()
would work as is i.e., the explicit localize()
call would be necessary only to pass is_dst
.