1"""
2DateInterval.py
3
4Convert interval strings (in the form of 1w2d, etc) to
5seconds, and back again.  Is not exactly about months or
6years (leap years in particular).
7
8Accepts (y)ear, (b)month, (w)eek, (d)ay, (h)our, (m)inute, (s)econd.
9
10Exports only timeEncode and timeDecode functions.
11"""
12
13import re
14
15__all__ = ['interval_decode', 'interval_encode']
16
17second = 1
18minute = second*60
19hour = minute*60
20day = hour*24
21week = day*7
22month = day*30
23year = day*365
24timeValues = {
25    'y': year,
26    'b': month,
27    'w': week,
28    'd': day,
29    'h': hour,
30    'm': minute,
31    's': second,
32    }
33timeOrdered = list(timeValues.items())
34timeOrdered.sort(key=lambda x: x[1], reverse=True)
35
36
37def interval_encode(seconds, include_sign=False):
38    """Encodes a number of seconds (representing a time interval)
39    into a form like 1h2d3s.
40
41    >>> interval_encode(10)
42    '10s'
43    >>> interval_encode(493939)
44    '5d17h12m19s'
45    """
46    s = ''
47    orig = seconds
48    seconds = abs(seconds)
49    for char, amount in timeOrdered:
50        if seconds >= amount:
51            i, seconds = divmod(seconds, amount)
52            s += '%i%s' % (i, char)
53    if orig < 0:
54        s = '-' + s
55    elif not orig:
56        return '0'
57    elif include_sign:
58        s = '+' + s
59    return s
60
61_timeRE = re.compile(r'[0-9]+[a-zA-Z]')
62def interval_decode(s):
63    """Decodes a number in the format 1h4d3m (1 hour, 3 days, 3 minutes)
64    into a number of seconds
65
66    >>> interval_decode('40s')
67    40
68    >>> interval_decode('10000s')
69    10000
70    >>> interval_decode('3d1w45s')
71    864045
72    """
73    time = 0
74    sign = 1
75    s = s.strip()
76    if s.startswith('-'):
77        s = s[1:]
78        sign = -1
79    elif s.startswith('+'):
80        s = s[1:]
81    for match in allMatches(s, _timeRE):
82        char = match.group(0)[-1].lower()
83        if char not in timeValues:
84            # @@: should signal error
85            continue
86        time += int(match.group(0)[:-1]) * timeValues[char]
87    return time
88
89# @@-sgd 2002-12-23 - this function does not belong in this module, find a better place.
90def allMatches(source, regex):
91    """Return a list of matches for regex in source
92    """
93    pos = 0
94    end = len(source)
95    rv = []
96    match = regex.search(source, pos)
97    while match:
98        rv.append(match)
99        match = regex.search(source, match.end() )
100    return rv
101
102if __name__ == '__main__':
103    import doctest
104    doctest.testmod()
105