Fixed #20246 -- Added non-breaking spaces between values an units

This commit is contained in:
Emil Stenström 2013-05-18 13:58:45 +02:00 committed by Claude Paroz
parent caf56ad174
commit 7d77e9786a
9 changed files with 155 additions and 130 deletions

View File

@ -536,6 +536,7 @@ answer newbie questions, and generally made Django that much better:
starrynight <cmorgh@gmail.com>
Vasiliy Stavenko <stavenko@gmail.com>
Thomas Steinacher <http://www.eggdrop.ch/>
Emil Stenström <em@kth.se>
Johan C. Stöver <johan@nilling.nl>
Nowell Strite <http://nowell.strite.org/>
Thomas Stromberg <tstromberg@google.com>

View File

@ -194,17 +194,20 @@ def naturaltime(value):
return _('now')
elif delta.seconds < 60:
return ungettext(
'a second ago', '%(count)s seconds ago', delta.seconds
# Translators: \\u00a0 is non-breaking space
'a second ago', '%(count)s\u00a0seconds ago', delta.seconds
) % {'count': delta.seconds}
elif delta.seconds // 60 < 60:
count = delta.seconds // 60
return ungettext(
'a minute ago', '%(count)s minutes ago', count
# Translators: \\u00a0 is non-breaking space
'a minute ago', '%(count)s\u00a0minutes ago', count
) % {'count': count}
else:
count = delta.seconds // 60 // 60
return ungettext(
'an hour ago', '%(count)s hours ago', count
# Translators: \\u00a0 is non-breaking space
'an hour ago', '%(count)s\u00a0hours ago', count
) % {'count': count}
else:
delta = value - now
@ -216,15 +219,18 @@ def naturaltime(value):
return _('now')
elif delta.seconds < 60:
return ungettext(
'a second from now', '%(count)s seconds from now', delta.seconds
# Translators: \\u00a0 is non-breaking space
'a second from now', '%(count)s\u00a0seconds from now', delta.seconds
) % {'count': delta.seconds}
elif delta.seconds // 60 < 60:
count = delta.seconds // 60
return ungettext(
'a minute from now', '%(count)s minutes from now', count
# Translators: \\u00a0 is non-breaking space
'a minute from now', '%(count)s\u00a0minutes from now', count
) % {'count': count}
else:
count = delta.seconds // 60 // 60
return ungettext(
'an hour from now', '%(count)s hours from now', count
# Translators: \\u00a0 is non-breaking space
'an hour from now', '%(count)s\u00a0hours from now', count
) % {'count': count}

View File

@ -195,22 +195,22 @@ class HumanizeTests(TestCase):
result_list = [
'now',
'a second ago',
'30 seconds ago',
'30\xa0seconds ago',
'a minute ago',
'2 minutes ago',
'2\xa0minutes ago',
'an hour ago',
'23 hours ago',
'1 day ago',
'1 year, 4 months ago',
'23\xa0hours ago',
'1\xa0day ago',
'1\xa0year, 4\xa0months ago',
'a second from now',
'30 seconds from now',
'30\xa0seconds from now',
'a minute from now',
'2 minutes from now',
'2\xa0minutes from now',
'an hour from now',
'23 hours from now',
'1 day from now',
'2 days, 6 hours from now',
'1 year, 4 months from now',
'23\xa0hours from now',
'1\xa0day from now',
'2\xa0days, 6\xa0hours from now',
'1\xa0year, 4\xa0months from now',
'now',
'now',
]
@ -218,8 +218,8 @@ class HumanizeTests(TestCase):
# 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'
assert result_list_with_tz_support[-4] == '2\xa0days, 6\xa0hours from now'
result_list_with_tz_support[-4] == '2\xa0days, 5\xa0hours from now'
orig_humanize_datetime, humanize.datetime = humanize.datetime, MockDateTime
try:

View File

