Fixed #20246 -- Added non-breaking spaces between values an units
This commit is contained in:
parent
caf56ad174
commit
7d77e9786a
1
AUTHORS
1
AUTHORS
|
@ -536,6 +536,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
starrynight <cmorgh@gmail.com>
|
starrynight <cmorgh@gmail.com>
|
||||||
Vasiliy Stavenko <stavenko@gmail.com>
|
Vasiliy Stavenko <stavenko@gmail.com>
|
||||||
Thomas Steinacher <http://www.eggdrop.ch/>
|
Thomas Steinacher <http://www.eggdrop.ch/>
|
||||||
|
Emil Stenström <em@kth.se>
|
||||||
Johan C. Stöver <johan@nilling.nl>
|
Johan C. Stöver <johan@nilling.nl>
|
||||||
Nowell Strite <http://nowell.strite.org/>
|
Nowell Strite <http://nowell.strite.org/>
|
||||||
Thomas Stromberg <tstromberg@google.com>
|
Thomas Stromberg <tstromberg@google.com>
|
||||||
|
|
|
@ -194,17 +194,20 @@ def naturaltime(value):
|
||||||
return _('now')
|
return _('now')
|
||||||
elif delta.seconds < 60:
|
elif delta.seconds < 60:
|
||||||
return ungettext(
|
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}
|
) % {'count': delta.seconds}
|
||||||
elif delta.seconds // 60 < 60:
|
elif delta.seconds // 60 < 60:
|
||||||
count = delta.seconds // 60
|
count = delta.seconds // 60
|
||||||
return ungettext(
|
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}
|
) % {'count': count}
|
||||||
else:
|
else:
|
||||||
count = delta.seconds // 60 // 60
|
count = delta.seconds // 60 // 60
|
||||||
return ungettext(
|
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}
|
) % {'count': count}
|
||||||
else:
|
else:
|
||||||
delta = value - now
|
delta = value - now
|
||||||
|
@ -216,15 +219,18 @@ def naturaltime(value):
|
||||||
return _('now')
|
return _('now')
|
||||||
elif delta.seconds < 60:
|
elif delta.seconds < 60:
|
||||||
return ungettext(
|
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}
|
) % {'count': delta.seconds}
|
||||||
elif delta.seconds // 60 < 60:
|
elif delta.seconds // 60 < 60:
|
||||||
count = delta.seconds // 60
|
count = delta.seconds // 60
|
||||||
return ungettext(
|
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}
|
) % {'count': count}
|
||||||
else:
|
else:
|
||||||
count = delta.seconds // 60 // 60
|
count = delta.seconds // 60 // 60
|
||||||
return ungettext(
|
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}
|
) % {'count': count}
|
||||||
|
|
|
@ -195,22 +195,22 @@ class HumanizeTests(TestCase):
|
||||||
result_list = [
|
result_list = [
|
||||||
'now',
|
'now',
|
||||||
'a second ago',
|
'a second ago',
|
||||||
'30 seconds ago',
|
'30\xa0seconds ago',
|
||||||
'a minute ago',
|
'a minute ago',
|
||||||
'2 minutes ago',
|
'2\xa0minutes ago',
|
||||||
'an hour ago',
|
'an hour ago',
|
||||||
'23 hours ago',
|
'23\xa0hours ago',
|
||||||
'1 day ago',
|
'1\xa0day ago',
|
||||||
'1 year, 4 months ago',
|
'1\xa0year, 4\xa0months ago',
|
||||||
'a second from now',
|
'a second from now',
|
||||||
'30 seconds from now',
|
'30\xa0seconds from now',
|
||||||
'a minute from now',
|
'a minute from now',
|
||||||
'2 minutes from now',
|
'2\xa0minutes from now',
|
||||||
'an hour from now',
|
'an hour from now',
|
||||||
'23 hours from now',
|
'23\xa0hours from now',
|
||||||
'1 day from now',
|
'1\xa0day from now',
|
||||||
'2 days, 6 hours from now',
|
'2\xa0days, 6\xa0hours from now',
|
||||||
'1 year, 4 months from now',
|
'1\xa0year, 4\xa0months from now',
|
||||||
'now',
|
'now',
|
||||||
'now',
|
'now',
|
||||||
]
|
]
|
||||||
|
@ -218,8 +218,8 @@ class HumanizeTests(TestCase):
|
||||||
# date in naive arithmetic is only 2 days and 5 hours after in
|
# date in naive arithmetic is only 2 days and 5 hours after in
|
||||||
# aware arithmetic.
|
# aware arithmetic.
|
||||||
result_list_with_tz_support = result_list[:]
|
result_list_with_tz_support = result_list[:]
|
||||||
assert result_list_with_tz_support[-4] == '2 days, 6 hours from now'
|
assert result_list_with_tz_support[-4] == '2\xa0days, 6\xa0hours from now'
|
||||||
result_list_with_tz_support[-4] == '2 days, 5 hours from now'
|
result_list_with_tz_support[-4] == '2\xa0days, 5\xa0hours from now'
|
||||||
|
|
||||||
orig_humanize_datetime, humanize.datetime = humanize.datetime, MockDateTime
|
orig_humanize_datetime, humanize.datetime = humanize.datetime, MockDateTime
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -14,7 +14,7 @@ from django.utils import formats
|
||||||
from django.utils.dateformat import format, time_format
|
from django.utils.dateformat import format, time_format
|
||||||
from django.utils.encoding import force_text, iri_to_uri
|
from django.utils.encoding import force_text, iri_to_uri
|
||||||
from django.utils.html import (conditional_escape, escapejs, fix_ampersands,
|
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.http import urlquote
|
||||||
from django.utils.text import Truncator, wrap, phone2numeric
|
from django.utils.text import Truncator, wrap, phone2numeric
|
||||||
from django.utils.safestring import mark_safe, SafeData, mark_for_escaping
|
from django.utils.safestring import mark_safe, SafeData, mark_for_escaping
|
||||||
|
@ -810,7 +810,8 @@ def filesizeformat(bytes):
|
||||||
try:
|
try:
|
||||||
bytes = float(bytes)
|
bytes = float(bytes)
|
||||||
except (TypeError,ValueError,UnicodeDecodeError):
|
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)
|
filesize_number_format = lambda value: formats.number_format(round(value, 1), 1)
|
||||||
|
|
||||||
|
@ -821,16 +822,19 @@ def filesizeformat(bytes):
|
||||||
PB = 1<<50
|
PB = 1<<50
|
||||||
|
|
||||||
if bytes < KB:
|
if bytes < KB:
|
||||||
return ungettext("%(size)d byte", "%(size)d bytes", bytes) % {'size': bytes}
|
value = ungettext("%(size)d byte", "%(size)d bytes", bytes) % {'size': bytes}
|
||||||
if bytes < MB:
|
elif bytes < MB:
|
||||||
return ugettext("%s KB") % filesize_number_format(bytes / KB)
|
value = ugettext("%s KB") % filesize_number_format(bytes / KB)
|
||||||
if bytes < GB:
|
elif bytes < GB:
|
||||||
return ugettext("%s MB") % filesize_number_format(bytes / MB)
|
value = ugettext("%s MB") % filesize_number_format(bytes / MB)
|
||||||
if bytes < TB:
|
elif bytes < TB:
|
||||||
return ugettext("%s GB") % filesize_number_format(bytes / GB)
|
value = ugettext("%s GB") % filesize_number_format(bytes / GB)
|
||||||
if bytes < PB:
|
elif bytes < PB:
|
||||||
return ugettext("%s TB") % filesize_number_format(bytes / TB)
|
value = ugettext("%s TB") % filesize_number_format(bytes / TB)
|
||||||
return ugettext("%s PB") % filesize_number_format(bytes / PB)
|
else:
|
||||||
|
value = ugettext("%s PB") % filesize_number_format(bytes / PB)
|
||||||
|
|
||||||
|
return avoid_wrapping(value)
|
||||||
|
|
||||||
@register.filter(is_safe=False)
|
@register.filter(is_safe=False)
|
||||||
def pluralize(value, arg='s'):
|
def pluralize(value, arg='s'):
|
||||||
|
|
|
@ -281,3 +281,10 @@ def clean_html(text):
|
||||||
text = trailing_empty_content_re.sub('', text)
|
text = trailing_empty_content_re.sub('', text)
|
||||||
return text
|
return text
|
||||||
clean_html = allow_lazy(clean_html, six.text_type)
|
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")
|
||||||
|
|
|
@ -2,6 +2,7 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
from django.utils.html import avoid_wrapping
|
||||||
from django.utils.timezone import is_aware, utc
|
from django.utils.timezone import is_aware, utc
|
||||||
from django.utils.translation import ugettext, ungettext_lazy
|
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
|
since = delta.days * 24 * 60 * 60 + delta.seconds
|
||||||
if since <= 0:
|
if since <= 0:
|
||||||
# d is in the future compared to now, stop processing.
|
# 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):
|
for i, (seconds, name) in enumerate(chunks):
|
||||||
count = since // seconds
|
count = since // seconds
|
||||||
if count != 0:
|
if count != 0:
|
||||||
break
|
break
|
||||||
result = name % count
|
result = avoid_wrapping(name % count)
|
||||||
if i + 1 < len(chunks):
|
if i + 1 < len(chunks):
|
||||||
# Now get the second item
|
# Now get the second item
|
||||||
seconds2, name2 = chunks[i + 1]
|
seconds2, name2 = chunks[i + 1]
|
||||||
count2 = (since - (seconds * count)) // seconds2
|
count2 = (since - (seconds * count)) // seconds2
|
||||||
if count2 != 0:
|
if count2 != 0:
|
||||||
result += ugettext(', ') + name2 % count2
|
result += ugettext(', ') + avoid_wrapping(name2 % count2)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def timeuntil(d, now=None):
|
def timeuntil(d, now=None):
|
||||||
|
|
|
@ -527,24 +527,26 @@ class DefaultFiltersTests(TestCase):
|
||||||
|
|
||||||
def test_timesince(self):
|
def test_timesince(self):
|
||||||
# real testing is done in timesince.py, where we can provide our own 'now'
|
# real testing is done in timesince.py, where we can provide our own 'now'
|
||||||
|
# NOTE: \xa0 avoids wrapping between value and unit
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
timesince_filter(datetime.datetime.now() - datetime.timedelta(1)),
|
timesince_filter(datetime.datetime.now() - datetime.timedelta(1)),
|
||||||
'1 day')
|
'1\xa0day')
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
timesince_filter(datetime.datetime(2005, 12, 29),
|
timesince_filter(datetime.datetime(2005, 12, 29),
|
||||||
datetime.datetime(2005, 12, 30)),
|
datetime.datetime(2005, 12, 30)),
|
||||||
'1 day')
|
'1\xa0day')
|
||||||
|
|
||||||
def test_timeuntil(self):
|
def test_timeuntil(self):
|
||||||
|
# NOTE: \xa0 avoids wrapping between value and unit
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
timeuntil_filter(datetime.datetime.now() + datetime.timedelta(1, 1)),
|
timeuntil_filter(datetime.datetime.now() + datetime.timedelta(1, 1)),
|
||||||
'1 day')
|
'1\xa0day')
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
timeuntil_filter(datetime.datetime(2005, 12, 30),
|
timeuntil_filter(datetime.datetime(2005, 12, 30),
|
||||||
datetime.datetime(2005, 12, 29)),
|
datetime.datetime(2005, 12, 29)),
|
||||||
'1 day')
|
'1\xa0day')
|
||||||
|
|
||||||
def test_default(self):
|
def test_default(self):
|
||||||
self.assertEqual(default("val", "default"), 'val')
|
self.assertEqual(default("val", "default"), 'val')
|
||||||
|
@ -574,43 +576,45 @@ class DefaultFiltersTests(TestCase):
|
||||||
'get out of town')
|
'get out of town')
|
||||||
|
|
||||||
def test_filesizeformat(self):
|
def test_filesizeformat(self):
|
||||||
self.assertEqual(filesizeformat(1023), '1023 bytes')
|
# NOTE: \xa0 avoids wrapping between value and unit
|
||||||
self.assertEqual(filesizeformat(1024), '1.0 KB')
|
self.assertEqual(filesizeformat(1023), '1023\xa0bytes')
|
||||||
self.assertEqual(filesizeformat(10*1024), '10.0 KB')
|
self.assertEqual(filesizeformat(1024), '1.0\xa0KB')
|
||||||
self.assertEqual(filesizeformat(1024*1024-1), '1024.0 KB')
|
self.assertEqual(filesizeformat(10*1024), '10.0\xa0KB')
|
||||||
self.assertEqual(filesizeformat(1024*1024), '1.0 MB')
|
self.assertEqual(filesizeformat(1024*1024-1), '1024.0\xa0KB')
|
||||||
self.assertEqual(filesizeformat(1024*1024*50), '50.0 MB')
|
self.assertEqual(filesizeformat(1024*1024), '1.0\xa0MB')
|
||||||
self.assertEqual(filesizeformat(1024*1024*1024-1), '1024.0 MB')
|
self.assertEqual(filesizeformat(1024*1024*50), '50.0\xa0MB')
|
||||||
self.assertEqual(filesizeformat(1024*1024*1024), '1.0 GB')
|
self.assertEqual(filesizeformat(1024*1024*1024-1), '1024.0\xa0MB')
|
||||||
self.assertEqual(filesizeformat(1024*1024*1024*1024), '1.0 TB')
|
self.assertEqual(filesizeformat(1024*1024*1024), '1.0\xa0GB')
|
||||||
self.assertEqual(filesizeformat(1024*1024*1024*1024*1024), '1.0 PB')
|
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),
|
self.assertEqual(filesizeformat(1024*1024*1024*1024*1024*2000),
|
||||||
'2000.0 PB')
|
'2000.0\xa0PB')
|
||||||
self.assertEqual(filesizeformat(complex(1,-1)), '0 bytes')
|
self.assertEqual(filesizeformat(complex(1,-1)), '0\xa0bytes')
|
||||||
self.assertEqual(filesizeformat(""), '0 bytes')
|
self.assertEqual(filesizeformat(""), '0\xa0bytes')
|
||||||
self.assertEqual(filesizeformat("\N{GREEK SMALL LETTER ALPHA}"),
|
self.assertEqual(filesizeformat("\N{GREEK SMALL LETTER ALPHA}"),
|
||||||
'0 bytes')
|
'0\xa0bytes')
|
||||||
|
|
||||||
def test_localized_filesizeformat(self):
|
def test_localized_filesizeformat(self):
|
||||||
|
# NOTE: \xa0 avoids wrapping between value and unit
|
||||||
with self.settings(USE_L10N=True):
|
with self.settings(USE_L10N=True):
|
||||||
with translation.override('de', deactivate=True):
|
with translation.override('de', deactivate=True):
|
||||||
self.assertEqual(filesizeformat(1023), '1023 Bytes')
|
self.assertEqual(filesizeformat(1023), '1023\xa0Bytes')
|
||||||
self.assertEqual(filesizeformat(1024), '1,0 KB')
|
self.assertEqual(filesizeformat(1024), '1,0\xa0KB')
|
||||||
self.assertEqual(filesizeformat(10*1024), '10,0 KB')
|
self.assertEqual(filesizeformat(10*1024), '10,0\xa0KB')
|
||||||
self.assertEqual(filesizeformat(1024*1024-1), '1024,0 KB')
|
self.assertEqual(filesizeformat(1024*1024-1), '1024,0\xa0KB')
|
||||||
self.assertEqual(filesizeformat(1024*1024), '1,0 MB')
|
self.assertEqual(filesizeformat(1024*1024), '1,0\xa0MB')
|
||||||
self.assertEqual(filesizeformat(1024*1024*50), '50,0 MB')
|
self.assertEqual(filesizeformat(1024*1024*50), '50,0\xa0MB')
|
||||||
self.assertEqual(filesizeformat(1024*1024*1024-1), '1024,0 MB')
|
self.assertEqual(filesizeformat(1024*1024*1024-1), '1024,0\xa0MB')
|
||||||
self.assertEqual(filesizeformat(1024*1024*1024), '1,0 GB')
|
self.assertEqual(filesizeformat(1024*1024*1024), '1,0\xa0GB')
|
||||||
self.assertEqual(filesizeformat(1024*1024*1024*1024), '1,0 TB')
|
self.assertEqual(filesizeformat(1024*1024*1024*1024), '1,0\xa0TB')
|
||||||
self.assertEqual(filesizeformat(1024*1024*1024*1024*1024),
|
self.assertEqual(filesizeformat(1024*1024*1024*1024*1024),
|
||||||
'1,0 PB')
|
'1,0\xa0PB')
|
||||||
self.assertEqual(filesizeformat(1024*1024*1024*1024*1024*2000),
|
self.assertEqual(filesizeformat(1024*1024*1024*1024*1024*2000),
|
||||||
'2000,0 PB')
|
'2000,0\xa0PB')
|
||||||
self.assertEqual(filesizeformat(complex(1,-1)), '0 Bytes')
|
self.assertEqual(filesizeformat(complex(1,-1)), '0\xa0Bytes')
|
||||||
self.assertEqual(filesizeformat(""), '0 Bytes')
|
self.assertEqual(filesizeformat(""), '0\xa0Bytes')
|
||||||
self.assertEqual(filesizeformat("\N{GREEK SMALL LETTER ALPHA}"),
|
self.assertEqual(filesizeformat("\N{GREEK SMALL LETTER ALPHA}"),
|
||||||
'0 Bytes')
|
'0\xa0Bytes')
|
||||||
|
|
||||||
def test_pluralize(self):
|
def test_pluralize(self):
|
||||||
self.assertEqual(pluralize(1), '')
|
self.assertEqual(pluralize(1), '')
|
||||||
|
|
|
@ -35,59 +35,60 @@ def get_filter_tests():
|
||||||
now_tz_i = datetime.now(FixedOffset((3 * 60) + 15)) # imaginary time zone
|
now_tz_i = datetime.now(FixedOffset((3 * 60) + 15)) # imaginary time zone
|
||||||
today = date.today()
|
today = date.today()
|
||||||
|
|
||||||
|
# NOTE: \xa0 avoids wrapping between value and unit
|
||||||
return {
|
return {
|
||||||
# Default compare with datetime.now()
|
# Default compare with datetime.now()
|
||||||
'filter-timesince01' : ('{{ a|timesince }}', {'a': datetime.now() + timedelta(minutes=-1, seconds = -10)}, '1 minute'),
|
'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 day'),
|
'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 hour, 25 minutes'),
|
'filter-timesince03' : ('{{ a|timesince }}', {'a': datetime.now() - timedelta(hours=1, minutes=25, seconds = 10)}, '1\xa0hour, 25\xa0minutes'),
|
||||||
|
|
||||||
# Compare to a given parameter
|
# Compare to a given parameter
|
||||||
'filter-timesince04' : ('{{ a|timesince:b }}', {'a':now - timedelta(days=2), 'b':now - timedelta(days=1)}, '1 day'),
|
'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 minute'),
|
'filter-timesince05' : ('{{ a|timesince:b }}', {'a':now - timedelta(days=2, minutes=1), 'b':now - timedelta(days=2)}, '1\xa0minute'),
|
||||||
|
|
||||||
# Check that timezone is respected
|
# 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
|
# Regression for #7443
|
||||||
'filter-timesince07': ('{{ earlier|timesince }}', { 'earlier': now - timedelta(days=7) }, '1 week'),
|
'filter-timesince07': ('{{ earlier|timesince }}', { 'earlier': now - timedelta(days=7) }, '1\xa0week'),
|
||||||
'filter-timesince08': ('{{ earlier|timesince:now }}', { 'now': now, 'earlier': now - timedelta(days=7) }, '1 week'),
|
'filter-timesince08': ('{{ earlier|timesince:now }}', { 'now': now, 'earlier': now - timedelta(days=7) }, '1\xa0week'),
|
||||||
'filter-timesince09': ('{{ later|timesince }}', { 'later': now + timedelta(days=7) }, '0 minutes'),
|
'filter-timesince09': ('{{ later|timesince }}', { 'later': now + timedelta(days=7) }, '0\xa0minutes'),
|
||||||
'filter-timesince10': ('{{ later|timesince:now }}', { 'now': now, 'later': now + timedelta(days=7) }, '0 minutes'),
|
'filter-timesince10': ('{{ later|timesince:now }}', { 'now': now, 'later': now + timedelta(days=7) }, '0\xa0minutes'),
|
||||||
|
|
||||||
# Ensures that differing timezones are calculated correctly
|
# Ensures that differing timezones are calculated correctly
|
||||||
'filter-timesince11' : ('{{ a|timesince }}', {'a': now}, '0 minutes'),
|
'filter-timesince11' : ('{{ a|timesince }}', {'a': now}, '0\xa0minutes'),
|
||||||
'filter-timesince12' : ('{{ a|timesince }}', {'a': now_tz}, '0 minutes'),
|
'filter-timesince12' : ('{{ a|timesince }}', {'a': now_tz}, '0\xa0minutes'),
|
||||||
'filter-timesince13' : ('{{ a|timesince }}', {'a': now_tz_i}, '0 minutes'),
|
'filter-timesince13' : ('{{ a|timesince }}', {'a': now_tz_i}, '0\xa0minutes'),
|
||||||
'filter-timesince14' : ('{{ a|timesince:b }}', {'a': now_tz, 'b': now_tz_i}, '0 minutes'),
|
'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-timesince15' : ('{{ a|timesince:b }}', {'a': now, 'b': now_tz_i}, ''),
|
||||||
'filter-timesince16' : ('{{ a|timesince:b }}', {'a': now_tz_i, 'b': now}, ''),
|
'filter-timesince16' : ('{{ a|timesince:b }}', {'a': now_tz_i, 'b': now}, ''),
|
||||||
|
|
||||||
# Regression for #9065 (two date objects).
|
# Regression for #9065 (two date objects).
|
||||||
'filter-timesince17' : ('{{ a|timesince:b }}', {'a': today, 'b': today}, '0 minutes'),
|
'filter-timesince17' : ('{{ a|timesince:b }}', {'a': today, 'b': today}, '0\xa0minutes'),
|
||||||
'filter-timesince18' : ('{{ a|timesince:b }}', {'a': today, 'b': today + timedelta(hours=24)}, '1 day'),
|
'filter-timesince18' : ('{{ a|timesince:b }}', {'a': today, 'b': today + timedelta(hours=24)}, '1\xa0day'),
|
||||||
|
|
||||||
# Default compare with datetime.now()
|
# Default compare with datetime.now()
|
||||||
'filter-timeuntil01' : ('{{ a|timeuntil }}', {'a':datetime.now() + timedelta(minutes=2, seconds = 10)}, '2 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 day'),
|
'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 hours, 10 minutes'),
|
'filter-timeuntil03' : ('{{ a|timeuntil }}', {'a':(datetime.now() + timedelta(hours=8, minutes=10, seconds = 10))}, '8\xa0hours, 10\xa0minutes'),
|
||||||
|
|
||||||
# Compare to a given parameter
|
# Compare to a given parameter
|
||||||
'filter-timeuntil04' : ('{{ a|timeuntil:b }}', {'a':now - timedelta(days=1), 'b':now - timedelta(days=2)}, '1 day'),
|
'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 minute'),
|
'filter-timeuntil05' : ('{{ a|timeuntil:b }}', {'a':now - timedelta(days=2), 'b':now - timedelta(days=2, minutes=1)}, '1\xa0minute'),
|
||||||
|
|
||||||
# Regression for #7443
|
# Regression for #7443
|
||||||
'filter-timeuntil06': ('{{ earlier|timeuntil }}', { 'earlier': now - timedelta(days=7) }, '0 minutes'),
|
'filter-timeuntil06': ('{{ earlier|timeuntil }}', { 'earlier': now - timedelta(days=7) }, '0\xa0minutes'),
|
||||||
'filter-timeuntil07': ('{{ earlier|timeuntil:now }}', { 'now': now, 'earlier': now - timedelta(days=7) }, '0 minutes'),
|
'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 week'),
|
'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 week'),
|
'filter-timeuntil09': ('{{ later|timeuntil:now }}', { 'now': now, 'later': now + timedelta(days=7) }, '1\xa0week'),
|
||||||
|
|
||||||
# Ensures that differing timezones are calculated correctly
|
# Ensures that differing timezones are calculated correctly
|
||||||
'filter-timeuntil10' : ('{{ a|timeuntil }}', {'a': now_tz_i}, '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 minutes'),
|
'filter-timeuntil11' : ('{{ a|timeuntil:b }}', {'a': now_tz_i, 'b': now_tz}, '0\xa0minutes'),
|
||||||
|
|
||||||
# Regression for #9065 (two date objects).
|
# Regression for #9065 (two date objects).
|
||||||
'filter-timeuntil12' : ('{{ a|timeuntil:b }}', {'a': today, 'b': today}, '0 minutes'),
|
'filter-timeuntil12' : ('{{ a|timeuntil:b }}', {'a': today, 'b': today}, '0\xa0minutes'),
|
||||||
'filter-timeuntil13' : ('{{ a|timeuntil:b }}', {'a': today, 'b': today - timedelta(hours=24)}, '1 day'),
|
'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-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"<a>\' <a>\'"),
|
'filter-addslash02': ("{{ a|addslashes }} {{ b|addslashes }}", {"a": "<a>'", "b": mark_safe("<a>'")}, r"<a>\' <a>\'"),
|
||||||
|
|
|
@ -21,32 +21,33 @@ class TimesinceTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_equal_datetimes(self):
|
def test_equal_datetimes(self):
|
||||||
""" equal datetimes. """
|
""" 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):
|
def test_ignore_microseconds_and_seconds(self):
|
||||||
""" Microseconds and seconds are ignored. """
|
""" Microseconds and seconds are ignored. """
|
||||||
self.assertEqual(timesince(self.t, self.t+self.onemicrosecond),
|
self.assertEqual(timesince(self.t, self.t+self.onemicrosecond),
|
||||||
'0 minutes')
|
'0\xa0minutes')
|
||||||
self.assertEqual(timesince(self.t, self.t+self.onesecond),
|
self.assertEqual(timesince(self.t, self.t+self.onesecond),
|
||||||
'0 minutes')
|
'0\xa0minutes')
|
||||||
|
|
||||||
def test_other_units(self):
|
def test_other_units(self):
|
||||||
""" Test other units. """
|
""" Test other units. """
|
||||||
self.assertEqual(timesince(self.t, self.t+self.oneminute),
|
self.assertEqual(timesince(self.t, self.t+self.oneminute),
|
||||||
'1 minute')
|
'1\xa0minute')
|
||||||
self.assertEqual(timesince(self.t, self.t+self.onehour), '1 hour')
|
self.assertEqual(timesince(self.t, self.t+self.onehour), '1\xa0hour')
|
||||||
self.assertEqual(timesince(self.t, self.t+self.oneday), '1 day')
|
self.assertEqual(timesince(self.t, self.t+self.oneday), '1\xa0day')
|
||||||
self.assertEqual(timesince(self.t, self.t+self.oneweek), '1 week')
|
self.assertEqual(timesince(self.t, self.t+self.oneweek), '1\xa0week')
|
||||||
self.assertEqual(timesince(self.t, self.t+self.onemonth),
|
self.assertEqual(timesince(self.t, self.t+self.onemonth),
|
||||||
'1 month')
|
'1\xa0month')
|
||||||
self.assertEqual(timesince(self.t, self.t+self.oneyear), '1 year')
|
self.assertEqual(timesince(self.t, self.t+self.oneyear), '1\xa0year')
|
||||||
|
|
||||||
def test_multiple_units(self):
|
def test_multiple_units(self):
|
||||||
""" Test multiple units. """
|
""" Test multiple units. """
|
||||||
self.assertEqual(timesince(self.t,
|
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.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):
|
def test_display_first_unit(self):
|
||||||
"""
|
"""
|
||||||
|
@ -55,10 +56,10 @@ class TimesinceTests(unittest.TestCase):
|
||||||
"""
|
"""
|
||||||
self.assertEqual(timesince(self.t,
|
self.assertEqual(timesince(self.t,
|
||||||
self.t+2*self.oneweek+3*self.onehour+4*self.oneminute),
|
self.t+2*self.oneweek+3*self.onehour+4*self.oneminute),
|
||||||
'2 weeks')
|
'2\xa0weeks')
|
||||||
|
|
||||||
self.assertEqual(timesince(self.t,
|
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):
|
def test_display_second_before_first(self):
|
||||||
"""
|
"""
|
||||||
|
@ -66,30 +67,30 @@ class TimesinceTests(unittest.TestCase):
|
||||||
get 0 minutes.
|
get 0 minutes.
|
||||||
"""
|
"""
|
||||||
self.assertEqual(timesince(self.t, self.t-self.onemicrosecond),
|
self.assertEqual(timesince(self.t, self.t-self.onemicrosecond),
|
||||||
'0 minutes')
|
'0\xa0minutes')
|
||||||
self.assertEqual(timesince(self.t, self.t-self.onesecond),
|
self.assertEqual(timesince(self.t, self.t-self.onesecond),
|
||||||
'0 minutes')
|
'0\xa0minutes')
|
||||||
self.assertEqual(timesince(self.t, self.t-self.oneminute),
|
self.assertEqual(timesince(self.t, self.t-self.oneminute),
|
||||||
'0 minutes')
|
'0\xa0minutes')
|
||||||
self.assertEqual(timesince(self.t, self.t-self.onehour),
|
self.assertEqual(timesince(self.t, self.t-self.onehour),
|
||||||
'0 minutes')
|
'0\xa0minutes')
|
||||||
self.assertEqual(timesince(self.t, self.t-self.oneday),
|
self.assertEqual(timesince(self.t, self.t-self.oneday),
|
||||||
'0 minutes')
|
'0\xa0minutes')
|
||||||
self.assertEqual(timesince(self.t, self.t-self.oneweek),
|
self.assertEqual(timesince(self.t, self.t-self.oneweek),
|
||||||
'0 minutes')
|
'0\xa0minutes')
|
||||||
self.assertEqual(timesince(self.t, self.t-self.onemonth),
|
self.assertEqual(timesince(self.t, self.t-self.onemonth),
|
||||||
'0 minutes')
|
'0\xa0minutes')
|
||||||
self.assertEqual(timesince(self.t, self.t-self.oneyear),
|
self.assertEqual(timesince(self.t, self.t-self.oneyear),
|
||||||
'0 minutes')
|
'0\xa0minutes')
|
||||||
self.assertEqual(timesince(self.t,
|
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.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.assertEqual(timesince(self.t,
|
||||||
self.t-2*self.oneweek-3*self.onehour-4*self.oneminute),
|
self.t-2*self.oneweek-3*self.onehour-4*self.oneminute),
|
||||||
'0 minutes')
|
'0\xa0minutes')
|
||||||
self.assertEqual(timesince(self.t,
|
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):
|
def test_different_timezones(self):
|
||||||
""" When using two different timezones. """
|
""" When using two different timezones. """
|
||||||
|
@ -97,28 +98,28 @@ class TimesinceTests(unittest.TestCase):
|
||||||
now_tz = datetime.datetime.now(LocalTimezone(now))
|
now_tz = datetime.datetime.now(LocalTimezone(now))
|
||||||
now_tz_i = datetime.datetime.now(FixedOffset((3 * 60) + 15))
|
now_tz_i = datetime.datetime.now(FixedOffset((3 * 60) + 15))
|
||||||
|
|
||||||
self.assertEqual(timesince(now), '0 minutes')
|
self.assertEqual(timesince(now), '0\xa0minutes')
|
||||||
self.assertEqual(timesince(now_tz), '0 minutes')
|
self.assertEqual(timesince(now_tz), '0\xa0minutes')
|
||||||
self.assertEqual(timeuntil(now_tz, now_tz_i), '0 minutes')
|
self.assertEqual(timeuntil(now_tz, now_tz_i), '0\xa0minutes')
|
||||||
|
|
||||||
def test_date_objects(self):
|
def test_date_objects(self):
|
||||||
""" Both timesince and timeuntil should work on date objects (#17937). """
|
""" Both timesince and timeuntil should work on date objects (#17937). """
|
||||||
today = datetime.date.today()
|
today = datetime.date.today()
|
||||||
self.assertEqual(timesince(today + self.oneday), '0 minutes')
|
self.assertEqual(timesince(today + self.oneday), '0\xa0minutes')
|
||||||
self.assertEqual(timeuntil(today - self.oneday), '0 minutes')
|
self.assertEqual(timeuntil(today - self.oneday), '0\xa0minutes')
|
||||||
|
|
||||||
def test_both_date_objects(self):
|
def test_both_date_objects(self):
|
||||||
""" Timesince should work with both date objects (#9672) """
|
""" Timesince should work with both date objects (#9672) """
|
||||||
today = datetime.date.today()
|
today = datetime.date.today()
|
||||||
self.assertEqual(timeuntil(today + self.oneday, today), '1 day')
|
self.assertEqual(timeuntil(today + self.oneday, today), '1\xa0day')
|
||||||
self.assertEqual(timeuntil(today - self.oneday, today), '0 minutes')
|
self.assertEqual(timeuntil(today - self.oneday, today), '0\xa0minutes')
|
||||||
self.assertEqual(timeuntil(today + self.oneweek, today), '1 week')
|
self.assertEqual(timeuntil(today + self.oneweek, today), '1\xa0week')
|
||||||
|
|
||||||
def test_naive_datetime_with_tzinfo_attribute(self):
|
def test_naive_datetime_with_tzinfo_attribute(self):
|
||||||
class naive(datetime.tzinfo):
|
class naive(datetime.tzinfo):
|
||||||
def utcoffset(self, dt):
|
def utcoffset(self, dt):
|
||||||
return None
|
return None
|
||||||
future = datetime.datetime(2080, 1, 1, tzinfo=naive())
|
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())
|
past = datetime.datetime(1980, 1, 1, tzinfo=naive())
|
||||||
self.assertEqual(timeuntil(past), '0 minutes')
|
self.assertEqual(timeuntil(past), '0\xa0minutes')
|
||||||
|
|
Loading…
Reference in New Issue