I think I have found bug in datetime.timedelta during DST changes (Python 3.9.2)

Hello.

I think, that I have found bug in datetime module in Python 3.9.2.

When I compute difference/timedelta of datetime aware objects of noons nearest after and before so-called dayling saving time, I get exactly 1 day, e.g. 24 hours.

But when I convert them to the same moments with UTC timezone, I get 23 hours.

In my opinion: The first result is bug, the second one is correct.

You can reproduce the alleged bug by this code:
(I use non-standard module dateutil for simple tzinfo creation.)

from datetime import datetime
from dateutil import tz

CET = tz.gettz('Europe/Amsterdam')  # because Guido van Rossum ;-)
UTC = tz.gettz('UTC')

before_CET = datetime(2023, 3, 25, hour=12, tzinfo=CET)
after_CET = datetime(2023, 3, 26, hour=12, tzinfo=CET)

before_UTC = before_CET.astimezone(tz=UTC)
after_UTC = after_CET.astimezone(tz=UTC)

print("All datetime.datetime instances are aware:")
print(f"before_CET.tzinfo={before_CET.tzinfo}")
print("before_CET.tzinfo.utcoffset(before_CET)"
    f"={before_CET.tzinfo.utcoffset(before_CET)}")
print(f"after_CET.tzinfo={after_CET.tzinfo}")
print("after_CET.tzinfo.utcoffset(after_CET)"
    f"={after_CET.tzinfo.utcoffset(after_CET)}")
print(f"before_UTC.tzinfo={before_UTC.tzinfo}")
print("before_UTC.tzinfo.utcoffset(before_UTC)"
    f"={before_UTC.tzinfo.utcoffset(before_UTC)}")
print(f"after_UTC.tzinfo={after_UTC.tzinfo}")
print("after_UTC.tzinfo.utcoffset(after_UTC)"
    f"={after_UTC.tzinfo.utcoffset(after_UTC)}\n")
print("The same aware objects with changed timezone give distinct timedetlas:")
print(f"{after_CET.isoformat(timespec='hours')}"
    f" - {before_CET.isoformat(timespec='hours')} = {after_CET-before_CET}")
print(f"{after_UTC.isoformat(timespec='hours')}"
    f" - {before_UTC.isoformat(timespec='hours')} = {after_UTC-before_UTC}")

Please, is this real bug or strange intentional feature of the module?

You could argue that the behaviour is incorrect, but it is documented. Search this page for ‘subtract’ (sorry, I don’t think this is deep-linkable): datetime — Basic date and time types — Python 3.11.3 documentation

“”"
If both are naive, or both are aware and have the same tzinfo attribute, the tzinfo attributes are ignored, and the result is a timedelta object t such that datetime2 + t == datetime1. No time zone adjustments are done in this case.
“”"

Personally, I think this isn’t unreasonable behaviour. How long is it from today at noon until tomorrow at noon? One day. If you want to know the exact number of seconds that will elapse, you can do as you’ve done in this demo and convert both to UTC, which then makes them points on a timeline rather than points on a calendar.

This is yet another demonstration of how, no matter what you do, Daylight Saving Time is a mess.

1 Like

For more details about how dateime arithmetic works, see Semantics of datetime arithmetic.

This is indeed the intended behavior, despite the fact that it is counter-intuitive for many people.

1 Like

Sorry for bumping this old post, but I think it’s importent that this quirk would be addressed more explicitly by the language/documentation (It’s not obvious for someone).

I would say it’s that considering DST for such cases would be the more intuitive behavior. If I ask long is it from today at noon until tomorrow at noon, and I receive a timedelta of (236060) seconds it would be a more correct and less ambiguate answer.

I could concede to the opposite point if it wasn’t for the fact that the timedelta object defines a day as exactly 24 hours (which is how it calculates timedelta.total_seconds()). Because of this, I think there can be only one correct answer in this case.

That being said, can we do something about this? Obviously it would not be backwards compatible, so I assume we can’t just change the semantics. But maybe we could implement one of the following solutions?

  1. Introduce alternative methods (datetime.add, datetime.subtract) which always consider timezones
  2. Mention this use-case as a warning in the documentation. Currently, this behavior IS documented, but only as a side note. I think we should add a warning for users who may not fully realize what this means for DSTs and such. We can also add an example code showing the difference, and a suggestion to convert the datetimes to UTC for users who don’t want the current behavior.