Problem outputting date in template as timestamp

709 views
Skip to first unread message

Sean Brant

unread,
Feb 20, 2009, 11:49:56 AM2/20/09
to Django users
I am trying to render a timestamp in my template with date:"U" but the
timestamps are days off. I wrote up a simple test case that fails. Do
you think this is a bug or a user error? I am running rev: 9846.

from django.test import TestCase

class SimpleTest(TestCase):
def test_template_timestamp(self):
from datetime import datetime
from django.template import Context, Template
now = datetime.now()
t = Template('{{ now|date:"U" }}')
c = Context({'now': now})
self.failUnlessEqual(int(now.strftime('%s')), int(t.render
(c)))

Thanks.

garagefan

unread,
Feb 20, 2009, 12:30:22 PM2/20/09
to Django users
server date incorrect?

I asked a similar question a while ago... turns out the server i'm
using from godaddy was set for Arizona and was an additional 20 some
minutes off...

Sean Brant

unread,
Feb 20, 2009, 12:46:39 PM2/20/09
to Django users
> I asked a similar question a while ago... turns out the server i'm
> using from godaddy was set for Arizona and was an additional 20 some
> minutes off...

I checked my server and the timezone is set to US/CENTRAL and my
settings file is set to America/Chicago. So that should be the same.
Plus the time is off by ~14 days.

Jeff FW

unread,
Feb 20, 2009, 6:21:21 PM2/20/09
to Django users
I just tried running the code that the "U" date-formatting parameter
uses, and for me, it was off by about 11.5 days. According to the
documentation, the "U" parameter is not implemented:
http://docs.djangoproject.com/en/dev/ref/templates/builtins/#now

According to this ticket, someone might change the documentation soon,
so you might want to get a word in beforehand:
http://code.djangoproject.com/ticket/9850

I'd do it, but you already have the lovely test case :-)

-Jeff

Karen Tracey

unread,
Feb 20, 2009, 7:45:49 PM2/20/09
to django...@googlegroups.com
On Fri, Feb 20, 2009 at 6:21 PM, Jeff FW <jef...@gmail.com> wrote:

I just tried running the code that the "U" date-formatting parameter
uses, and for me, it was off by about 11.5 days.  According to the
documentation, the "U" parameter is not implemented:
http://docs.djangoproject.com/en/dev/ref/templates/builtins/#now

According to this ticket, someone might change the documentation soon,
so you might want to get a word in beforehand:
http://code.djangoproject.com/ticket/9850

I'd do it, but you already have the lovely test case :-)

Note that test case doesn't work on Windows, where strftime('%s') doesn't work the same as on Unix:

Python 2.6 (r26:66721, Oct  2 2008, 11:35:03) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from datetime import datetime
>>> now = datetime.now()
>>> now.strftime('%s')
''
>>> quit()

So we can't add a testcase that relies on strftime('%s') returning anything in particular.  The Python docs note that anything outside of what is explicitly listed (and %s is not listed) is platform-dependent and may vary unpredictably.

Near as I can tell, also, the "U" date-formatting implementation doesn't work.  It is:

    def U(self):
        "Seconds since the Unix epoch (January 1 1970 00:00:00 GMT)"
        off = self.timezone.utcoffset(self.data)
        return int(time.mktime(self.data.timetuple())) + off.seconds * 60

If you break down what that's doing in a shell, considering an easy time like Jan 1 1970 on the dot in my tz:

>>> import datetime
>>> x = datetime.datetime(1970,1,1)
>>> x.strftime('%s')
'18000'

That makes sense, midnight Jan 1 1970 in my timezone -> 5 hours (18000/60/60) UTC (or is it GMT?).  Whichever, 5 hours sounds right.  But the U formatter comes up with:

>>> from django.utils.dateformat import format
>>> format(x,'U')
u'4122000'

? Not the same.  U up there is method on a DateFormat object initialized with the datetime value:

>>> from django.utils.dateformat import DateFormat
>>> df = DateFormat(x)
>>> df.data
datetime.datetime(1970, 1, 1, 0, 0)
>>> df.timezone
EST

