Fixed #18504 -- Computed |naturalday in local time.

This commit is contained in:
Aymeric Augustin 2012-07-19 23:02:22 +02:00
parent 123362dd37
commit 5d560dcb98
2 changed files with 52 additions and 26 deletions

View File

@ -1,6 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import re import re
from datetime import date, datetime, timedelta from datetime import date, datetime
from django import template from django import template
from django.conf import settings from django.conf import settings
@ -143,7 +143,9 @@ def apnumber(value):
return value return value
return (_('one'), _('two'), _('three'), _('four'), _('five'), _('six'), _('seven'), _('eight'), _('nine'))[value-1] return (_('one'), _('two'), _('three'), _('four'), _('five'), _('six'), _('seven'), _('eight'), _('nine'))[value-1]
@register.filter # Perform the comparison in the default time zone when USE_TZ = True
# (unless a specific time zone has been applied with the |timezone filter).
@register.filter(expects_localtime=True)
def naturalday(value, arg=None): def naturalday(value, arg=None):
""" """
For date values that are tomorrow, today or yesterday compared to For date values that are tomorrow, today or yesterday compared to
@ -169,6 +171,8 @@ def naturalday(value, arg=None):
return _('yesterday') return _('yesterday')
return defaultfilters.date(value, arg) return defaultfilters.date(value, arg)
# This filter doesn't require expects_localtime=True because it deals properly
# with both naive and aware datetimes. Therefore avoid the cost of conversion.
@register.filter @register.filter
def naturaltime(value): def naturaltime(value):
""" """

View File

@ -1,13 +1,31 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import datetime import datetime
import new
from django.contrib.humanize.templatetags import humanize
from django.template import Template, Context, defaultfilters from django.template import Template, Context, defaultfilters
from django.test import TestCase from django.test import TestCase
from django.utils import translation, tzinfo from django.test.utils import override_settings
from django.utils.translation import ugettext as _
from django.utils.html import escape from django.utils.html import escape
from django.utils.timezone import utc from django.utils.timezone import utc
from django.utils import translation
from django.utils.translation import ugettext as _
from django.utils import tzinfo
# Mock out datetime in some tests so they don't fail occasionally when they
# run too slow. Use a fixed datetime for datetime.now(). DST change in
# America/Chicago (the default time zone) happened on March 11th in 2012.
now = datetime.datetime(2012, 3, 9, 22, 30)
class MockDateTime(datetime.datetime):
@classmethod
def now(self, tz=None):
if tz is None or tz.utcoffset(now) is None:
return now
else:
# equals now.replace(tzinfo=utc)
return now.replace(tzinfo=tz) + tz.utcoffset(now)
class HumanizeTests(TestCase): class HumanizeTests(TestCase):
@ -109,28 +127,36 @@ class HumanizeTests(TestCase):
self.humanize_tester(test_list, result_list, 'naturalday') self.humanize_tester(test_list, result_list, 'naturalday')
def test_naturalday_tz(self): def test_naturalday_tz(self):
from django.contrib.humanize.templatetags.humanize import naturalday
today = datetime.date.today() today = datetime.date.today()
tz_one = tzinfo.FixedOffset(datetime.timedelta(hours=-12)) tz_one = tzinfo.FixedOffset(datetime.timedelta(hours=-12))
tz_two = tzinfo.FixedOffset(datetime.timedelta(hours=12)) tz_two = tzinfo.FixedOffset(datetime.timedelta(hours=12))
# Can be today or yesterday # Can be today or yesterday
date_one = datetime.datetime(today.year, today.month, today.day, tzinfo=tz_one) date_one = datetime.datetime(today.year, today.month, today.day, tzinfo=tz_one)
naturalday_one = naturalday(date_one) naturalday_one = humanize.naturalday(date_one)
# Can be today or tomorrow # Can be today or tomorrow
date_two = datetime.datetime(today.year, today.month, today.day, tzinfo=tz_two) date_two = datetime.datetime(today.year, today.month, today.day, tzinfo=tz_two)
naturalday_two = naturalday(date_two) naturalday_two = humanize.naturalday(date_two)
# As 24h of difference they will never be the same # As 24h of difference they will never be the same
self.assertNotEqual(naturalday_one, naturalday_two) self.assertNotEqual(naturalday_one, naturalday_two)
def test_naturalday_uses_localtime(self):
# Regression for #18504
# This is 2012-03-08HT19:30:00-06:00 in Ameria/Chicago
dt = datetime.datetime(2012, 3, 9, 1, 30, tzinfo=utc)
orig_humanize_datetime, humanize.datetime = humanize.datetime, MockDateTime
try:
with override_settings(USE_TZ=True):
self.humanize_tester([dt], ['yesterday'], 'naturalday')
finally:
humanize.datetime = orig_humanize_datetime
def test_naturaltime(self): def test_naturaltime(self):
class naive(datetime.tzinfo): class naive(datetime.tzinfo):
def utcoffset(self, dt): def utcoffset(self, dt):
return None return None
# we're going to mock datetime.datetime, so use a fixed datetime
now = datetime.datetime(2011, 8, 15, 1, 23)
test_list = [ test_list = [
now, now,
now - datetime.timedelta(seconds=1), now - datetime.timedelta(seconds=1),
@ -148,6 +174,7 @@ class HumanizeTests(TestCase):
now + datetime.timedelta(hours=1, minutes=30, seconds=30), now + datetime.timedelta(hours=1, minutes=30, seconds=30),
now + datetime.timedelta(hours=23, minutes=50, seconds=50), now + datetime.timedelta(hours=23, minutes=50, seconds=50),
now + datetime.timedelta(days=1), now + datetime.timedelta(days=1),
now + datetime.timedelta(days=2, hours=6),
now + datetime.timedelta(days=500), now + datetime.timedelta(days=500),
now.replace(tzinfo=naive()), now.replace(tzinfo=naive()),
now.replace(tzinfo=utc), now.replace(tzinfo=utc),
@ -169,27 +196,22 @@ class HumanizeTests(TestCase):
'an hour from now', 'an hour from now',
'23 hours from now', '23 hours from now',
'1 day from now', '1 day from now',
'2 days, 6 hours from now',
'1 year, 4 months from now', '1 year, 4 months from now',
'now', 'now',
'now', 'now',
] ]
# Because of the DST change, 2 days and 6 hours after the chosen
# date in naive arithmetic is only 2 days and 5 hours after in
# aware arithmetic.
result_list_with_tz_support = result_list[:]
assert result_list_with_tz_support[-4] == '2 days, 6 hours from now'
result_list_with_tz_support[-4] == '2 days, 5 hours from now'
# mock out datetime so these tests don't fail occasionally when the orig_humanize_datetime, humanize.datetime = humanize.datetime, MockDateTime
# test runs too slow
class MockDateTime(datetime.datetime):
@classmethod
def now(self, tz=None):
if tz is None or tz.utcoffset(now) is None:
return now
else:
# equals now.replace(tzinfo=utc)
return now.replace(tzinfo=tz) + tz.utcoffset(now)
from django.contrib.humanize.templatetags import humanize
orig_humanize_datetime = humanize.datetime
humanize.datetime = MockDateTime
try: try:
self.humanize_tester(test_list, result_list, 'naturaltime') self.humanize_tester(test_list, result_list, 'naturaltime')
with override_settings(USE_TZ=True):
self.humanize_tester(test_list, result_list_with_tz_support, 'naturaltime')
finally: finally:
humanize.datetime = orig_humanize_datetime humanize.datetime = orig_humanize_datetime