"Wrong" time delta calculation in python datetime package
Question:
What value do you expect to be
26-Mar-2023 8AM Amsterdam time
– 25-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
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
What value do you expect to be
26-Mar-2023 8AM Amsterdam time
– 25-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
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