Welcome, guest | Sign In | My Account | Store | Cart

You want to use nicely formatted timedeltas as an alternative to using absolute dates. Instead of writing "Last modified 2006-07-01" you want that to read "Modified two months ago" Or you are working for a credit card company and you want to send threatening letters; "Dear sir or madam, two weeks ago we contacted you about your overdue credit..."

Python, 131 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
-------- texttime.py -----------

# -*- coding: utf-8 -*-
from datetime import timedelta

# Set this to the language you want to use.
LANG = "sv"

# Singular and plural forms of time units in your language.
unit_names = dict(sv = {"year" : ("år", "år"),
                        "month" : ("månad", "månader"),
                        "week" : ("vecka", "veckor"),
                        "day" : ("dag", "dagar"),
                        "hour" : ("timme", "timmar"),
                        "minute" : ("minut", "minuter"),
                        "second" : ("sekund", "sekunder")},
                  en = {"year" : ("year", "years"),
                        "month" : ("month", "months"),
                        "week" : ("week", "weeks"),
                        "day" : ("day", "days"),
                        "hour" : ("hour", "hours"),
                        "minute" : ("minute", "minutes"),
                        "second" : ("second", "seconds")})
                  
num_repr = dict(sv = {1 : "en",
                      2 : "två",
                      3 : "tre",
                      4 : "fyra",
                      5 : "fem",
                      6 : "sex",
                      7 : "sju",
                      8 : "åtta",
                      9 : "nio",
                      10 : "tio",
                      11 : "elva",
                      12 : "tolv"},
                en = {1 : "one",
                      2 : "two",
                      3 : "three",
                      4 : "four",
                      5 : "five",
                      6 : "six",
                      7 : "seven",
                      8 : "eight",
                      9 : "nine",
                      10 : "ten",
                      11 : "eleven",
                      12 : "twelve"})

def amount_to_str(amount, unit_name):
    # This is the Swedish hack. The Swedish language has two words for
    # "one" - "en" and "ett". Sometimes "en" is used and other times
    # "ett" is used. For the word "år," "ett" is used instead of "en."
    # No doubt other languages contain similar weirdness.
    if amount == 1 and unit_name == "year" and LANG == "sv":
        return "ett"
    if amount in num_repr[LANG]:
        return num_repr[LANG][amount]
    return str(amount)

def seconds_in_units(seconds):
    """
    Returns a tuple containing the most appropriate unit for the
    number of seconds supplied and the value in that units form.

        >>> seconds_in_units(7700)
        (2, 'hour')
    """
    unit_limits = [("year", 365 * 24 * 3600),
                   ("month", 30 * 24 * 3600),
                   ("week", 7 * 24 * 3600),
                   ("day", 24 * 3600),
                   ("hour", 3600),
                   ("minute", 60)]
    for unit_name, limit in unit_limits:
        if seconds >= limit:
            amount = int(round(float(seconds) / limit))
            return amount, unit_name
    return seconds, "second"

def stringify(td):
    """
    Converts a timedelta into a nicely readable string.

        >>> td = timedelta(days = 77, seconds = 5)
        >>> print readable_timedelta(td)
        two months
    """
    seconds = td.days * 3600 * 24 + td.seconds
    amount, unit_name = seconds_in_units(seconds)

    # Localize it.
    i18n_amount = amount_to_str(amount, unit_name)
    i18n_unit = unit_names[LANG][unit_name][1]
    if amount == 1:
        i18n_unit = unit_names[LANG][unit_name][0]
    return "%s %s" % (i18n_amount, i18n_unit)
    
def test(td):
    if td.days > 100:
        fmt = "In %s, it's a long time. (%s)"
    elif td.days > 4:
        fmt = "I've only got %s to finish the project. (%s)"
    elif td.days > 0:
        fmt = "The party was %s ago. (%s)"
    elif td.seconds > 3600:
        fmt = "Something weird happened %s ago. (%s)"
    elif td.seconds > 60:
        fmt = "The train arrives in %s. (%s)"
    else:
        fmt = "%s passes fast. (%s)"
    print fmt % (stringify(td), str(td))

def main():
    global LANG
    LANG = "en"
    test(timedelta(weeks = 7, days = 3))
    test(timedelta(weeks = 1))
    test(timedelta(days = 1000))
    test(timedelta(days = 400))
    test(timedelta(days = 4))
    test(timedelta(seconds = 2000))
    test(timedelta(seconds = 9888))
    test(timedelta(seconds = 999888))
    test(timedelta(seconds = 999))
    test(timedelta(seconds = 99))
    test(timedelta(seconds = 45))
    test(timedelta(seconds = 3))

if __name__ == "__main__":
    main()

Usage:

>>> from datetime import date
>>> import texttime
>>> texttime.LANG = "en"
>>> print texttime.stringify(date.today() - date(2006, 8, 15))
three weeks

This recipe is useful when you want to print a timedelta (subtracting two dates yield a timedelta) in a more readable form than what str(timedelta) outputs. For many purpouses one unit is accurate enough. The output is only localized to Swedish and English but it should be fairly easy to add translations for other latin languages.

This recipe works by first "rounding" the timedelta to the most fitting unit. Then the time measurement is converted to a string by first localizing it. The algorithm could be reused for converting, say 2153139, to "two megabytes"

Created by Björn Lindqvist on Sat, 2 Sep 2006 (PSF)
Python recipes (4591)
Björn Lindqvist's recipes (2)

Required Modules

  • (none specified)

Other Information and Tasks