From: Armin Ronacher Date: Fri, 5 Jul 2013 18:53:51 +0000 (+0200) Subject: Greatly improved timedelta formatting X-Git-Tag: 1.0~119 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5204709d3a3f11ef8c5bb7d0dd64f69ee46b47ab;p=thirdparty%2Fbabel.git Greatly improved timedelta formatting --- diff --git a/babel/dates.py b/babel/dates.py index 642195b9..48e16af0 100644 --- a/babel/dates.py +++ b/babel/dates.py @@ -589,13 +589,15 @@ TIMEDELTA_UNITS = ( ('second', 1) ) -def format_timedelta(delta, granularity='second', threshold=.85, locale=LC_TIME): +def format_timedelta(delta, granularity='second', threshold=.85, + add_direction=False, format='medium', + locale=LC_TIME): """Return a time delta according to the rules of the given locale. >>> format_timedelta(timedelta(weeks=12), locale='en_US') - u'3 mths' + u'3 months' >>> format_timedelta(timedelta(seconds=1), locale='es') - u'1 s' + u'1 segundo' The granularity parameter can be provided to alter the lowest unit presented, which defaults to a second. @@ -611,7 +613,15 @@ def format_timedelta(delta, granularity='second', threshold=.85, locale=LC_TIME) >>> format_timedelta(timedelta(hours=23), threshold=0.9, locale='en_US') u'1 day' >>> format_timedelta(timedelta(hours=23), threshold=1.1, locale='en_US') - u'23 hrs' + u'23 hours' + + In addition directional information can be provided that informs + the user if the date is in the past or in the future: + + >>> format_timedelta(timedelta(hours=1), add_direction=True) + u'In 1 hour' + >>> format_timedelta(timedelta(hours=-1), add_direction=True) + u'1 hour ago' :param delta: a ``timedelta`` object representing the time difference to format, or the delta in seconds as an `int` value @@ -620,15 +630,32 @@ def format_timedelta(delta, granularity='second', threshold=.85, locale=LC_TIME) "hour", "minute" or "second" :param threshold: factor that determines at which point the presentation switches to the next higher unit + :param add_direction: if this flag is set to `True` the return value will + include directional information. For instance a + positive timedelta will include the information about + it being in the future, a negative will be information + about the value being in the past. + :param format: the format (currently only "medium" and "short" are supported) :param locale: a `Locale` object or a locale identifier :rtype: `unicode` """ + if format not in ('short', 'medium'): + raise TypeError('Format can only be one of "short" or "medium"') if isinstance(delta, timedelta): seconds = int((delta.days * 86400) + delta.seconds) else: seconds = delta locale = Locale.parse(locale) + def _iter_choices(unit): + if add_direction: + if seconds >= 0: + yield unit + '-future' + else: + yield unit + '-past' + yield unit + ':' + format + yield unit + for unit, secs_per_unit in TIMEDELTA_UNITS: value = abs(seconds) / secs_per_unit if value >= threshold or unit == granularity: @@ -636,7 +663,15 @@ def format_timedelta(delta, granularity='second', threshold=.85, locale=LC_TIME) value = max(1, value) value = int(round(value)) plural_form = locale.plural_form(value) - pattern = locale._data['unit_patterns'][unit][plural_form] + pattern = None + for choice in _iter_choices(unit): + patterns = locale._data['unit_patterns'].get(choice) + if patterns is not None: + pattern = patterns[plural_form] + break + # This really should not happen + if pattern is None: + return u'' return pattern.replace('{0}', str(value)) return u'' diff --git a/babel/support.py b/babel/support.py index 80f015cf..ba45ad91 100644 --- a/babel/support.py +++ b/babel/support.py @@ -88,17 +88,20 @@ class Format(object): """ return format_time(time, format, tzinfo=self.tzinfo, locale=self.locale) - def timedelta(self, delta, granularity='second', threshold=.85): + def timedelta(self, delta, granularity='second', threshold=.85, + format='medium', add_direction=False): """Return a time delta according to the rules of the given locale. >>> fmt = Format('en_US') >>> fmt.timedelta(timedelta(weeks=11)) - u'3 mths' + u'3 months' :see: `babel.dates.format_timedelta` """ return format_timedelta(delta, granularity=granularity, - threshold=threshold, locale=self.locale) + threshold=threshold, + format=format, add_direction=add_direction, + locale=self.locale) def number(self, number): """Return an integer number formatted for the locale. diff --git a/babel/tests/dates.py b/babel/tests/dates.py index ece32aad..75e7e119 100644 --- a/babel/tests/dates.py +++ b/babel/tests/dates.py @@ -281,16 +281,37 @@ class FormatTimedeltaTestCase(unittest.TestCase): def test_zero_seconds(self): string = dates.format_timedelta(timedelta(seconds=0), locale='en') + self.assertEqual('0 seconds', string) + string = dates.format_timedelta(timedelta(seconds=0), locale='en', + format='short') self.assertEqual('0 secs', string) string = dates.format_timedelta(timedelta(seconds=0), granularity='hour', locale='en') + self.assertEqual('0 hours', string) + string = dates.format_timedelta(timedelta(seconds=0), + granularity='hour', locale='en', + format='short') self.assertEqual('0 hrs', string) def test_small_value_with_granularity(self): string = dates.format_timedelta(timedelta(seconds=42), granularity='hour', locale='en') + self.assertEqual('1 hour', string) + string = dates.format_timedelta(timedelta(seconds=42), + granularity='hour', locale='en', + format='short') self.assertEqual('1 hr', string) + def test_direction_adding(self): + string = dates.format_timedelta(timedelta(hours=1), + locale='en', + add_direction=True) + self.assertEqual('In 1 hour', string) + string = dates.format_timedelta(timedelta(hours=-1), + locale='en', + add_direction=True) + self.assertEqual('1 hour ago', string) + class TimeZoneAdjustTestCase(unittest.TestCase): def _utc(self): diff --git a/scripts/import_cldr.py b/scripts/import_cldr.py index 8c9ee8d8..fbf9f9b7 100755 --- a/scripts/import_cldr.py +++ b/scripts/import_cldr.py @@ -527,9 +527,11 @@ def main(): unit_patterns = data.setdefault('unit_patterns', {}) for elem in tree.findall('.//units/unit'): unit_type = elem.attrib['type'] - unit_pattern = unit_patterns.setdefault(unit_type, {}) for pattern in elem.findall('unitPattern'): - unit_patterns[unit_type][pattern.attrib['count']] = \ + box = unit_type + if 'alt' in pattern.attrib: + box += ':' + pattern.attrib['alt'] + unit_patterns.setdefault(box, {})[pattern.attrib['count']] = \ unicode(pattern.text) outfile = open(data_filename, 'wb')