So this is what off winds up being:

>>> off = df.timezone.utcoffset(x)
>>> off
datetime.timedelta(-1, 68400)
>>> off.days
-1
>>> off.seconds
68400

This is negative one day, plus 68,4000 seconds, equivalent to -18,000 seconds, but the subsequent code:

return int(time.mktime(self.data.timetuple())) + off.seconds * 60

doesn't consider the negative days value and just uses the 68,4000 seconds value, so that's a problem.

Furthermore I don't understand why this code multiplies a seconds value by 60.  You divide seconds by 60 to get minutes, and again to get hours, but why would you multiply seconds by 60?  It may be related to the fact that the Ptyhon docs (http://docs.python.org/library/datetime.html#datetime.tzinfo.utcoffset) say the timezone utcoffset is a minutes value, which is a bit confusing since in fact later on it says a timedelta should be returned.  But surely it wouldn't be expected that the seconds value of the returned timedelta would contain a number of minutes?

Finally, in fact, at least on my box, doing any adjustment to the first bit isn't going to result in a value that matches strftime('%s'), because the first part is already what we get with strftime('%s'):

>>> int(time.mktime(x.timetuple()))
18000

So, that may be another problem with the testcase -- the strftime('%s') half is assuming the answer is in UTC while the "U" format seems to be trying to adjust the answer so that it is expressed in local time -- even if that was working properly the answers wouldn't match.  But I'll admit trying to figure out datetimes and timezones, etc. in Python rather makes my head spin so I could be missing something here.

Karen





Malcolm Tredinnick

unread,
Feb 20, 2009, 8:17:55 PM2/20/09
to django...@googlegroups.com
On Fri, 2009-02-20 at 19:45 -0500, Karen Tracey wrote:
[...]

> Near as I can tell, also, the "U" date-formatting implementation
> doesn't work. It is:
>
> def U(self):
> "Seconds since the Unix epoch (January 1 1970 00:00:00 GMT)"
> off = self.timezone.utcoffset(self.data)
> return int(time.mktime(self.data.timetuple())) + off.seconds *
> 60

Wow, that code's really old (like 2005 old). Possibly marked as
unimplemented because Adrian knew it was broken and wanted to fix it, or
something.

[...]


> So this is what off winds up being:
>
> >>> off = df.timezone.utcoffset(x)
> >>> off
> datetime.timedelta(-1, 68400)
> >>> off.days
> -1
> >>> off.seconds
> 68400
>
> This is negative one day, plus 68,4000 seconds, equivalent to -18,000
> seconds, but the subsequent code:
>
> return int(time.mktime(self.data.timetuple())) + off.seconds * 60
>
> doesn't consider the negative days value and just uses the 68,4000
> seconds value, so that's a problem.

That's a lovely trap in the timedelta API that regularly catches people
out (including me, regularly enough, when I forget it happens). When we
were implementing timezone support for syndicated feeds at the
Washington DC sprint last year, I seem to recall it took a large portion
of the time spent on that issue just to get things working for timezones
west of UTC due to that "feature".

If somebody wants to fix this robustly, I'd strongly suggest swiping the
offset computation code from django.utils.feedgenerator. Both the
rfc822_date() and rfc3339_date() functions do the computation correctly.

Regards,
Malcolm

Sean Brant

unread,
Feb 21, 2009, 2:05:53 PM2/21/09
to Django users
Would something like this work?

def U(self):
"Seconds since the Unix epoch (January 1 1970 00:00:00 GMT)"
import calendar
timestamp = calendar.timegm(self.data.timetuple())
return timestamp + time.timezone

Test:
r"""
>>> print dateformat.format(my_birthday, 'U')
404370000
"""

from django.utils import dateformat, translation
import datetime

my_birthday = datetime.datetime(1982, 10, 25)

On Feb 20, 7:17 pm, Malcolm Tredinnick <malc...@pointy-stick.com>
wrote:

Sean Brant

unread,
Feb 21, 2009, 2:07:20 PM2/21/09
to Django users
BTW, That is with a timezone of "America/New_York"
Reply all
Reply to author
Forward
0 new messages