#30575: Union of TruncBase annotations with different tzinfo apply 
`convert_value`
of last tzinfo.
-------------------------------------+-------------------------------------
     Reporter:  Jurgis Pralgauskis   |                    Owner:  nobody
         Type:  Bug                  |                   Status:  new
    Component:  Database layer       |                  Version:  master
  (models, ORM)                      |
     Severity:  Normal               |               Resolution:
     Keywords:  timezone,            |             Triage Stage:
  TruncBase, Union, PostgreSQL       |  Unreviewed
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Changes (by Jurgis Pralgauskis):

 * status:  closed => new
 * resolution:  worksforme =>


Old description:

> {{{#!python
> class Message(models.Model):
>     timestamp = models.DateTimeField(auto_now=True) # PostgreSQL,
> USE_TZ=True
>     msg = models.CharField(max_length=254)
>     timezone = models.CharField(max_length=64, default="UTC",
> help_text="pytz name")
> }}}
>
> {{{#!python
>     def test_demo_bug(self):
>         from ..models import Message
>         import pytz
>         from django.db.models.functions import Trunc, TruncSecond
>         import mock
>         from django.utils import timezone
>
>         with patch.object(timezone, 'now',
> return_value=pytz.utc.localize(datetime(2017, 1, 1, 0, 0))):
>
>             # we have info
>             Message.objects.create( msg = "bar", timezone = 'UTC')
>             Message.objects.create( msg = "foo", timezone =
> 'America/Los_Angeles')
>

>             # we want to get it with "localized" timestamps:
>             qs = Message.objects.all()
>
>             partitions = []  # partition by timezones
>             for tzname in ['UTC', 'America/Los_Angeles']:
>                 tz = pytz.timezone(tzname)
>                 _qs = qs.filter(timezone=tzname)
>                 _qs =
> _qs.annotate(trunc_local_time=TruncSecond('timestamp', tzinfo=tz)) #
> could be Trunc_anything_
>                 partitions.append(_qs)
>
>             qs1, qs2 = partitions
>
>             result = list(qs1.union(qs2))
>
>             for x in result:
>                 tz = pytz.timezone(x.timezone)
>                 print(x.msg)
>                 x.timestamp_trunc = x.timestamp.replace(microsecond=0)
>                 x.expected = x.timestamp_trunc.astimezone(tz)  # this is
> the working way  (but I wanted to get localization in DB layer)
>                 assert x.trunc_local_time == x.expected,  "Error (msg:
> '{x.msg}'): {x.trunc_local_time} != {x.expected}".format(x=x)
>
> }}}
>
> {{{
> Failure
> Traceback (most recent call last):
>   File "/home/jurgis/dev/new/tableair/sync_tableair-
> cloud/ta/api/booking/tests/test_endpoints.py", line 1107, in
> test_demo_bug
>     assert x.trunc_local_time == x.expected,  "Error (msg: '{x.msg}'):
> {x.trunc_local_time} != {x.expected}".format(x=x)
> AssertionError: Error (msg: 'bla'): 2016-12-31 16:00:00+00:00 !=
> 2017-01-01 00:00:00+00:00}}}

New description:

 {{{#!python
 class Message(models.Model):
     timestamp = models.DateTimeField(auto_now=True) # PostgreSQL,
 USE_TZ=True
     msg = models.CharField(max_length=254)
     timezone = models.CharField(max_length=64, default="UTC",
 help_text="pytz name")
 }}}

 {{{#!python
     def test_demo_bug(self):
         from ..models import Message
         import pytz
         from django.db.models.functions import Trunc, TruncSecond
         import mock
         from django.utils import timezone

         with patch.object(timezone, 'now',
 return_value=pytz.utc.localize(datetime(2017, 1, 1, 0, 0))):

             # we have info
             Message.objects.create( msg = "bar", timezone = 'UTC')
             Message.objects.create( msg = "foo", timezone =
 'America/Los_Angeles')


             # we want to get it with "localized" timestamps:
             qs = Message.objects.all()

             partitions = []  # partition by timezones
             for tzname in ['UTC', 'America/Los_Angeles']:
                 tz = pytz.timezone(tzname)
                 _qs = qs.filter(timezone=tzname)  # was but in test
 hardcoded "UTC" instead of `tzname`
                 _qs =
 _qs.annotate(trunc_local_time=TruncSecond('timestamp', tzinfo=tz)) # could
 be Trunc_anything_
                 partitions.append(_qs)

             qs1, qs2 = partitions

             result = list(qs1.union(qs2))

             for x in result:
                 tz = pytz.timezone(x.timezone)
                 print(x.msg)
                 x.timestamp_trunc = x.timestamp.replace(microsecond=0)
                 x.expected = x.timestamp_trunc.astimezone(tz)  # this is
 the working way  (but I wanted to get localization in DB layer)
                 assert x.trunc_local_time == x.expected,  "Error (msg:
 '{x.msg}'): {x.trunc_local_time} != {x.expected}".format(x=x)

 }}}

 Problem - different TZ offsets
 {{{
 Failure
 Traceback (most recent call last):
   File "/home/jurgis/dev/new/tableair/sync_tableair-
 cloud/ta/api/bookables/tests/test_endpoints.py", line 689, in
 test_demo_bug
     assert x.trunc_local_time == x.expected,  "Error (msg: '{x.msg}'):
 {x.trunc_local_time} != {x.expected}".format(x=x)
 AssertionError: Error (msg: 'foo'): 2016-12-31 16:00:00+00:00 !=
 2016-12-31 16:00:00-08:00
 }}}

--

Comment:

 Sorry, was a bug in my test, and failure didn't show up  (probalby copied
 wrong revision here)..

 The SQL is generated OK, but imo, the problem is that
 `TruncBase#convert_value` has the lines
 {{{
                 value = value.replace(tzinfo=None)
                 value = timezone.make_aware(value, self.tzinfo)
 }}}

 they apply the same `tzinfo` to all (union'ed) rows (that come from
 different timezones)

-- 
Ticket URL: <https://code.djangoproject.com/ticket/30575#comment:7>
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 post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/061.231a829560e32e783dd905f763c38abf%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to