1515# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717#
18- # $Id: date.py,v 1.18 2002-01-23 20:00:50 jhermann Exp $
18+ # $Id: date.py,v 1.19 2002-02-21 23:11:45 richard Exp $
1919
2020__doc__ = """
2121Date, time and time interval handling.
@@ -104,14 +104,52 @@ def applyInterval(self, interval):
104104 self .second , x , x , x = time .gmtime (calendar .timegm (t ))
105105
106106 def __add__ (self , other ):
107- """Add an interval to this date to produce another date."""
108- t = (self .year + other .sign * other .year ,
109- self .month + other .sign * other .month ,
110- self .day + other .sign * other .day ,
111- self .hour + other .sign * other .hour ,
112- self .minute + other .sign * other .minute ,
113- self .second + other .sign * other .second , 0 , 0 , 0 )
114- return Date (time .gmtime (calendar .timegm (t )))
107+ """Add an interval to this date to produce another date.
108+ """
109+ # do the basic calc
110+ sign = other .sign
111+ year = self .year + sign * other .year
112+ month = self .month + sign * other .month
113+ day = self .day + sign * other .day
114+ hour = self .hour + sign * other .hour
115+ minute = self .minute + sign * other .minute
116+ second = self .second + sign * other .second
117+
118+ # now cope with under- and over-flow
119+ # first do the time
120+ while (second < 0 or second > 59 or minute < 0 or minute > 59 or
121+ hour < 0 or hour > 59 ):
122+ if second < 0 : minute -= 1 ; second += 60
123+ elif second > 59 : minute += 1 ; second -= 60
124+ if minute < 0 : hour -= 1 ; minute += 60
125+ elif minute > 59 : hour += 1 ; minute -= 60
126+ if hour < 0 : day -= 1 ; hour += 60
127+ elif hour > 59 : day += 1 ; hour -= 60
128+
129+ # fix up the month so we're within range
130+ while month < 1 or month > 12 :
131+ if month < 1 : year -= 1 ; month += 12
132+ if month > 12 : year += 1 ; month -= 12
133+
134+ # now do the days, now that we know what month we're in
135+ mdays = calendar .mdays
136+ if month == 2 and calendar .isleap (year ): month_days = 29
137+ else : month_days = mdays [month ]
138+ while month < 1 or month > 12 or day < 0 or day > month_days :
139+ # now to day under/over
140+ if day < 0 : month -= 1 ; day += month_days
141+ elif day > month_days : month += 1 ; day -= month_days
142+
143+ # possibly fix up the month so we're within range
144+ while month < 1 or month > 12 :
145+ if month < 1 : year -= 1 ; month += 12
146+ if month > 12 : year += 1 ; month -= 12
147+
148+ # re-figure the number of days for this month
149+ if month == 2 and calendar .isleap (year ): month_days = 29
150+ else : month_days = mdays [month ]
151+
152+ return Date ((year , month , day , hour , minute , second , 0 , 0 , 0 ))
115153
116154 # XXX deviates from spec to allow subtraction of dates as well
117155 def __sub__ (self , other ):
@@ -124,14 +162,14 @@ def __sub__(self, other):
124162 # leap years, phases of the moon, ....
125163 a = calendar .timegm ((self .year , self .month , self .day , self .hour ,
126164 self .minute , self .second , 0 , 0 , 0 ))
127- b = calendar .timegm ((other .year , other .month , other .day , other . hour ,
128- other .minute , other .second , 0 , 0 , 0 ))
165+ b = calendar .timegm ((other .year , other .month , other .day ,
166+ other .hour , other . minute , other .second , 0 , 0 , 0 ))
129167 diff = a - b
130168 if diff < 0 :
131- sign = - 1
169+ sign = 1
132170 diff = - diff
133171 else :
134- sign = 1
172+ sign = - 1
135173 S = diff % 60
136174 M = (diff / 60 )% 60
137175 H = (diff / (60 * 60 ))% 60
@@ -143,13 +181,7 @@ def __sub__(self, other):
143181 y = (diff / (365 * 24 * 60 * 60 ))
144182 if y > 1 : d = H = S = M = 0
145183 return Interval ((y , m , d , H , M , S ), sign = sign )
146- t = (self .year - other .sign * other .year ,
147- self .month - other .sign * other .month ,
148- self .day - other .sign * other .day ,
149- self .hour - other .sign * other .hour ,
150- self .minute - other .sign * other .minute ,
151- self .second - other .sign * other .second , 0 , 0 , 0 )
152- return Date (time .gmtime (calendar .timegm (t )))
184+ return self .__add__ (other )
153185
154186 def __cmp__ (self , other ):
155187 """Compare this date to another date."""
@@ -244,8 +276,17 @@ class Interval:
244276 Example usage:
245277 >>> Interval(" 3w 1 d 2:00")
246278 <Interval 22d 2:00>
247- >>> Date(". + 2d") - Interval("3w")
279+ >>> Date(". + 2d") + Interval("- 3w")
248280 <Date 2000-06-07.00:34:02>
281+
282+ Intervals are added/subtracted in order of:
283+ seconds, minutes, hours, years, months, days
284+
285+ Calculations involving monts (eg '+2m') have no effect on days - only
286+ days (or over/underflow from hours/mins/secs) will do that, and
287+ days-per-month and leap years are accounted for. Leap seconds are not.
288+
289+ TODO: more examples, showing the order of addition operation
249290 '''
250291 def __init__ (self , spec , sign = 1 ):
251292 """Construct an interval given a specification."""
@@ -290,7 +331,7 @@ def set(self, spec, interval_re = re.compile('''
290331 \s*
291332 ((?P<d>\d+\s*)d)? # day
292333 \s*
293- (((?P<H>\d?\d ):(?P<M>\d\d ))?(:(?P<S>\d\d ))?)? # time
334+ (((?P<H>\d+ ):(?P<M>\d+ ))?(:(?P<S>\d+ ))?)? # time
294335 \s*
295336 ''' , re .VERBOSE )):
296337 ''' set the date to the value in spec
@@ -323,40 +364,47 @@ def pretty(self):
323364 '''
324365 if self .year or self .month > 2 :
325366 return None
326- if self .month or self .day > 13 :
367+ elif self .month or self .day > 13 :
327368 days = (self .month * 30 ) + self .day
328369 if days > 28 :
329370 if int (days / 30 ) > 1 :
330- return _ ('%(number)s months' )% {'number' : int (days / 30 )}
371+ s = _ ('%(number)s months' )% {'number' : int (days / 30 )}
331372 else :
332- return _ ('1 month' )
373+ s = _ ('1 month' )
374+ else :
375+ s = _ ('%(number)s weeks' )% {'number' : int (days / 7 )}
376+ elif self .day > 7 :
377+ s = _ ('1 week' )
378+ elif self .day > 1 :
379+ s = _ ('%(number)s days' )% {'number' : self .day }
380+ elif self .day == 1 or self .hour > 12 :
381+ if self .sign > 0 :
382+ return _ ('tomorrow' )
333383 else :
334- return _ ('%(number)s weeks' )% {'number' : int (days / 7 )}
335- if self .day > 7 :
336- return _ ('1 week' )
337- if self .day > 1 :
338- return _ ('%(number)s days' )% {'number' : self .day }
339- if self .day == 1 or self .hour > 12 :
340- return _ ('yesterday' )
341- if self .hour > 1 :
342- return _ ('%(number)s hours' )% {'number' : self .hour }
343- if self .hour == 1 :
384+ return _ ('yesterday' )
385+ elif self .hour > 1 :
386+ s = _ ('%(number)s hours' )% {'number' : self .hour }
387+ elif self .hour == 1 :
344388 if self .minute < 15 :
345- return _ ('an hour' )
346- quart = self .minute / 15
347- if quart == 2 :
348- return _ ('1 1/2 hours' )
349- return _ ('1 %(number)s/4 hours' )% {'number' : quart }
350- if self .minute < 1 :
351- return _ ('just now' )
352- if self .minute == 1 :
353- return _ ('1 minute' )
354- if self .minute < 15 :
355- return _ ('%(number)s minutes' )% {'number' : self .minute }
356- quart = int (self .minute / 15 )
357- if quart == 2 :
358- return _ ('1/2 an hour' )
359- return _ ('%(number)s/4 hour' )% {'number' : quart }
389+ s = _ ('an hour' )
390+ elif self .minute / 15 == 2 :
391+ s = _ ('1 1/2 hours' )
392+ else :
393+ s = _ ('1 %(number)s/4 hours' )% {'number' : self .minute / 15 }
394+ elif self .minute < 1 :
395+ if self .sign > 0 :
396+ return _ ('in a moment' )
397+ else :
398+ return _ ('just now' )
399+ elif self .minute == 1 :
400+ s = _ ('1 minute' )
401+ elif self .minute < 15 :
402+ s = _ ('%(number)s minutes' )% {'number' : self .minute }
403+ elif int (self .minute / 15 ) == 2 :
404+ s = _ ('1/2 an hour' )
405+ else :
406+ s = _ ('%(number)s/4 hour' )% {'number' : int (self .minute / 15 )}
407+ return s
360408
361409 def get_tuple (self ):
362410 return (self .year , self .month , self .day , self .hour , self .minute ,
@@ -385,6 +433,9 @@ def test():
385433
386434#
387435# $Log: not supported by cvs2svn $
436+ # Revision 1.18 2002/01/23 20:00:50 jhermann
437+ # %e is a UNIXism and not documented for Python
438+ #
388439# Revision 1.17 2002/01/16 07:02:57 richard
389440# . lots of date/interval related changes:
390441# - more relaxed date format for input
0 commit comments