comparison roundup/date.py @ 1597:4d55f90d4af1

granularity based ranges
author Andrey Lebedev <kedder@users.sourceforge.net>
date Mon, 21 Apr 2003 14:29:40 +0000
parents 3faabaeab6aa
children cc96bf971b33
comparison
equal deleted inserted replaced
1596:33a0d94c7658 1597:4d55f90d4af1
13 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 13 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
14 # FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" 14 # FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
15 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, 15 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
16 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 16 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
17 # 17 #
18 # $Id: date.py,v 1.51 2003-03-22 22:43:21 richard Exp $ 18 # $Id: date.py,v 1.52 2003-04-21 14:29:39 kedder Exp $
19 19
20 __doc__ = """ 20 __doc__ = """
21 Date, time and time interval handling. 21 Date, time and time interval handling.
22 """ 22 """
23 23
24 import time, re, calendar, types 24 import time, re, calendar, types
25 from i18n import _ 25 from i18n import _
26
27 def _add_granularity(src, order, value = 1):
28 '''Increment first non-None value in src dictionary ordered by 'order'
29 parameter
30 '''
31 for gran in order:
32 if src[gran]:
33 src[gran] = int(src[gran]) + value
34 break
26 35
27 class Date: 36 class Date:
28 ''' 37 '''
29 As strings, date-and-time stamps are specified with the date in 38 As strings, date-and-time stamps are specified with the date in
30 international standard format (yyyy-mm-dd) joined to the time 39 international standard format (yyyy-mm-dd) joined to the time
78 87
79 The date format 'yyyymmddHHMMSS' (year, month, day, hour, 88 The date format 'yyyymmddHHMMSS' (year, month, day, hour,
80 minute, second) is the serialisation format returned by the serialise() 89 minute, second) is the serialisation format returned by the serialise()
81 method, and is accepted as an argument on instatiation. 90 method, and is accepted as an argument on instatiation.
82 ''' 91 '''
83 def __init__(self, spec='.', offset=0): 92 def __init__(self, spec='.', offset=0, add_granularity=0):
84 """Construct a date given a specification and a time zone offset. 93 """Construct a date given a specification and a time zone offset.
85 94
86 'spec' is a full date or a partial form, with an optional 95 'spec' is a full date or a partial form, with an optional
87 added or subtracted interval. Or a date 9-tuple. 96 added or subtracted interval. Or a date 9-tuple.
88 'offset' is the local time zone offset from GMT in hours. 97 'offset' is the local time zone offset from GMT in hours.
89 """ 98 """
90 if type(spec) == type(''): 99 if type(spec) == type(''):
91 self.set(spec, offset=offset) 100 self.set(spec, offset=offset, add_granularity=add_granularity)
92 else: 101 else:
93 y,m,d,H,M,S,x,x,x = spec 102 y,m,d,H,M,S,x,x,x = spec
94 ts = calendar.timegm((y,m,d,H+offset,M,S,0,0,0)) 103 ts = calendar.timegm((y,m,d,H+offset,M,S,0,0,0))
95 self.year, self.month, self.day, self.hour, self.minute, \ 104 self.year, self.month, self.day, self.hour, self.minute, \
96 self.second, x, x, x = time.gmtime(ts) 105 self.second, x, x, x = time.gmtime(ts)
100 (?P<n>\.)? # . 109 (?P<n>\.)? # .
101 (((?P<H>\d?\d):(?P<M>\d\d))?(:(?P<S>\d\d))?)? # hh:mm:ss 110 (((?P<H>\d?\d):(?P<M>\d\d))?(:(?P<S>\d\d))?)? # hh:mm:ss
102 (?P<o>.+)? # offset 111 (?P<o>.+)? # offset
103 ''', re.VERBOSE), serialised_re=re.compile(r''' 112 ''', re.VERBOSE), serialised_re=re.compile(r'''
104 (\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d) 113 (\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)
105 ''', re.VERBOSE)): 114 ''', re.VERBOSE), add_granularity=0):
106 ''' set the date to the value in spec 115 ''' set the date to the value in spec
107 ''' 116 '''
117
108 m = serialised_re.match(spec) 118 m = serialised_re.match(spec)
109 if m is not None: 119 if m is not None:
110 # we're serialised - easy! 120 # we're serialised - easy!
111 self.year, self.month, self.day, self.hour, self.minute, \ 121 self.year, self.month, self.day, self.hour, self.minute, \
112 self.second = map(int, m.groups()[:6]) 122 self.second = map(int, m.groups()[:6])
117 if m is None: 127 if m is None:
118 raise ValueError, _('Not a date spec: [[yyyy-]mm-dd].' 128 raise ValueError, _('Not a date spec: [[yyyy-]mm-dd].'
119 '[[h]h:mm[:ss]][offset]') 129 '[[h]h:mm[:ss]][offset]')
120 130
121 info = m.groupdict() 131 info = m.groupdict()
132
133 if add_granularity:
134 _add_granularity(info, 'SMHdmy')
122 135
123 # get the current date as our default 136 # get the current date as our default
124 y,m,d,H,M,S,x,x,x = time.gmtime(time.time()) 137 y,m,d,H,M,S,x,x,x = time.gmtime(time.time())
125 138
126 # override year, month, day parts 139 # override year, month, day parts
138 H = int(info['H']) - offset 151 H = int(info['H']) - offset
139 M = int(info['M']) 152 M = int(info['M'])
140 S = 0 153 S = 0
141 if info['S'] is not None: S = int(info['S']) 154 if info['S'] is not None: S = int(info['S'])
142 155
156 if add_granularity:
157 S = S - 1
158
143 # now handle the adjustment of hour 159 # now handle the adjustment of hour
144 ts = calendar.timegm((y,m,d,H,M,S,0,0,0)) 160 ts = calendar.timegm((y,m,d,H,M,S,0,0,0))
145 self.year, self.month, self.day, self.hour, self.minute, \ 161 self.year, self.month, self.day, self.hour, self.minute, \
146 self.second, x, x, x = time.gmtime(ts) 162 self.second, x, x, x = time.gmtime(ts)
147 163
343 minute, second) is the serialisation format returned by the serialise() 359 minute, second) is the serialisation format returned by the serialise()
344 method, and is accepted as an argument on instatiation. 360 method, and is accepted as an argument on instatiation.
345 361
346 TODO: more examples, showing the order of addition operation 362 TODO: more examples, showing the order of addition operation
347 ''' 363 '''
348 def __init__(self, spec, sign=1, allowdate=1): 364 def __init__(self, spec, sign=1, allowdate=1, add_granularity=0):
349 """Construct an interval given a specification.""" 365 """Construct an interval given a specification."""
350 if type(spec) == type(''): 366 if type(spec) == type(''):
351 self.set(spec, allowdate) 367 self.set(spec, allowdate=allowdate, add_granularity=add_granularity)
352 else: 368 else:
353 if len(spec) == 7: 369 if len(spec) == 7:
354 self.sign, self.year, self.month, self.day, self.hour, \ 370 self.sign, self.year, self.month, self.day, self.hour, \
355 self.minute, self.second = spec 371 self.minute, self.second = spec
356 else: 372 else:
370 (\d\d\d\d[/-])?(\d\d?)?[/-](\d\d?)? # [yyyy-]mm-dd 386 (\d\d\d\d[/-])?(\d\d?)?[/-](\d\d?)? # [yyyy-]mm-dd
371 \.? # . 387 \.? # .
372 (\d?\d:\d\d)?(:\d\d)? # hh:mm:ss 388 (\d?\d:\d\d)?(:\d\d)? # hh:mm:ss
373 )?''', re.VERBOSE), serialised_re=re.compile(''' 389 )?''', re.VERBOSE), serialised_re=re.compile('''
374 (?P<s>[+-])?1?(?P<y>([ ]{3}\d|\d{4}))(?P<m>\d{2})(?P<d>\d{2}) 390 (?P<s>[+-])?1?(?P<y>([ ]{3}\d|\d{4}))(?P<m>\d{2})(?P<d>\d{2})
375 (?P<H>\d{2})(?P<M>\d{2})(?P<S>\d{2})''', re.VERBOSE)): 391 (?P<H>\d{2})(?P<M>\d{2})(?P<S>\d{2})''', re.VERBOSE),
392 add_granularity=0):
376 ''' set the date to the value in spec 393 ''' set the date to the value in spec
377 ''' 394 '''
378 self.year = self.month = self.week = self.day = self.hour = \ 395 self.year = self.month = self.week = self.day = self.hour = \
379 self.minute = self.second = 0 396 self.minute = self.second = 0
380 self.sign = 1 397 self.sign = 1
387 else: 404 else:
388 allowdate = 0 405 allowdate = 0
389 406
390 # pull out all the info specified 407 # pull out all the info specified
391 info = m.groupdict() 408 info = m.groupdict()
409 if add_granularity:
410 _add_granularity(info, 'SMHdwmy', (info['s']=='-' and -1 or 1))
411
392 valid = 0 412 valid = 0
393 for group, attr in {'y':'year', 'm':'month', 'w':'week', 'd':'day', 413 for group, attr in {'y':'year', 'm':'month', 'w':'week', 'd':'day',
394 'H':'hour', 'M':'minute', 'S':'second'}.items(): 414 'H':'hour', 'M':'minute', 'S':'second'}.items():
395 if info.get(group, None) is not None: 415 if info.get(group, None) is not None:
396 valid = 1 416 valid = 1
652 672
653 >>> Range("; 20:00 +1d") 673 >>> Range("; 20:00 +1d")
654 <Range from None to 2003-03-09.20:00:00> 674 <Range from None to 2003-03-09.20:00:00>
655 675
656 """ 676 """
657 def __init__(self, spec, Type, **params): 677 def __init__(self, spec, Type, allow_granularity=1, **params):
658 """Initializes Range of type <Type> from given <spec> string. 678 """Initializes Range of type <Type> from given <spec> string.
659 679
660 Sets two properties - from_value and to_value. None assigned to any of 680 Sets two properties - from_value and to_value. None assigned to any of
661 this properties means "infinitum" (-infinitum to from_value and 681 this properties means "infinitum" (-infinitum to from_value and
662 +infinitum to to_value) 682 +infinitum to to_value)
664 The Type parameter here should be class itself (e.g. Date), not a 684 The Type parameter here should be class itself (e.g. Date), not a
665 class instance. 685 class instance.
666 686
667 """ 687 """
668 self.range_type = Type 688 self.range_type = Type
669 re_range = r'(?:^|(?:from)?(.+?))(?:to(.+?)$|$)' 689 re_range = r'(?:^|from(.+?))(?:to(.+?)$|$)'
670 re_geek_range = r'(?:^|(.+?))(?:;(.+?)$|$)' 690 re_geek_range = r'(?:^|(.+?));(?:(.+?)$|$)'
671 # Check which syntax to use 691 # Check which syntax to use
672 if spec.find(';') == -1: 692 if spec.find(';') == -1:
673 # Native english 693 # Native english
674 mch_range = re.search(re_range, spec.strip(), re.IGNORECASE) 694 mch_range = re.search(re_range, spec.strip(), re.IGNORECASE)
675 else: 695 else:
680 if self.from_value: 700 if self.from_value:
681 self.from_value = Type(self.from_value.strip(), **params) 701 self.from_value = Type(self.from_value.strip(), **params)
682 if self.to_value: 702 if self.to_value:
683 self.to_value = Type(self.to_value.strip(), **params) 703 self.to_value = Type(self.to_value.strip(), **params)
684 else: 704 else:
685 raise ValueError, "Invalid range" 705 if allow_granularity:
706 self.from_value = Type(spec, **params)
707 self.to_value = Type(spec, add_granularity=1, **params)
708 else:
709 raise ValueError, "Invalid range"
686 710
687 def __str__(self): 711 def __str__(self):
688 return "from %s to %s" % (self.from_value, self.to_value) 712 return "from %s to %s" % (self.from_value, self.to_value)
689 713
690 def __repr__(self): 714 def __repr__(self):
691 return "<Range %s>" % self.__str__() 715 return "<Range %s>" % self.__str__()
692 716
693 def test_range(): 717 def test_range():
694 rspecs = ("from 2-12 to 4-2", "18:00 TO +2m", "12:00", "tO +3d", 718 rspecs = ("from 2-12 to 4-2", "from 18:00 TO +2m", "12:00;", "tO +3d",
695 "2002-11-10; 2002-12-12", "; 20:00 +1d") 719 "2002-11-10; 2002-12-12", "; 20:00 +1d", '2002-10-12')
720 rispecs = ('from -1w 2d 4:32 to 4d', '-2w 1d')
696 for rspec in rspecs: 721 for rspec in rspecs:
697 print '>>> Range("%s")' % rspec 722 print '>>> Range("%s")' % rspec
698 print `Range(rspec, Date)` 723 print `Range(rspec, Date)`
724 print
725 for rspec in rispecs:
726 print '>>> Range("%s")' % rspec
727 print `Range(rspec, Interval)`
699 print 728 print
700 729
701 def test(): 730 def test():
702 intervals = (" 3w 1 d 2:00", " + 2d", "3w") 731 intervals = (" 3w 1 d 2:00", " + 2d", "3w")
703 for interval in intervals: 732 for interval in intervals:
704 print '>>> Interval("%s")'%interval 733 print '>>> Interval("%s")'%interval
705 print `Interval(interval)` 734 print `Interval(interval)`
706 735
707 dates = (".", "2000-06-25.19:34:02", ". + 2d", "1997-04-17", "01-25", 736 dates = (".", "2000-06-25.19:34:02", ". + 2d", "1997-04-17", "01-25",
708 "08-13.22:13", "14:25") 737 "08-13.22:13", "14:25", '2002-12')
709 for date in dates: 738 for date in dates:
710 print '>>> Date("%s")'%date 739 print '>>> Date("%s")'%date
711 print `Date(date)` 740 print `Date(date)`
712 741
713 sums = ((". + 2d", "3w"), (".", " 3w 1 d 2:00")) 742 sums = ((". + 2d", "3w"), (".", " 3w 1 d 2:00"))
714 for date, interval in sums: 743 for date, interval in sums:
715 print '>>> Date("%s") + Interval("%s")'%(date, interval) 744 print '>>> Date("%s") + Interval("%s")'%(date, interval)
716 print `Date(date) + Interval(interval)` 745 print `Date(date) + Interval(interval)`
717 746
718 if __name__ == '__main__': 747 if __name__ == '__main__':
719 test_range() 748 test()
720 749
721 # vim: set filetype=python ts=4 sw=4 et si 750 # vim: set filetype=python ts=4 sw=4 et si

Roundup Issue Tracker: http://roundup-tracker.org/