首页 > 解决方案 > `timedelta` object not accounting for daylight savings

问题描述

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?

标签: pythondatetimetimezonedst

解决方案


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.


推荐阅读