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