Opened 19 years ago
Closed 18 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)
Change History (9)
comment:1 by , 19 years ago
comment:2 by , 19 years ago
| Has patch: | set | 
|---|---|
| Keywords: | datetime added | 
| Needs tests: | set | 
| Triage Stage: | Unreviewed → Accepted | 
| Version: | 0.95 | 
Can we get some tests for this please?
comment:3 by , 19 years ago
| Keywords: | Date Filter added | 
|---|---|
| Patch needs improvement: | set | 
| Triage Stage: | Accepted → Design 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 , 19 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 >
comment:5 by , 19 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 , 19 years ago
| Attachment: | tmp_fix.diff added | 
|---|
comment:6 by , 19 years ago
| Cc: | added | 
|---|
comment:7 by , 18 years ago
| Resolution: | → fixed | 
|---|---|
| Status: | new → closed | 
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: