]> git.ipfire.org Git - thirdparty/babel.git/commitdiff
Greatly improved timedelta formatting
authorArmin Ronacher <armin.ronacher@active-4.com>
Fri, 5 Jul 2013 18:53:51 +0000 (20:53 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Fri, 5 Jul 2013 18:53:51 +0000 (20:53 +0200)
babel/dates.py
babel/support.py
babel/tests/dates.py
scripts/import_cldr.py

index 642195b92e38a274074098b1c21ca4abffac3570..48e16af098d4970e6e854c2aa0b719a752b58efc 100644 (file)
@@ -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''
index 80f015cff43d17584cd801bdc9522fbc42cd6791..ba45ad91501cacaeafd6fc4c9ae8e33a8916ea99 100644 (file)
@@ -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.
index ece32aada3c63b616545da80e194f9d37889fb21..75e7e11902280634087a12c997122586ae81ab8c 100644 (file)
@@ -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):
index 8c9ee8d8663c4277c2fdabcc5b2bd4b259bbe962..fbf9f9b78836a7cfa3bb8e54f068d858b3a1861a 100755 (executable)
@@ -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')