datetime.now(tz) and datetime(year, month, day, tzinfo=tz) don't have same UTC offset

Viewed 452

Why are the UTC offsets in the 2 datetimes created in the code below different? I'm using pytz==2019.1

from datetime import datetime
import pytz

EASTERN = pytz.timezone('US/Eastern')
dt1 = datetime.now(EASTERN)
dt2 = datetime(2020, 8, 11, 15, 30, tzinfo=EASTERN)
print(f'dt1: {dt1}')
print(f'dt2: {dt2}')

results:

dt1: 2020-08-11 18:35:47.984745-04:00

dt2: 2020-08-11 15:30:00-04:56

The first one shows an UTC offset of -04:00 which is correct (for this time of year), but the 2nd one is giving an UTC offset of -04:56.

How can I declare a datetime with the 2nd method and get the UTC offset from the first method.

2 Answers

The issue with the shown code is that datetime.now(tz) can handle a pytz.timezone as tz argument while the default constructor datetime(y,m,d,...) cannot. In this case, you have to use the localize method of the timezone,

from datetime import datetime
import pytz

EASTERN = pytz.timezone('US/Eastern')
dt1 = datetime.now(EASTERN)
dt2 = EASTERN.localize(datetime(2020, 8, 11, 15, 30))
print(f'dt1: {dt1}')
print(f'dt2: {dt2}')
# prints
# dt1: 2020-08-12 10:07:09.738763-04:00
# dt2: 2020-08-11 15:30:00-04:00

dateutil avoids this issue, more info can be found here. That would make the code work "as it is":

from dateutil.tz import gettz

EASTERN = gettz('US/Eastern')
dt1 = datetime.now(EASTERN)
dt2 = datetime(2020, 8, 11, 15, 30, tzinfo=EASTERN)
print(f'dt1: {dt1}')
print(f'dt2: {dt2}')
# prints e.g.
# dt1: 2020-08-12 10:20:07.796811-04:00
# dt2: 2020-08-11 15:30:00-04:00

In addition pytz is likely to be deprecated with Python 3.9 as you'll have zoneinfo as part of the standard library for these kinds of jobs.

with regards to dt1:

datetime.now() will return the tzinfo from utc. You can see this if you do the following:

>>> repr(EASTERN.fromutc(datetime.now()))
"datetime.datetime(2020, 8, 11, 12, 33, 28, 849873, tzinfo=<DstTzInfo 'US/Eastern' EDT-1 day, 20:00:00 DST>)"

the tz.fromutc() is what datetime.now() does behind the scenes.

with regards to dt2:

if you check the repr of EASTERN you will see the following:

>>> repr(EASTERN)
"<DstTzInfo 'US/Eastern' LMT-1 day, 19:04:00 STD>"
>>> repr(dt2)
"datetime.datetime(2020, 8, 11, 15, 30, tzinfo=<DstTzInfo 'US/Eastern' LMT-1 day, 19:04:00 STD>)"

I'm not very comfortable with timezone differences so I don't want to steer you in the wrong direction, but from what I can tell both of these two options yield the same result:

dt2 = datetime(2020, 8, 11, 15, 30, tzinfo=EASTERN)
dt2_eastern_corrected = EASTERN.fromutc(dt2)
dt3 = datetime(2020, 8, 11, 15, 30)
dt3_eastern_corrected = EASTERN.fromutc(dt3

>>> repr(dt2_eastern_corrected)
"datetime.datetime(2020, 8, 11, 11, 30, tzinfo=<DstTzInfo 'US/Eastern' EDT-1 day, 20:00:00 DST>)"
>>> repr(dt3_eastern_corrected)
"datetime.datetime(2020, 8, 11, 11, 30, tzinfo=<DstTzInfo 'US/Eastern' EDT-1 day, 20:00:00 DST>)"
Related