{{{
import zoneinfo
import datetime as _dt
from django.utils.timesince import timesince
import django.utils.timezone as _timezone
timesince(_timezone.now().astimezone(zoneinfo.ZoneInfo(key='Europe/Berlin'))-_dt.timedelta(seconds=1))
>>> '-1\xa0Year, 11\xa0Months'
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/34483>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
* cc: GianpaoloBranca (added)
* severity: Normal => Release blocker
* stage: Unreviewed => Accepted
Comment:
Thanks for the report.
Regression in 8d67e16493c903adc9d049141028bc0fff43f8c8.
Reproduced at 3b4728310a7a64f8fcc548163b0aa5f98a5c78f5.
--
Ticket URL: <https://code.djangoproject.com/ticket/34483#comment:1>
* cc: Natalia Bidart (added)
--
Ticket URL: <https://code.djangoproject.com/ticket/34483#comment:2>
* owner: nobody => Natalia Bidart
* status: new => assigned
--
Ticket URL: <https://code.djangoproject.com/ticket/34483#comment:3>
* has_patch: 0 => 1
--
Ticket URL: <https://code.djangoproject.com/ticket/34483#comment:4>
* needs_better_patch: 0 => 1
* needs_tests: 0 => 1
--
Ticket URL: <https://code.djangoproject.com/ticket/34483#comment:5>
* needs_better_patch: 1 => 0
* needs_tests: 1 => 0
* stage: Accepted => Ready for checkin
--
Ticket URL: <https://code.djangoproject.com/ticket/34483#comment:6>
* status: assigned => closed
* resolution: => fixed
Comment:
In [changeset:"813015d67e2557fa859a07930a9becec4e5f64a0" 813015d]:
{{{
#!CommitTicketReference repository=""
revision="813015d67e2557fa859a07930a9becec4e5f64a0"
Fixed #34483 -- Fixed timesince()/timeuntil() with timezone-aware dates
and interval less than 1 day.
Regression in 8d67e16493c903adc9d049141028bc0fff43f8c8.
Thanks Lorenzo Peña for the report.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/34483#comment:7>
Comment (by Natalia <124304+nessita@…>):
In [changeset:"a3c14ea61b7b0e91ff646dfe75850d918ef7e478" a3c14ea]:
{{{
#!CommitTicketReference repository=""
revision="a3c14ea61b7b0e91ff646dfe75850d918ef7e478"
[4.2.x] Fixed #34483 -- Fixed timesince()/timeuntil() with timezone-aware
dates and interval less than 1 day.
Regression in 8d67e16493c903adc9d049141028bc0fff43f8c8.
Thanks Lorenzo Peña for the report.
Backport of 813015d67e2557fa859a07930a9becec4e5f64a0 from main
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/34483#comment:8>
* status: closed => new
* has_patch: 1 => 0
* resolution: fixed =>
* stage: Ready for checkin => Accepted
Comment:
It seems that there is still an issue when a time difference is less than
1 day but days are different, see [https://djangoci.com/job/django-
main/2424/ logs].
--
Ticket URL: <https://code.djangoproject.com/ticket/34483#comment:9>
* status: new => assigned
--
Ticket URL: <https://code.djangoproject.com/ticket/34483#comment:10>
Comment (by GitHub <noreply@…>):
In [changeset:"cd464fbc3ab5c3eccd9d026607341bfe859f10aa" cd464fbc]:
{{{
#!CommitTicketReference repository=""
revision="cd464fbc3ab5c3eccd9d026607341bfe859f10aa"
[4.2.x] Refs #34483 -- Fixed utils_tests.test_timesince crash on Python
3.8.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/34483#comment:11>
Comment (by Mariusz Felisiak):
It seems that `now` must be in the same time zone (as you suggested at the
beginning), however we also have to change time zone when `now` is passed,
e.g.
{{{#!diff
diff --git a/django/utils/timesince.py b/django/utils/timesince.py
index 94ba24d48a..9b9dd45af9 100644
--- a/django/utils/timesince.py
+++ b/django/utils/timesince.py
@@ -63,7 +63,10 @@ def timesince(d, now=None, reversed=False,
time_strings=None, depth=2):
if now and not isinstance(now, datetime.datetime):
now = datetime.datetime(now.year, now.month, now.day)
- now = now or datetime.datetime.now(datetime.timezone.utc if
is_aware(d) else None)
+ if is_aware(d):
+ now = now.astimezone(d.tzinfo) if now else
datetime.datetime.now(tz=d.tzinfo)
+ else:
+ now = now or datetime.datetime.now()
if reversed:
d, now = now, d
@@ -77,8 +80,7 @@ def timesince(d, now=None, reversed=False,
time_strings=None, depth=2):
# Get years and months.
total_months = (now.year - d.year) * 12 + (now.month - d.month)
- time_delta = delta - datetime.timedelta(days=delta.days)
- if d.day > now.day or (d.day == now.day and
time_delta.total_seconds() < 0):
+ if d.day > now.day or (d.day == now.day and d.time() > now.time()):
total_months -= 1
years, months = divmod(total_months, 12)
}}}
see a regression test:
{{{#!diff
diff --git a/tests/utils_tests/test_timesince.py
b/tests/utils_tests/test_timesince.py
index d54fce2be6..e023e95148 100644
--- a/tests/utils_tests/test_timesince.py
+++ b/tests/utils_tests/test_timesince.py
@@ -258,6 +258,24 @@ class TimesinceTests(TestCase):
with self.subTest(value):
self.assertEqual(timesince(value), expected)
+ @requires_tz_support
+ def test_less_than_a_day_cross_day_with_zoneinfo(self):
+ now_with_zoneinfo = timezone.make_aware(
+ datetime.datetime(2023, 4, 14, 1, 30, 30),
+ zoneinfo.ZoneInfo(key="Asia/Kathmandu") # UTC+05:45
+ )
+ now_utc = now_with_zoneinfo.astimezone(datetime.timezone.utc)
+ tests = [
+ (now_with_zoneinfo, "0\xa0minutes"),
+ (now_with_zoneinfo - self.onemicrosecond, "0\xa0minutes"),
+ (now_with_zoneinfo - self.onesecond, "0\xa0minutes"),
+ (now_with_zoneinfo - self.oneminute, "1\xa0minute"),
+ (now_with_zoneinfo - self.onehour, "1\xa0hour"),
+ ]
+ for value, expected in tests:
+ with self.subTest(value):
+ self.assertEqual(timesince(value, now_utc), expected)
+
@requires_tz_support
@override_settings(USE_TZ=True)
}}}
What do you think?
--
Ticket URL: <https://code.djangoproject.com/ticket/34483#comment:12>
Comment (by Mariusz Felisiak):
[https://github.com/django/django/pull/16764 PR]
--
Ticket URL: <https://code.djangoproject.com/ticket/34483#comment:13>
* has_patch: 0 => 1
--
Ticket URL: <https://code.djangoproject.com/ticket/34483#comment:14>
Comment (by GitHub <noreply@…>):
In [changeset:"198a19b692699ad3940373d9ed797fe9155f3f4a" 198a19b6]:
{{{
#!CommitTicketReference repository=""
revision="198a19b692699ad3940373d9ed797fe9155f3f4a"
Refs #34483 -- Fixed timesince()/timeuntil() with timezone-aware dates on
different days and interval less than 1 day.
Follow up to 813015d67e2557fa859a07930a9becec4e5f64a0.
Regression in 8d67e16493c903adc9d049141028bc0fff43f8c8.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/34483#comment:15>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"f75a6977e475cb46184a3c9acef7d8777a650681" f75a6977]:
{{{
#!CommitTicketReference repository=""
revision="f75a6977e475cb46184a3c9acef7d8777a650681"
[4.2.x] Refs #34483 -- Fixed timesince()/timeuntil() with timezone-aware
dates on different days and interval less than 1 day.
Follow up to 813015d67e2557fa859a07930a9becec4e5f64a0.
Regression in 8d67e16493c903adc9d049141028bc0fff43f8c8.
Backport of 198a19b692699ad3940373d9ed797fe9155f3f4a from main
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/34483#comment:16>
* status: assigned => closed
* resolution: => fixed
--
Ticket URL: <https://code.djangoproject.com/ticket/34483#comment:17>