Opened 39 hours ago
Last modified 36 hours ago
#35857 new Bug
django.utils.timesince.timesince incorrectly handles daylight saving time
Reported by: | Frank Sauerburger | Owned by: | |
---|---|---|---|
Component: | Utilities | Version: | 5.0 |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Accepted | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
timesince computes the time elapsed between two datetimes (d and now) and returns it as a human readable string. The function is intended to show the elapsed time from a user perspective (sitting with a stopwatch in front of the computer). timesince relies on Python's timezone arithmetic, however, there are subtle implementation details for intra- and inter-timezone calculations. See
- Discussion about the topic: https://github.com/python/cpython/issues/116111
- Quiz to illustrate subtle examples: https://quiz.sauerburger.com/dxi7m/
Consider the following example around the daylight saving time transition next weekend in Europe. We start at a point in time a, ten minutes later we have a_10, and another 60 minutes later we have a_70.
from zoneinfo import ZoneInfo from datetime import datetime from django.utils.timesince import timesince berlin = ZoneInfo("Europe/Berlin") a = datetime(2024, 10, 27, 2, 55, tzinfo=berlin) a_10 = datetime(2024, 10, 27, 2, 5, fold=1, tzinfo=berlin) a_70 = datetime(2024, 10, 27, 3, 5, tzinfo=berlin) assert a.isoformat() == '2024-10-27T02:55:00+02:00' assert a_10.isoformat() == '2024-10-27T02:05:00+01:00' assert a_70.isoformat() == '2024-10-27T03:05:00+01:00' assert timesince(a, a_10) == '0\xa0minutes' assert timesince(a, a_70) == '10\xa0minutes'
My expectation is that timesince(a, a_10) yields 10 minutes and timesince(a, a_70) yields 70 minutes aligned with what a user with a stopwatch would observe.
I think this can lead to a lot of unexpected behavior in web applications around the DST transition and maybe even exploitable behavior.
Change History (3)
comment:1 by , 36 hours ago
Component: | Uncategorized → Utilities |
---|---|
Triage Stage: | Unreviewed → Accepted |
Type: | Uncategorized → Bug |
comment:2 by , 36 hours ago
Hi Sarah,
yes that's also a good way to demonstrate the issue. In my example, I also wanted to demonstrate that timesince
can confuse the order of events. a_10
happens 10 minutes after event a
, but timesince
returns '0 minutes' as if the order was reversed.
EDIT: Actually, if I'm not mistaken, in your example, there is no difference between
a_10 = datetime(2024, 10, 27, 3, 5, fold=0, tzinfo=berlin) a_70 = datetime(2024, 10, 27, 3, 5, fold=1, tzinfo=berlin)
The overlapping folds are between 2 and 3 am.
If I understood correctly, I think the example was meant to show:
For those not familiar with fold, this is used to disambiguate wall times during a repeated interval. The values 0 and 1 represent, respectively, the earlier and later of the two moments with the same wall time representation.
Looking at the discussion, it appears timesince should convert the dates to UTC before subtracting them.
Linking #34483 as this is slightly related