Opened 18 years ago

Closed 17 years ago

#2633 closed defect (fixed)

django.utils.dateformat.DateFormat.O() returns wrong value

Reported by: mukappa Owned by: nobody
Component: Core (Other) Version: dev
Severity: normal Keywords: datetime Date Filter
Cc: wbyoung@… Triage Stage: Design decision needed
Has patch: yes Needs documentation: no
Needs tests: yes Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

from django.utils.dateformat

    def O(self):
        "Difference to Greenwich time in hours; e.g. '+0200'"
        tz = self.timezone.utcoffset(self.data)
        return "%+03d%02d" % (tz.seconds // 3600, (tz.seconds // 60) % 60)

This is wrong when tz.days == -1

I think this modified method should work

    def O(self):
        "Difference to Greenwich time in hours; e.g. '+0200'"
        tz = self.timezone.utcoffset(self.data)
        seconds = (tz.days * 24 * 60 * 60) + tz.seconds
        return "%+03d%02d" % (seconds // 3600, (seconds // 60) % 60)

This session demonstrates the bug. df.O() should output -0500 not +1900

mukappa@xxxx 0 /home/mukappa/src/mysite
[1005]$ ./manage.py shell
Python 2.3.4 (#1, Feb 22 2005, 04:09:37) 
[GCC 3.4.3 20041212 (Red Hat 3.4.3-9.EL4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from datetime import datetime
>>> from django.utils.dateformat import DateFormat
>>> dt = datetime.now()
>>> dt
datetime.datetime(2006, 8, 30, 20, 48, 14, 86518)
>>> df = DateFormat(dt)
>>> df.O()
'+1900'
>>> df.r()
'Wed, 30 Aug 2006 20:48:14 +1900'
>>> 

mukappa@xxxx 0 /home/mukappa/src/mysite
[1005]$ date -R
Wed, 30 Aug 2006 20:49:02 -0500

Attachments (2)

tzinfo.patch (656 bytes ) - added by anonymous 17 years ago.
Patch for django/utils/tzinfo.py
tmp_fix.diff (1.5 KB ) - added by wbyoung@… 17 years ago.

Download all attachments as: .zip

Change History (9)

comment:1 by bahamut@…, 18 years ago

This bug should be marked as critical, it will break PostgreSQL 8.1.3 and above when the SET TIME ZONE query is ran.

From my PostgreSQL log:

ERROR:  time zone displacement out of range: "2006-09-10 13:18:09.079872+20:00"

comment:2 by Simon G. <dev@…>, 17 years ago

Has patch: set
Keywords: datetime added
Needs tests: set
Triage Stage: UnreviewedAccepted
Version: 0.95

Can we get some tests for this please?

comment:3 by marcdm@…, 17 years ago

Keywords: Date Filter added
Patch needs improvement: set
Triage Stage: AcceptedDesign decision needed

Actually, I was just looking at this one. The problem here is that django.utils.tzinfo.Localtime.utcoffset uses a negative value for seconds when creating the timedelta object.

here in file django/utils/tzinfo.py line 33:

    def utcoffset(self, dt):
        if self._isdst(dt):
            return timedelta(0,seconds=-time.altzone)
        else:
            return timedelta(seconds=-time.timezone)

According to http://docs.python.org/lib/datetime-timedelta.html, negative values for seconds/microseconds when initializing timedelta will yield a -1 day as well. This is because the object is not designed to return negative values for the seconds attribute.

The solution I believe is to do a summation of tz.days*24*3600 + tz.seconds. That should yield the correct value for the number of seconds in the UTC offset.

I modified django/utils/dateformat.py from :
lines 160 - 163

    def O(self):
        "Difference to Greenwich time in hours; e.g. '+0200'"
        tz = self.timezone.utcoffset(self.data)
        return "%+03d%02d" % (tz.seconds // 3600, (tz.seconds // 60) % 60)

to the following :

    def O(self):
        "Difference to Greenwich time in hours; e.g. '+0200'"
        tz = self.timezone.utcoffset(self.data)
        seconds = tz.days*24*3600 + tz.seconds
        return "%+03d%02d" % (seconds // 3600, (seconds // 60) % 60)

But this is just a temporary fix. A more permanent fix I feel, would be to replace the use of the timedelta object in tzinfo.py and use a generic object that has a seconds attribute that gives the proper value in positive or negative.

comment:4 by marcdm@…, 17 years ago

Version: SVN

a more permanent fix. I not sure if it's flawed because the new timedelta object doesn't entirely represent the old one. So maybe it needs to use the getattribute method instead of getattr

Patch below :

for the file trunk/django/utils/tzinfo.py

4c4,21
< from datetime import timedelta, tzinfo
---
> from datetime import timedelta as real_timedelta, tzinfo
> 
> class timedelta(object) :
>     def __init__(self,*args,**kwargs) :
>         self.__td = real_timedelta(*args,**kwargs)
>         self.days = 0
>         
>     def __total_seconds(self) :
>         return self.__td.days*24*3600 + self.__td.seconds
> 
>     seconds = property(fget=__total_seconds, doc= 'total seconds for time difference')
>     def __getattr__(self, key) :
>         if key in ['days','seconds'] :
>             ret = getattr(self,key)
>         else :
>             ret = getattr(self.__td,key)
>         return ret
>         

by anonymous, 17 years ago

Attachment: tzinfo.patch added

Patch for django/utils/tzinfo.py

comment:5 by wbyoung@…, 17 years ago

I just ran across this bug myself. It'd be nice to see it fixed even if it's just marked as a HACK for now. It should at least work properly. I'm going to attach a patch that just fixes the problem rather than creating a new timedelta object.

by wbyoung@…, 17 years ago

Attachment: tmp_fix.diff added

comment:6 by wbyoung@…, 17 years ago

Cc: wbyoung@… added

comment:7 by Malcolm Tredinnick, 17 years ago

Resolution: fixed
Status: newclosed

(In [6358]) Fixed #2633 -- Fixed timezone computation in O() format function, using fix from [6300].

Note: See TracTickets for help on using tickets.
Back to Top