Ticket #12194: natural_timedelta.2.diff

File natural_timedelta.2.diff, 8.3 KB (added by stemmetje, 15 years ago)

A slightly better version of the code filter.

  • django/contrib/humanize/templatetags/humanize.py

     
    22from django.utils.encoding import force_unicode
    33from django import template
    44from django.template import defaultfilters
    5 from datetime import date
     5import datetime
    66import re
    77
    88register = template.Library()
     
    8181    formatted according to settings.DATE_FORMAT.
    8282    """
    8383    try:
    84         value = date(value.year, value.month, value.day)
     84        value = datetime.date(value.year, value.month, value.day)
    8585    except AttributeError:
    8686        # Passed value wasn't a date object
    8787        return value
    8888    except ValueError:
    8989        # Date arguments out of range
    9090        return value
    91     delta = value - date.today()
     91    delta = value - datetime.date.today()
    9292    if delta.days == 0:
    9393        return _(u'today')
    9494    elif delta.days == 1:
     
    9797        return _(u'yesterday')
    9898    return defaultfilters.date(value, arg)
    9999register.filter(naturalday)
     100
     101def natural_timedelta(time1, time2=datetime.datetime.now()):
     102    """
     103    Express the difference in time between time1 and time2 (which defaults to
     104    now) in a human-friendly way (e.g. 'about a minute').
     105    """
     106    try:
     107        delta = abs(time1 - time2)
     108        delta.days
     109    except (ValueError, AttributeError, TypeError):
     110        return time1
     111    if delta.days == 0:
     112        delta_days = 0
     113        delta_minutes = int(round(delta.seconds / 60.0))
     114        if delta.seconds in range(0, 50):
     115            if delta.seconds in range(0, 5):
     116                return _(u'less than %(count)d seconds') % {'count': 5}
     117            elif delta.seconds in range(5, 10):
     118                return _(u'less than %(count)d seconds') % {'count': 10}
     119            elif delta.seconds in range(10, 20):
     120                return _(u'less than %(count)d seconds') % {'count': 20}
     121            elif delta.seconds in range(20, 40):
     122                return _(u'about half a minute')
     123            elif delta.seconds in range(40, 50):
     124                return _(u'less than a minute')
     125        elif delta_minutes in range(1, 20):
     126            return ungettext(u'about a minute', u'about %(count)d minutes', delta_minutes) % {'count': delta_minutes}
     127        elif delta_minutes in range(20, 45):
     128            return _(u'about half an hour')
     129        elif delta_minutes in range(45, 90):
     130            return _(u'about an hour')
     131        elif delta_minutes in range(90, 1440):
     132            return _(u'about %(count)d hours') % {'count': int(round(delta_minutes / 60.0))}
     133    else:
     134        # Get a rounded number of days instead of timedelta's truncated days.
     135        delta_days = int(round(delta.days + (delta.seconds / 86400.0)))
     136    if delta_days in range(1, 30):
     137        return ungettext(u'about a day', u'about %(count)d days', delta_days) % {'count': delta_days}
     138    elif delta_days in range(30, 365):
     139        count = round(delta_days / 30.4)
     140        return ungettext(u'about a month', u'about %(count)d months', count) % {'count': count}
     141    elif delta_days >= 365:
     142        count = round(delta_days / 365.25)
     143        return ungettext(u'about a year', u'about %(count)d years', count) % {'count': count}
     144register.filter(natural_timedelta)
  • tests/regressiontests/humanize/tests.py

     
    11import unittest
    2 from datetime import timedelta, date
     2import datetime
    33from django.template import Template, Context, add_to_builtins
    44from django.utils.dateformat import DateFormat
    55from django.utils.translation import ugettext as _
     
    5454
    5555    def test_naturalday(self):
    5656        from django.template import defaultfilters
    57         today = date.today()
    58         yesterday = today - timedelta(days=1)
    59         tomorrow = today + timedelta(days=1)
    60         someday = today - timedelta(days=10)
     57        today = datetime.date.today()
     58        yesterday = today - datetime.timedelta(days=1)
     59        tomorrow = today + datetime.timedelta(days=1)
     60        someday = today - datetime.timedelta(days=10)
    6161        notdate = u"I'm not a date value"
    6262
    6363        test_list = (today, yesterday, tomorrow, someday, notdate)
     
    6565        result_list = (_(u'today'), _(u'yesterday'), _(u'tomorrow'),
    6666                       someday_result, u"I'm not a date value")
    6767        self.humanize_tester(test_list, result_list, 'naturalday')
     68   
     69    def test_natural_timedelta(self):
     70        now = datetime.datetime.now()
     71       
     72        test_list = (
     73            now,
     74            now + datetime.timedelta(microseconds=4),
     75            now + datetime.timedelta(seconds=4),
     76            now + datetime.timedelta(seconds=9),
     77            now + datetime.timedelta(seconds=19),
     78            now + datetime.timedelta(seconds=31),
     79            now + datetime.timedelta(seconds=45),
     80            now + datetime.timedelta(seconds=55),
     81            now + datetime.timedelta(seconds=60),
     82            now + datetime.timedelta(seconds=121),
     83            now + datetime.timedelta(seconds=400),
     84            now + datetime.timedelta(seconds=1800),
     85            now + datetime.timedelta(seconds=2700),
     86            now + datetime.timedelta(seconds=3660),
     87            now + datetime.timedelta(seconds=11880),
     88            now + datetime.timedelta(seconds=82800),
     89            now + datetime.timedelta(days=1),
     90            now + datetime.timedelta(days=1.5),
     91            now + datetime.timedelta(days=29),
     92            now + datetime.timedelta(days=30),
     93            now + datetime.timedelta(days=60),
     94            now + datetime.timedelta(days=340),
     95            now + datetime.timedelta(days=365),
     96            now + datetime.timedelta(days=730.5),
     97            now + datetime.timedelta(days=2922),
     98            now - datetime.timedelta(seconds=45),
     99            # Non-time-related inputs.
     100            u'spam',
     101            1,
     102        )
     103        result_list = (
     104            _(u'less than 5 seconds'),
     105            _(u'less than 5 seconds'),
     106            _(u'less than 5 seconds'),
     107            _(u'less than 10 seconds'),
     108            _(u'less than 20 seconds'),
     109            _(u'about half a minute'),
     110            _(u'less than a minute'),
     111            _(u'about a minute'),
     112            _(u'about a minute'),
     113            _(u'about 2 minutes'),
     114            _(u'about 7 minutes'),
     115            _(u'about half an hour'),
     116            _(u'about an hour'),
     117            _(u'about an hour'),
     118            _(u'about 3 hours'),
     119            _(u'about 23 hours'),
     120            _(u'about a day'),
     121            _(u'about 2 days'),
     122            _(u'about 29 days'),
     123            _(u'about a month'),
     124            _(u'about 2 months'),
     125            _(u'about 11 months'),
     126            _(u'about a year'),
     127            _(u'about 2 years'),
     128            _(u'about 8 years'),
     129            _(u'less than a minute'),
     130            # Non-time-related outputs.
     131            u'spam',
     132            1,
     133        )
     134        self.humanize_tester(test_list, result_list, 'natural_timedelta')
    68135
    69136if __name__ == '__main__':
    70137    unittest.main()
  • docs/ref/contrib/humanize.txt

     
    8989    * ``18 Feb 2007`` becomes ``tomorrow``.
    9090    * Any other day is formatted according to given argument or the
    9191      :setting:`DATE_FORMAT` setting if no argument is given.
     92
     93natural_timedelta
     94-----------------
     95
     96.. versionadded:: 1.2
     97
     98Determine the difference between one date and/or time object and another and
     99return that difference in a human-friendly format.
     100
     101**Argument:** A date and/or time object (i.e. ``datetime.date``,
     102``datetime.time`` or ``datetime.datetime``). Defaults to
     103``datetime.datetime.now()`` if not given.
     104
     105Examples:
     106
     107    * A difference of 9 seconds becomes ``less than 10 seconds``.
     108    * A difference of 30 seconds becomes ``about half a minute``.
     109    * A difference of 1 minute, 10 seconds becomes ``about a minute``.
     110    * A difference of 30 days becomes ``about a month``.
     111    * A difference of 365 days becomes ``about a year``.
Back to Top