@ -14,7 +14,7 @@ from django.utils import formats
from django.utils.dateformat import format, time_format
from django.utils.encoding import force_text, iri_to_uri
from django.utils.html import (conditional_escape, escapejs, fix_ampersands,
escape, urlize as urlize_impl, linebreaks, strip_tags)
escape, urlize as urlize_impl, linebreaks, strip_tags, avoid_wrapping)
from django.utils.http import urlquote
from django.utils.text import Truncator, wrap, phone2numeric
from django.utils.safestring import mark_safe, SafeData, mark_for_escaping
@ -810,7 +810,8 @@ def filesizeformat(bytes):
try:
bytes = float(bytes)
except (TypeError,ValueError,UnicodeDecodeError):
return ungettext("%(size)d byte", "%(size)d bytes", 0) % {'size': 0}
value = ungettext("%(size)d byte", "%(size)d bytes", 0) % {'size': 0}
return avoid_wrapping(value)
filesize_number_format = lambda value: formats.number_format(round(value, 1), 1)
@ -821,16 +822,19 @@ def filesizeformat(bytes):
PB = 1<<50
if bytes < KB:
return ungettext("%(size)d byte", "%(size)d bytes", bytes) % {'size': bytes}
if bytes < MB:
return ugettext("%s KB") % filesize_number_format(bytes / KB)
if bytes < GB:
return ugettext("%s MB") % filesize_number_format(bytes / MB)
if bytes < TB:
return ugettext("%s GB") % filesize_number_format(bytes / GB)
if bytes < PB:
return ugettext("%s TB") % filesize_number_format(bytes / TB)
return ugettext("%s PB") % filesize_number_format(bytes / PB)
value = ungettext("%(size)d byte", "%(size)d bytes", bytes) % {'size': bytes}
elif bytes < MB:
value = ugettext("%s KB") % filesize_number_format(bytes / KB)
elif bytes < GB:
value = ugettext("%s MB") % filesize_number_format(bytes / MB)
elif bytes < TB:
value = ugettext("%s GB") % filesize_number_format(bytes / GB)
elif bytes < PB:
value = ugettext("%s TB") % filesize_number_format(bytes / TB)
else:
value = ugettext("%s PB") % filesize_number_format(bytes / PB)
return avoid_wrapping(value)
@register.filter(is_safe=False)
def pluralize(value, arg='s'):

View File

@ -281,3 +281,10 @@ def clean_html(text):
text = trailing_empty_content_re.sub('', text)
return text
clean_html = allow_lazy(clean_html, six.text_type)
def avoid_wrapping(value):
"""
Avoid text wrapping in the middle of a phrase by adding non-breaking
spaces where there previously were normal spaces.
"""
return value.replace(" ", "\xa0")

View File

@ -2,6 +2,7 @@ from __future__ import unicode_literals
import datetime
from django.utils.html import avoid_wrapping
from django.utils.timezone import is_aware, utc
from django.utils.translation import ugettext, ungettext_lazy
@ -40,18 +41,18 @@ def timesince(d, now=None, reversed=False):
since = delta.days * 24 * 60 * 60 + delta.seconds
if since <= 0:
# d is in the future compared to now, stop processing.
return ugettext('0 minutes')
return avoid_wrapping(ugettext('0 minutes'))
for i, (seconds, name) in enumerate(chunks):
count = since // seconds
if count != 0:
break
result = name % count
result = avoid_wrapping(name % count)
if i + 1 < len(chunks):
# Now get the second item
seconds2, name2 = chunks[i + 1]
count2 = (since - (seconds * count)) // seconds2
if count2 != 0:
result += ugettext(', ') + name2 % count2
result += ugettext(', ') + avoid_wrapping(name2 % count2)
return result
def timeuntil(d, now=None):

View File

