Home | History | Annotate | Download | only in util
      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 = list(timeValues.items())
     34 timeOrdered.sort(key=lambda x: x[1], reverse=True)
     35 
     36 
     37 def 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]')
     62 def 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.
     90 def 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 
    102 if __name__ == '__main__':
    103     import doctest
    104     doctest.testmod()
    105