mirror of
				https://github.com/django/django.git
				synced 2025-10-31 01:25:32 +00:00 
			
		
		
		
	Fixed #20246 -- Added non-breaking spaces between values an units
This commit is contained in:
		
				
					committed by
					
						 Claude Paroz
						Claude Paroz
					
				
			
			
				
	
			
			
			
						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> | ||||
|     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> | ||||
|   | ||||
| @@ -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} | ||||
|   | ||||
| @@ -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: | ||||
|   | ||||
| @@ -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'): | ||||
|   | ||||
| @@ -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") | ||||
|   | ||||
| @@ -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): | ||||
|   | ||||
| @@ -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), '') | ||||
|   | ||||
| @@ -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"<a>\' <a>\'"), | ||||
|   | ||||
| @@ -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') | ||||
|   | ||||
		Reference in New Issue
	
	Block a user