@ -527,24 +527,26 @@ class DefaultFiltersTests(TestCase):
def test_timesince(self):
# real testing is done in timesince.py, where we can provide our own 'now'
# NOTE: \xa0 avoids wrapping between value and unit
self.assertEqual(
timesince_filter(datetime.datetime.now() - datetime.timedelta(1)),
'1 day')
'1\xa0day')
self.assertEqual(
timesince_filter(datetime.datetime(2005, 12, 29),
datetime.datetime(2005, 12, 30)),
'1 day')
'1\xa0day')
def test_timeuntil(self):
# NOTE: \xa0 avoids wrapping between value and unit
self.assertEqual(
timeuntil_filter(datetime.datetime.now() + datetime.timedelta(1, 1)),
'1 day')
'1\xa0day')
self.assertEqual(
timeuntil_filter(datetime.datetime(2005, 12, 30),
datetime.datetime(2005, 12, 29)),
'1 day')
'1\xa0day')
def test_default(self):
self.assertEqual(default("val", "default"), 'val')
@ -574,43 +576,45 @@ class DefaultFiltersTests(TestCase):
'get out of town')
def test_filesizeformat(self):
self.assertEqual(filesizeformat(1023), '1023 bytes')
self.assertEqual(filesizeformat(1024), '1.0 KB')
self.assertEqual(filesizeformat(10*1024), '10.0 KB')
self.assertEqual(filesizeformat(1024*1024-1), '1024.0 KB')
self.assertEqual(filesizeformat(1024*1024), '1.0 MB')
self.assertEqual(filesizeformat(1024*1024*50), '50.0 MB')
self.assertEqual(filesizeformat(1024*1024*1024-1), '1024.0 MB')
self.assertEqual(filesizeformat(1024*1024*1024), '1.0 GB')
self.assertEqual(filesizeformat(1024*1024*1024*1024), '1.0 TB')
self.assertEqual(filesizeformat(1024*1024*1024*1024*1024), '1.0 PB')
# NOTE: \xa0 avoids wrapping between value and unit
self.assertEqual(filesizeformat(1023), '1023\xa0bytes')
self.assertEqual(filesizeformat(1024), '1.0\xa0KB')
self.assertEqual(filesizeformat(10*1024), '10.0\xa0KB')
self.assertEqual(filesizeformat(1024*1024-1), '1024.0\xa0KB')
self.assertEqual(filesizeformat(1024*1024), '1.0\xa0MB')
self.assertEqual(filesizeformat(1024*1024*50), '50.0\xa0MB')
self.assertEqual(filesizeformat(1024*1024*1024-1), '1024.0\xa0MB')
self.assertEqual(filesizeformat(1024*1024*1024), '1.0\xa0GB')
self.assertEqual(filesizeformat(1024*1024*1024*1024), '1.0\xa0TB')
self.assertEqual(filesizeformat(1024*1024*1024*1024*1024), '1.0\xa0PB')
self.assertEqual(filesizeformat(1024*1024*1024*1024*1024*2000),
'2000.0 PB')
self.assertEqual(filesizeformat(complex(1,-1)), '0 bytes')
self.assertEqual(filesizeformat(""), '0 bytes')
'2000.0\xa0PB')
self.assertEqual(filesizeformat(complex(1,-1)), '0\xa0bytes')
self.assertEqual(filesizeformat(""), '0\xa0bytes')
self.assertEqual(filesizeformat("\N{GREEK SMALL LETTER ALPHA}"),
'0 bytes')
'0\xa0bytes')
def test_localized_filesizeformat(self):
# NOTE: \xa0 avoids wrapping between value and unit
with self.settings(USE_L10N=True):
with translation.override('de', deactivate=True):
self.assertEqual(filesizeformat(1023), '1023 Bytes')
self.assertEqual(filesizeformat(1024), '1,0 KB')
self.assertEqual(filesizeformat(10*1024), '10,0 KB')
self.assertEqual(filesizeformat(1024*1024-1), '1024,0 KB')
self.assertEqual(filesizeformat(1024*1024), '1,0 MB')
self.assertEqual(filesizeformat(1024*1024*50), '50,0 MB')
self.assertEqual(filesizeformat(1024*1024*1024-1), '1024,0 MB')
self.assertEqual(filesizeformat(1024*1024*1024), '1,0 GB')
self.assertEqual(filesizeformat(1024*1024*1024*1024), '1,0 TB')
self.assertEqual(filesizeformat(1023), '1023\xa0Bytes')
self.assertEqual(filesizeformat(1024), '1,0\xa0KB')
self.assertEqual(filesizeformat(10*1024), '10,0\xa0KB')
self.assertEqual(filesizeformat(1024*1024-1), '1024,0\xa0KB')
self.assertEqual(filesizeformat(1024*1024), '1,0\xa0MB')
self.assertEqual(filesizeformat(1024*1024*50), '50,0\xa0MB')
self.assertEqual(filesizeformat(1024*1024*1024-1), '1024,0\xa0MB')
self.assertEqual(filesizeformat(1024*1024*1024), '1,0\xa0GB')
self.assertEqual(filesizeformat(1024*1024*1024*1024), '1,0\xa0TB')
self.assertEqual(filesizeformat(1024*1024*1024*1024*1024),
'1,0 PB')
'1,0\xa0PB')
self.assertEqual(filesizeformat(1024*1024*1024*1024*1024*2000),
'2000,0 PB')
self.assertEqual(filesizeformat(complex(1,-1)), '0 Bytes')
self.assertEqual(filesizeformat(""), '0 Bytes')
'2000,0\xa0PB')
self.assertEqual(filesizeformat(complex(1,-1)), '0\xa0Bytes')
self.assertEqual(filesizeformat(""), '0\xa0Bytes')
self.assertEqual(filesizeformat("\N{GREEK SMALL LETTER ALPHA}"),
'0 Bytes')
'0\xa0Bytes')
def test_pluralize(self):
self.assertEqual(pluralize(1), '')

