mirror of
https://github.com/django/django.git
synced 2025-04-22 00:04:43 +00:00
ticket #34699. Added a warning about using Trunc functions in a filter when the timezone is not UTC to database-functions. Added tests to confirm that the documentation is correct.
This commit is contained in:
parent
5f0ed95e10
commit
033f11be06
@ -658,6 +658,41 @@ Usage example:
|
||||
2015-06-15 14:30:50.000321
|
||||
2015-06-15 14:40:02.000123
|
||||
|
||||
.. warning::
|
||||
|
||||
Trunc functions, at the database level, return a timezone naive value which
|
||||
is converted to a timezone aware value by the ORM. When you use a Trunc
|
||||
function in a filter you will need to remember that it is a timezone naive
|
||||
value. This can lead to unexpected results if you are using timezones other
|
||||
than UTC. Django will store date/time values in the database in the UTC
|
||||
timezone. The following example demonstrates what happens when using the
|
||||
timezone "Europe/Berlin" and how to adjust for this:
|
||||
|
||||
Filter example using the "Europe/Berlin" timezone.:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> from django.utils import timezone
|
||||
>>> from datetime import datetime
|
||||
>>> from django.db.models.functions import TruncSecond
|
||||
>>> import zoneinfo
|
||||
>>> start = datetime(2015, 6, 15, 14, 30, 50, 321)
|
||||
>>> start = timezone.make_aware(start)
|
||||
>>> exp = Experiment.objects.create(start_datetime=start)
|
||||
>>> find_this_exp = Experiment.objects.annotate(
|
||||
... trunc_start=TruncSecond("start_datetime")
|
||||
... ).filter(trunc_start__lte=start)
|
||||
>>> find_this_exp.count() # We expect to find one result but 0 are found
|
||||
...
|
||||
0
|
||||
>>> start_adjusted = timezone.localtime(start).replace(tzinfo=zoneinfo.ZoneInfo(key='UTC'))
|
||||
>>> find_this_exp_adjusted = Experiment.objects.annotate(
|
||||
... trunc_start=TruncSecond("start_datetime")
|
||||
... ).filter(trunc_start__lte=start_adjusted)
|
||||
>>> find_this_exp.count()
|
||||
...
|
||||
1
|
||||
|
||||
``DateField`` truncation
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -1935,3 +1935,43 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
||||
DTModel.objects.annotate(
|
||||
hour_melb=Trunc("start_time", "hour", tzinfo=melb),
|
||||
).get()
|
||||
|
||||
def test_trunc_in_filter(self):
|
||||
"""
|
||||
ticket #34699. When TruncSecond is used in a filter it can behave unexpectedly
|
||||
because the function at the database level returns a timezone naive value. The
|
||||
documentation at docs/ref/models/database-functions.txt describes the problem
|
||||
and provides a work-around in specific cases. These tests confirm the issue
|
||||
exists and confirm that the work-around performs as described. If these tests
|
||||
fail it could be because functionality has changed in which case the
|
||||
documentation should be updated and the release notes should include information
|
||||
about a potentially breaking change.
|
||||
"""
|
||||
# UTC: No adjustment required to filtering for TruncSecond
|
||||
now = timezone.now()
|
||||
later = now + timedelta(hours=2)
|
||||
non_utc_model = self.create_model(now, later)
|
||||
models_qs = DTModel.objects.annotate(
|
||||
start_trunc=TruncSecond('start_datetime')).filter(id=non_utc_model.id,
|
||||
start_trunc__lte=now)
|
||||
self.assertEqual(models_qs.count(), 1)
|
||||
test_timezones = [
|
||||
zoneinfo.ZoneInfo("Europe/Berlin"),
|
||||
zoneinfo.ZoneInfo("Australia/Melbourne")
|
||||
]
|
||||
for test_tz in test_timezones:
|
||||
with timezone.override(test_tz):
|
||||
now = timezone.now()
|
||||
later = now + timedelta(hours=2)
|
||||
non_utc_model = self.create_model(now, later)
|
||||
models_qs = DTModel.objects.annotate(
|
||||
start_trunc=TruncSecond('start_datetime')).filter(id=non_utc_model.id,
|
||||
start_trunc__lte=now)
|
||||
self.assertNotEqual(models_qs.count(), 1)
|
||||
adjusted_now = timezone.localtime(now).replace(tzinfo=zoneinfo.ZoneInfo(key='UTC'))
|
||||
models_qs = DTModel.objects.annotate(
|
||||
start_trunc=TruncSecond('start_datetime')).filter(id=non_utc_model.id,
|
||||
start_trunc__lte=adjusted_now)
|
||||
self.assertEqual(models_qs.count(), 1)
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user