Reputation: 1262
I am trying to get the appropriate time range to query in a database (where datetimes are stored in UTC) in the local timezone of a user. Here's some odd behavior that I'm finding that I don't know how to work around:
import pytz
from datetime import datetime, timedelta
local_tz = pytz.timezone("America/New_York")
utc = pytz.timezone("UTC")
start = local_tz.localize(datetime(2019, 11, 3)) # 2019-11-03 00:00:00-04:00
end = start + timedelta(days=1) # 2019-11-04 00:00:00-04:00
start_utc = utc.normalize(start) # 2019-11-03 04:00:00+00:00
end_utc = utc.normalize(end) # 2019-11-04 04:00:00+00:00
utc.normalize(local_tz.localize(datetime(2019, 11, 4))) # 2019-11-04 05:00:00+00:00
The Daylight savings change is lost somehow when normalizing the end
variable contstructed by adding a timedelta
object to start
. Why could this be happening?
Upvotes: 1
Views: 871
Reputation: 8855
Not sure about the expected behavior on the datetime calculations, but I'll just explain the behavior from shared code samples.
pytz.localize
creates a timezone aware datetime instance. When NY timezone is used to localize
the naive datetime, it assigns the correct timezone, EDT
until November 3rd, and EST
for Nevember 4th and later. Let's exclude timedelta
here:
>>> import pytz
>>> from datetime import datetime, timedelta
>>> tz_ny = pytz.timezone("America/New_York")
>>> tz_ny.localize(datetime(2019, 11, 3))
datetime.datetime(2019, 11, 3, 0, 0, tzinfo=<DstTzInfo 'America/New_York' EDT-1 day, 20:00:00 DST>)
>>> tz_ny.localize(datetime(2019, 11, 4))
datetime.datetime(2019, 11, 4, 0, 0, tzinfo=<DstTzInfo 'America/New_York' EST-1 day, 19:00:00 STD>)
As seen, the DstTzInfo
are different, as one would expect, because NY DST ends on November 3rd.
The shared code sample creates start
by localizing
November 3rd, which assigns EDT
as tzinfo
to the datetime object (using DST). To create end
, timedelta
of 1 day is added to start
, but the tzinfo
of the datetime
object is kept untouched:
>>> start = tz_ny.localize(datetime(2019, 11, 3))
>>> start.tzinfo
<DstTzInfo 'America/New_York' EDT-1 day, 20:00:00 DST>
>>> end = start + timedelta(days=1)
>>> end.tzinfo
<DstTzInfo 'America/New_York' EDT-1 day, 20:00:00 DST>
So end
is datetime
for November 4th, but DstTzInfo
info is still the same as November 3rd. And this is different than localizing
a naive datetime
of November 4th, which doesn't use DST.
Upvotes: 2