View File

@ -35,59 +35,60 @@ def get_filter_tests():
now_tz_i = datetime.now(FixedOffset((3 * 60) + 15)) # imaginary time zone
today = date.today()
# NOTE: \xa0 avoids wrapping between value and unit
return {
# Default compare with datetime.now()
'filter-timesince01' : ('{{ a|timesince }}', {'a': datetime.now() + timedelta(minutes=-1, seconds = -10)}, '1 minute'),
'filter-timesince02' : ('{{ a|timesince }}', {'a': datetime.now() - timedelta(days=1, minutes = 1)}, '1 day'),
'filter-timesince03' : ('{{ a|timesince }}', {'a': datetime.now() - timedelta(hours=1, minutes=25, seconds = 10)}, '1 hour, 25 minutes'),
'filter-timesince01' : ('{{ a|timesince }}', {'a': datetime.now() + timedelta(minutes=-1, seconds = -10)}, '1\xa0minute'),
'filter-timesince02' : ('{{ a|timesince }}', {'a': datetime.now() - timedelta(days=1, minutes = 1)}, '1\xa0day'),
'filter-timesince03' : ('{{ a|timesince }}', {'a': datetime.now() - timedelta(hours=1, minutes=25, seconds = 10)}, '1\xa0hour, 25\xa0minutes'),
# Compare to a given parameter
'filter-timesince04' : ('{{ a|timesince:b }}', {'a':now - timedelta(days=2), 'b':now - timedelta(days=1)}, '1 day'),
'filter-timesince05' : ('{{ a|timesince:b }}', {'a':now - timedelta(days=2, minutes=1), 'b':now - timedelta(days=2)}, '1 minute'),
'filter-timesince04' : ('{{ a|timesince:b }}', {'a':now - timedelta(days=2), 'b':now - timedelta(days=1)}, '1\xa0day'),
'filter-timesince05' : ('{{ a|timesince:b }}', {'a':now - timedelta(days=2, minutes=1), 'b':now - timedelta(days=2)}, '1\xa0minute'),
# Check that timezone is respected
'filter-timesince06' : ('{{ a|timesince:b }}', {'a':now_tz - timedelta(hours=8), 'b':now_tz}, '8 hours'),
'filter-timesince06' : ('{{ a|timesince:b }}', {'a':now_tz - timedelta(hours=8), 'b':now_tz}, '8\xa0hours'),
# Regression for #7443
'filter-timesince07': ('{{ earlier|timesince }}', { 'earlier': now - timedelta(days=7) }, '1 week'),
'filter-timesince08': ('{{ earlier|timesince:now }}', { 'now': now, 'earlier': now - timedelta(days=7) }, '1 week'),
'filter-timesince09': ('{{ later|timesince }}', { 'later': now + timedelta(days=7) }, '0 minutes'),
'filter-timesince10': ('{{ later|timesince:now }}', { 'now': now, 'later': now + timedelta(days=7) }, '0 minutes'),
'filter-timesince07': ('{{ earlier|timesince }}', { 'earlier': now - timedelta(days=7) }, '1\xa0week'),
'filter-timesince08': ('{{ earlier|timesince:now }}', { 'now': now, 'earlier': now - timedelta(days=7) }, '1\xa0week'),
'filter-timesince09': ('{{ later|timesince }}', { 'later': now + timedelta(days=7) }, '0\xa0minutes'),
'filter-timesince10': ('{{ later|timesince:now }}', { 'now': now, 'later': now + timedelta(days=7) }, '0\xa0minutes'),
# Ensures that differing timezones are calculated correctly
'filter-timesince11' : ('{{ a|timesince }}', {'a': now}, '0 minutes'),
'filter-timesince12' : ('{{ a|timesince }}', {'a': now_tz}, '0 minutes'),
'filter-timesince13' : ('{{ a|timesince }}', {'a': now_tz_i}, '0 minutes'),
'filter-timesince14' : ('{{ a|timesince:b }}', {'a': now_tz, 'b': now_tz_i}, '0 minutes'),
'filter-timesince11' : ('{{ a|timesince }}', {'a': now}, '0\xa0minutes'),
'filter-timesince12' : ('{{ a|timesince }}', {'a': now_tz}, '0\xa0minutes'),
'filter-timesince13' : ('{{ a|timesince }}', {'a': now_tz_i}, '0\xa0minutes'),
'filter-timesince14' : ('{{ a|timesince:b }}', {'a': now_tz, 'b': now_tz_i}, '0\xa0minutes'),
'filter-timesince15' : ('{{ a|timesince:b }}', {'a': now, 'b': now_tz_i}, ''),
'filter-timesince16' : ('{{ a|timesince:b }}', {'a': now_tz_i, 'b': now}, ''),
# Regression for #9065 (two date objects).
'filter-timesince17' : ('{{ a|timesince:b }}', {'a': today, 'b': today}, '0 minutes'),
'filter-timesince18' : ('{{ a|timesince:b }}', {'a': today, 'b': today + timedelta(hours=24)}, '1 day'),
'filter-timesince17' : ('{{ a|timesince:b }}', {'a': today, 'b': today}, '0\xa0minutes'),
'filter-timesince18' : ('{{ a|timesince:b }}', {'a': today, 'b': today + timedelta(hours=24)}, '1\xa0day'),
# Default compare with datetime.now()
'filter-timeuntil01' : ('{{ a|timeuntil }}', {'a':datetime.now() + timedelta(minutes=2, seconds = 10)}, '2 minutes'),
'filter-timeuntil02' : ('{{ a|timeuntil }}', {'a':(datetime.now() + timedelta(days=1, seconds = 10))}, '1 day'),
'filter-timeuntil03' : ('{{ a|timeuntil }}', {'a':(datetime.now() + timedelta(hours=8, minutes=10, seconds = 10))}, '8 hours, 10 minutes'),
'filter-timeuntil01' : ('{{ a|timeuntil }}', {'a':datetime.now() + timedelta(minutes=2, seconds = 10)}, '2\xa0minutes'),
'filter-timeuntil02' : ('{{ a|timeuntil }}', {'a':(datetime.now() + timedelta(days=1, seconds = 10))}, '1\xa0day'),
'filter-timeuntil03' : ('{{ a|timeuntil }}', {'a':(datetime.now() + timedelta(hours=8, minutes=10, seconds = 10))}, '8\xa0hours, 10\xa0minutes'),
# Compare to a given parameter
'filter-timeuntil04' : ('{{ a|timeuntil:b }}', {'a':now - timedelta(days=1), 'b':now - timedelta(days=2)}, '1 day'),
'filter-timeuntil05' : ('{{ a|timeuntil:b }}', {'a':now - timedelta(days=2), 'b':now - timedelta(days=2, minutes=1)}, '1 minute'),
'filter-timeuntil04' : ('{{ a|timeuntil:b }}', {'a':now - timedelta(days=1), 'b':now - timedelta(days=2)}, '1\xa0day'),
'filter-timeuntil05' : ('{{ a|timeuntil:b }}', {'a':now - timedelta(days=2), 'b':now - timedelta(days=2, minutes=1)}, '1\xa0minute'),
# Regression for #7443
'filter-timeuntil06': ('{{ earlier|timeuntil }}', { 'earlier': now - timedelta(days=7) }, '0 minutes'),
'filter-timeuntil07': ('{{ earlier|timeuntil:now }}', { 'now': now, 'earlier': now - timedelta(days=7) }, '0 minutes'),
'filter-timeuntil08': ('{{ later|timeuntil }}', { 'later': now + timedelta(days=7, hours=1) }, '1 week'),
'filter-timeuntil09': ('{{ later|timeuntil:now }}', { 'now': now, 'later': now + timedelta(days=7) }, '1 week'),
'filter-timeuntil06': ('{{ earlier|timeuntil }}', { 'earlier': now - timedelta(days=7) }, '0\xa0minutes'),
'filter-timeuntil07': ('{{ earlier|timeuntil:now }}', { 'now': now, 'earlier': now - timedelta(days=7) }, '0\xa0minutes'),
'filter-timeuntil08': ('{{ later|timeuntil }}', { 'later': now + timedelta(days=7, hours=1) }, '1\xa0week'),
'filter-timeuntil09': ('{{ later|timeuntil:now }}', { 'now': now, 'later': now + timedelta(days=7) }, '1\xa0week'),
# Ensures that differing timezones are calculated correctly
'filter-timeuntil10' : ('{{ a|timeuntil }}', {'a': now_tz_i}, '0 minutes'),
'filter-timeuntil11' : ('{{ a|timeuntil:b }}', {'a': now_tz_i, 'b': now_tz}, '0 minutes'),
'filter-timeuntil10' : ('{{ a|timeuntil }}', {'a': now_tz_i}, '0\xa0minutes'),
'filter-timeuntil11' : ('{{ a|timeuntil:b }}', {'a': now_tz_i, 'b': now_tz}, '0\xa0minutes'),
# Regression for #9065 (two date objects).
'filter-timeuntil12' : ('{{ a|timeuntil:b }}', {'a': today, 'b': today}, '0 minutes'),
'filter-timeuntil13' : ('{{ a|timeuntil:b }}', {'a': today, 'b': today - timedelta(hours=24)}, '1 day'),
'filter-timeuntil12' : ('{{ a|timeuntil:b }}', {'a': today, 'b': today}, '0\xa0minutes'),
'filter-timeuntil13' : ('{{ a|timeuntil:b }}', {'a': today, 'b': today - timedelta(hours=24)}, '1\xa0day'),
'filter-addslash01': ("{% autoescape off %}{{ a|addslashes }} {{ b|addslashes }}{% endautoescape %}", {"a": "<a>'", "b": mark_safe("<a>'")}, r"<a>\' <a>\'"),
'filter-addslash02': ("{{ a|addslashes }} {{ b|addslashes }}", {"a": "<a>'", "b": mark_safe("<a>'")}, r"&lt;a&gt;\&#39; <a>\'"),

View File

@ -21,32 +21,33 @@ class TimesinceTests(unittest.TestCase):
def test_equal_datetimes(self):
""" equal datetimes. """
self.assertEqual(timesince(self.t, self.t), '0 minutes')
# NOTE: \xa0 avoids wrapping between value and unit
self.assertEqual(timesince(self.t, self.t), '0\xa0minutes')
def test_ignore_microseconds_and_seconds(self):
""" Microseconds and seconds are ignored. """
self.assertEqual(timesince(self.t, self.t+self.onemicrosecond),
'0 minutes')
'0\xa0minutes')
self.assertEqual(timesince(self.t, self.t+self.onesecond),
'0 minutes')
'0\xa0minutes')
def test_other_units(self):
""" Test other units. """
self.assertEqual(timesince(self.t, self.t+self.oneminute),
'1 minute')
self.assertEqual(timesince(self.t, self.t+self.onehour), '1 hour')
self.assertEqual(timesince(self.t, self.t+self.oneday), '1 day')
self.assertEqual(timesince(self.t, self.t+self.oneweek), '1 week')
'1\xa0minute')
self.assertEqual(timesince(self.t, self.t+self.onehour), '1\xa0hour')
self.assertEqual(timesince(self.t, self.t+self.oneday), '1\xa0day')
self.assertEqual(timesince(self.t, self.t+self.oneweek), '1\xa0week')
self.assertEqual(timesince(self.t, self.t+self.onemonth),
'1 month')
self.assertEqual(timesince(self.t, self.t+self.oneyear), '1 year')
'1\xa0month')
self.assertEqual(timesince(self.t, self.t+self.oneyear), '1\xa0year')
def test_multiple_units(self):
""" Test multiple units. """
self.assertEqual(timesince(self.t,
self.t+2*self.oneday+6*self.onehour), '2 days, 6 hours')
self.t+2*self.oneday+6*self.onehour), '2\xa0days, 6\xa0hours')
self.assertEqual(timesince(self.t,
self.t+2*self.oneweek+2*self.oneday), '2 weeks, 2 days')
self.t+2*self.oneweek+2*self.oneday), '2\xa0weeks, 2\xa0days')
def test_display_first_unit(self):
"""
@ -55,10 +56,10 @@ class TimesinceTests(unittest.TestCase):
"""
self.assertEqual(timesince(self.t,
self.t+2*self.oneweek+3*self.onehour+4*self.oneminute),
'2 weeks')
'2\xa0weeks')
self.assertEqual(timesince(self.t,
self.t+4*self.oneday+5*self.oneminute), '4 days')
self.t+4*self.oneday+5*self.oneminute), '4\xa0days')
def test_display_second_before_first(self):
"""
@ -66,30 +67,30 @@ class TimesinceTests(unittest.TestCase):
get 0 minutes.
"""
self.assertEqual(timesince(self.t, self.t-self.onemicrosecond),
'0 minutes')
'0\xa0minutes')
self.assertEqual(timesince(self.t, self.t-self.onesecond),
'0 minutes')
'0\xa0minutes')
self.assertEqual(timesince(self.t, self.t-self.oneminute),
'0 minutes')
'0\xa0minutes')
self.assertEqual(timesince(self.t, self.t-self.onehour),
'0 minutes')
'0\xa0minutes')
self.assertEqual(timesince(self.t, self.t-self.oneday),
'0 minutes')
'0\xa0minutes')
self.assertEqual(timesince(self.t, self.t-self.oneweek),
'0 minutes')
'0\xa0minutes')
self.assertEqual(timesince(self.t, self.t-self.onemonth),
'0 minutes')
'0\xa0minutes')
self.assertEqual(timesince(self.t, self.t-self.oneyear),
'0 minutes')
'0\xa0minutes')
self.assertEqual(timesince(self.t,
self.t-2*self.oneday-6*self.onehour), '0 minutes')
self.t-2*self.oneday-6*self.onehour), '0\xa0minutes')
self.assertEqual(timesince(self.t,
self.t-2*self.oneweek-2*self.oneday), '0 minutes')
self.t-2*self.oneweek-2*self.oneday), '0\xa0minutes')
self.assertEqual(timesince(self.t,
self.t-2*self.oneweek-3*self.onehour-4*self.oneminute),
'0 minutes')
'0\xa0minutes')
self.assertEqual(timesince(self.t,
self.t-4*self.oneday-5*self.oneminute), '0 minutes')
self.t-4*self.oneday-5*self.oneminute), '0\xa0minutes')
def test_different_timezones(self):
""" When using two different timezones. """
@ -97,28 +98,28 @@ class TimesinceTests(unittest.TestCase):
now_tz = datetime.datetime.now(LocalTimezone(now))
now_tz_i = datetime.datetime.now(FixedOffset((3 * 60) + 15))
self.assertEqual(timesince(now), '0 minutes')
self.assertEqual(timesince(now_tz), '0 minutes')
self.assertEqual(timeuntil(now_tz, now_tz_i), '0 minutes')
self.assertEqual(timesince(now), '0\xa0minutes')
self.assertEqual(timesince(now_tz), '0\xa0minutes')
self.assertEqual(timeuntil(now_tz, now_tz_i), '0\xa0minutes')
def test_date_objects(self):
""" Both timesince and timeuntil should work on date objects (#17937). """
today = datetime.date.today()
self.assertEqual(timesince(today + self.oneday), '0 minutes')
self.assertEqual(timeuntil(today - self.oneday), '0 minutes')
self.assertEqual(timesince(today + self.oneday), '0\xa0minutes')
self.assertEqual(timeuntil(today - self.oneday), '0\xa0minutes')
def test_both_date_objects(self):
""" Timesince should work with both date objects (#9672) """
today = datetime.date.today()
self.assertEqual(timeuntil(today + self.oneday, today), '1 day')
self.assertEqual(timeuntil(today - self.oneday, today), '0 minutes')
self.assertEqual(timeuntil(today + self.oneweek, today), '1 week')
self.assertEqual(timeuntil(today + self.oneday, today), '1\xa0day')
self.assertEqual(timeuntil(today - self.oneday, today), '0\xa0minutes')
self.assertEqual(timeuntil(today + self.oneweek, today), '1\xa0week')
def test_naive_datetime_with_tzinfo_attribute(self):
class naive(datetime.tzinfo):
def utcoffset(self, dt):
return None
future = datetime.datetime(2080, 1, 1, tzinfo=naive())
self.assertEqual(timesince(future), '0 minutes')
self.assertEqual(timesince(future), '0\xa0minutes')
past = datetime.datetime(1980, 1, 1, tzinfo=naive())
self.assertEqual(timeuntil(past), '0 minutes')
self.assertEqual(timeuntil(past), '0\xa0minutes')