"Wrong" time delta calculation in python datetime package

Question:

What value do you expect to be
26-Mar-2023 8AM Amsterdam time25-Mar-2023 8AM Amsterdam time?
Day-light-saving is on since 26-Mar-2023 in Amsterdam, so 25-Mar is in UTC+1 and 26-Mar is in UTC+2, intuitively either 23 hours or 25 hours, but not 24 hours because of the DST shift…

But the below calculation shows 24 hours, why is that?

My python version is 3.9.6

from datetime import datetime, timedelta
import pendulum

p1 = datetime(year=2023, month=3, day=25, hour=8, minute=0, tzinfo=pendulum.timezone('Europe/Amsterdam')) # No DST
p2 = datetime(year=2023, month=3, day=26, hour=8, minute=0,  tzinfo=pendulum.timezone('Europe/Amsterdam')) # DST

print((p2 - p1).total_seconds()/3600) 

I tried to use pytz or pendulum, results are the same, 24 hours.

I convert those two time points to UTC, or just use the unix timestamp, I got 23 hours, as I expected.

(p2.astimezone(pendulum.timezone('UTC')) -  p1.astimezone(pendulum.timezone('UTC'))).total_seconds()/3600

Or

(p2.timestamp() - p1.timestamp())/3600
Asked By: D. Zhai

||

Answers:

Specifying tzinfo doesn’t correctly create the timezone-aware datetime object for timezones that have daylight-saving transformationsSee docs. Printing the repr of your p1 and p2 shows that both p1 and p2 have the same timezone:

print(repr(p1))
# datetime.datetime(2023, 3, 25, 8, 0, tzinfo=<DstTzInfo 'Europe/Amsterdam' LMT+0:18:00 STD>)
 
print(repr(p2))
# datetime.datetime(2023, 3, 26, 8, 0, tzinfo=<DstTzInfo 'Europe/Amsterdam' LMT+0:18:00 STD>)

You need to pass the timezone-unaware datetime object through timezone.localize.

import pytz

tz = pytz.timezone('Europe/Amsterdam')

u1 = datetime(year=2023, month=3, day=25, hour=8, minute=0)
u2 = datetime(year=2023, month=3, day=26, hour=8, minute=0)

a1 = tz.localize(u1)
a2 = tz.localize(u2)

print(repr(a1))
# datetime.datetime(2023, 3, 25, 8, 0, tzinfo=<DstTzInfo 'Europe/Amsterdam' CET+1:00:00 STD>)

print(repr(a2))
# datetime.datetime(2023, 3, 26, 8, 0, tzinfo=<DstTzInfo 'Europe/Amsterdam' CEST+2:00:00 DST>)

print((a2 - a1).total_seconds() / 3600)
# 23.0

Alternatively, create pendulum.datetime objects:

p1 = pendulum.datetime(year=2023, month=3, day=25, hour=8, minute=0, tz=pendulum.timezone('Europe/Amsterdam')) # No DST
p2 = pendulum.datetime(year=2023, month=3, day=26, hour=8, minute=0,  tz=pendulum.timezone('Europe/Amsterdam')) # DST

print((p2 - p1).total_seconds()/3600)
# 23.0
Answered By: Pranav Hosangadi
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.