#34699: Filtering on annotated TruncSecond expression gives unexpected result.
-------------------------------------+-------------------------------------
     Reporter:  Stefan               |                    Owner:  Wes P.
         Type:                       |                   Status:  assigned
  Cleanup/optimization               |
    Component:  Database layer       |                  Version:  dev
  (models, ORM)                      |
     Severity:  Normal               |               Resolution:
     Keywords:                       |             Triage Stage:  Accepted
    Has patch:  1                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  1
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Changes (by Natalia Bidart):

 * cc: Simon Charette, David, Sanders (added)
 * needs_better_patch:  0 => 1
 * version:  4.2 => dev

Comment:

 Thank you Wes for working on this ticket. I think the PR needs some
 adjusting in terms of docs and code formatting, but before getting into
 those, details, I would like to first define a plan in what would desired
 in terms of fixing this ticket.

 To refresh my memory, I have re-read the docs and tried the reproducing
 examples again. I still think there is an unexpected result when calling
 any of the truncation functions (when there is a `TIME_ZONE` defined in
 the Django project). Following the case by case explanation in
 [https://docs.djangoproject.com/en/dev/ref/models/database-
 functions/#trunc these docs]:

 > Trunc takes a single expression, representing a DateField, TimeField, or
 DateTimeField, a kind representing a date or time part, and an
 output_field that’s either DateTimeField(), TimeField(), or DateField().
 It returns a datetime, date, or time depending on output_field, with
 fields up to kind set to their minimum value. If output_field is omitted,
 it will default to the output_field of expression. A tzinfo subclass,
 usually provided by zoneinfo, can be passed to truncate a value in a
 specific timezone.

 To me, the above paragraph does not imply that the `tzinfo` SHOULD be
 passed to get results that make sense in the context of a custom project-
 wide`TIME_ZONE`. Moreover, I have transformed the example that follows
 that paragraph into a test case (PostgreSQL only) and I get test failures
 for all of the `Australia/Melbourne` cases because the truncated date is
 in `UTC`:

 {{{#!python
 class Ticket34699Tests(TestCase):
     def test_docs_example(self):
         self.assertSequenceEqual(DTModel.objects.all(), [])

         dt = datetime.datetime(2015,6, 15, 14, 30, 50, 321,
 zoneinfo.ZoneInfo("UTC"))
         # From the docs: Given the datetime 2015-06-15
 14:30:50.000321+00:00...
         docs_dt = "2015-06-15T14:30:50.000321+00:00"
         self.assertEqual(dt.isoformat(), docs_dt)

         utc = {
             "year": "2015-01-01T00:00:00+00:00",
             "quarter": "2015-04-01T00:00:00+00:00",
             "month": "2015-06-01T00:00:00+00:00",
             "week": "2015-06-15T00:00:00+00:00",
             "day": "2015-06-15T00:00:00+00:00",
             "hour": "2015-06-15T14:00:00+00:00",
             "minute": "2015-06-15T14:30:00+00:00",
             "second": "2015-06-15T14:30:50+00:00",
         }
         melbourne = {
             "year": "2015-01-01T00:00:00+11:00",
             "quarter": "2015-04-01T00:00:00+10:00",
             "month": "2015-06-01T00:00:00+10:00",
             "week": "2015-06-16T00:00:00+10:00",
             "day": "2015-06-16T00:00:00+10:00",
             "hour": "2015-06-16T00:00:00+10:00",
             "minute": "2015-06-16T00:30:00+10:00",
             "second": "2015-06-16T00:30:50+10:00",
         }
         for tz, cases in [("UTC", utc), ("Australia/Melbourne",
 melbourne)]:
             for kind, expected in cases.items():
                 with (self.settings(USE_TZ=True, TIME_ZONE="UTC"),
 self.subTest(kind=kind, tz=tz)):
                     instance = DTModel.objects.create(start_datetime=dt)
                     self.assertEqual(instance.start_datetime.isoformat(),
 docs_dt)

                     result = DTModel.objects.annotate(
                         truncated=Trunc("start_datetime", kind,
 output_field=DateTimeField())
                     ).get()
                     self.assertEqual(result.truncated.isoformat(),
 expected)

                 DTModel.objects.all().delete()
 }}}

 If we are looking for a docs-only fix at this stage, I think we need to do
 a heavier rework of these docs, to be much more upfront about the need to
 pass `tzinfo` to get expected results for any project with a custom
 `TIME_ZONE`. Personally I feel the code should behave differently, but I
 don't see clearly how such fix would look or we would handle the potential
 deprecation/behavior change.

 Simon, David, would you have further thoughts?
-- 
Ticket URL: <https://code.djangoproject.com/ticket/34699#comment:16>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/django-updates/0107019611768af2-1e6903ef-9488-444c-8993-ea27e878edf7-000000%40eu-central-1.amazonses.com.

Reply via email to