X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=roundup%2Fdate.py;h=1cc6464b9aa8748f64e13ce0a4c5b9a1d41d3c72;hb=0a18f0d1b9dbace21eaddea90430425eb888129d;hp=c170fb6b3ac3c61c5f2921ce8fc984cf47b572aa;hpb=6e0ba61c2ecbe9ea1c30894727395ec582026cd5;p=roundup.git diff --git a/roundup/date.py b/roundup/date.py index c170fb6..1cc6464 100644 --- a/roundup/date.py +++ b/roundup/date.py @@ -15,7 +15,7 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: date.py,v 1.20 2002-02-21 23:34:51 richard Exp $ +# $Id: date.py,v 1.28 2002-09-10 12:44:42 richard Exp $ __doc__ = """ Date, time and time interval handling. @@ -75,6 +75,10 @@ class Date: >>> Date("14:25", -5) + + The date format 'yyyymmddHHMMSS' (year, month, day, hour, + minute, second) is the serialisation format returned by the serialise() + method, and is accepted as an argument on instatiation. ''' def __init__(self, spec='.', offset=0): """Construct a date given a specification and a time zone offset. @@ -91,29 +95,17 @@ class Date: self.year, self.month, self.day, self.hour, self.minute, \ self.second, x, x, x = time.gmtime(ts) - def applyInterval(self, interval): - ''' Apply the interval to this date + def addInterval(self, interval): + ''' Add the interval to this date, returning the date tuple ''' - t = (self.year + interval.year, - self.month + interval.month, - self.day + interval.day, - self.hour + interval.hour, - self.minute + interval.minute, - self.second + interval.second, 0, 0, 0) - self.year, self.month, self.day, self.hour, self.minute, \ - self.second, x, x, x = time.gmtime(calendar.timegm(t)) - - def __add__(self, other): - """Add an interval to this date to produce another date. - """ # do the basic calc - sign = other.sign - year = self.year + sign * other.year - month = self.month + sign * other.month - day = self.day + sign * other.day - hour = self.hour + sign * other.hour - minute = self.minute + sign * other.minute - second = self.second + sign * other.second + sign = interval.sign + year = self.year + sign * interval.year + month = self.month + sign * interval.month + day = self.day + sign * interval.day + hour = self.hour + sign * interval.hour + minute = self.minute + sign * interval.minute + second = self.second + sign * interval.second # now cope with under- and over-flow # first do the time @@ -148,17 +140,28 @@ class Date: # re-figure the number of days for this month if month == 2 and calendar.isleap(year): month_days = 29 else: month_days = mdays[month] + return (year, month, day, hour, minute, second, 0, 0, 0) + + def applyInterval(self, interval): + ''' Apply the interval to this date + ''' + self.year, self.month, self.day, self.hour, self.minute, \ + self.second, x, x, x = self.addInterval(interval) - return Date((year, month, day, hour, minute, second, 0, 0, 0)) + def __add__(self, interval): + """Add an interval to this date to produce another date. + """ + return Date(self.addInterval(interval)) - # XXX deviates from spec to allow subtraction of dates as well + # deviates from spec to allow subtraction of dates as well def __sub__(self, other): """ Subtract: 1. an interval from this date to produce another date. 2. a date from this date to produce an interval. """ if isinstance(other, Interval): - other = Interval(other.get_tuple(), sign=-other.sign) + other = Interval(other.get_tuple()) + other.sign *= -1 return self.__add__(other) assert isinstance(other, Date), 'May only subtract Dates or Intervals' @@ -192,6 +195,8 @@ class Date: if other is None: return 1 for attr in ('year', 'month', 'day', 'hour', 'minute', 'second'): + if not hasattr(other, attr): + return 1 r = cmp(getattr(self, attr), getattr(other, attr)) if r: return r return 0 @@ -210,17 +215,23 @@ class Date: return str def set(self, spec, offset=0, date_re=re.compile(r''' - (((?P\d\d\d\d)-)?((?P\d\d?)-(?P\d\d?))?)? # yyyy-mm-dd - (?P\.)? # . - (((?P\d?\d):(?P\d\d))?(:(?P\d\d))?)? # hh:mm:ss - (?P.+)? # offset - ''', re.VERBOSE)): + (((?P\d\d\d\d)-)?((?P\d\d?)-(?P\d\d?))?)? # yyyy-mm-dd + (?P\.)? # . + (((?P\d?\d):(?P\d\d))?(:(?P\d\d))?)? # hh:mm:ss + (?P.+)? # offset + ''', re.VERBOSE), serialised_re=re.compile(''' + (?P\d{4})(?P\d{2})(?P\d{2}) # yyyymmdd + (?P\d{2})(?P\d{2})(?P\d{2}) # HHMMSS + ''', re.VERBOSE)): ''' set the date to the value in spec ''' - m = date_re.match(spec) + m = serialised_re.match(spec) if not m: - raise ValueError, _('Not a date spec: [[yyyy-]mm-dd].[[h]h:mm[:ss]]' - '[offset]') + m = date_re.match(spec) + if not m: + raise ValueError, _('Not a date spec: [[yyyy-]mm-dd].' + '[[h]h:mm[:ss]][offset]') + info = m.groupdict() # get the current date/time using the offset @@ -245,7 +256,7 @@ class Date: self.year, self.month, self.day, self.hour, self.minute, \ self.second, x, x, x = time.gmtime(ts) - if info['o']: + if info.get('o', None): self.applyInterval(Interval(info['o'])) def __repr__(self): @@ -262,6 +273,10 @@ class Date: return (self.year, self.month, self.day, self.hour, self.minute, self.second, 0, 0, 0) + def serialise(self): + return '%4d%02d%02d%02d%02d%02d'%(self.year, self.month, + self.day, self.hour, self.minute, self.second) + class Interval: ''' Date intervals are specified using the suffixes "y", "m", and "d". The @@ -290,6 +305,10 @@ class Interval: days (or over/underflow from hours/mins/secs) will do that, and days-per-month and leap years are accounted for. Leap seconds are not. + The interval format 'syyyymmddHHMMSS' (sign, year, month, day, hour, + minute, second) is the serialisation format returned by the serialise() + method, and is accepted as an argument on instatiation. + TODO: more examples, showing the order of addition operation ''' def __init__(self, spec, sign=1): @@ -297,19 +316,26 @@ class Interval: if type(spec) == type(''): self.set(spec) else: - self.sign = sign - self.year, self.month, self.day, self.hour, self.minute, \ - self.second = spec + if len(spec) == 7: + self.sign, self.year, self.month, self.day, self.hour, \ + self.minute, self.second = spec + else: + # old, buggy spec form + self.sign = sign + self.year, self.month, self.day, self.hour, self.minute, \ + self.second = spec def __cmp__(self, other): """Compare this interval to another interval.""" if other is None: return 1 for attr in ('year', 'month', 'day', 'hour', 'minute', 'second'): + if not hasattr(other, attr): + return 1 r = cmp(getattr(self, attr), getattr(other, attr)) if r: return r return 0 - + def __str__(self): """Return this interval as a string.""" sign = {1:'+', -1:'-'}[self.sign] @@ -323,35 +349,32 @@ class Interval: l.append('%d:%02d'%(self.hour, self.minute)) return ' '.join(l) - def set(self, spec, interval_re = re.compile(''' - \s* - (?P[-+])? # + or - - \s* - ((?P\d+\s*)y)? # year - \s* - ((?P\d+\s*)m)? # month - \s* - ((?P\d+\s*)w)? # week - \s* - ((?P\d+\s*)d)? # day - \s* - (((?P\d+):(?P\d+))?(:(?P\d+))?)? # time - \s* - ''', re.VERBOSE)): + def set(self, spec, interval_re=re.compile(''' + \s*(?P[-+])? # + or - + \s*((?P\d+\s*)y)? # year + \s*((?P\d+\s*)m)? # month + \s*((?P\d+\s*)w)? # week + \s*((?P\d+\s*)d)? # day + \s*(((?P\d+):(?P\d+))?(:(?P\d+))?)? # time + \s*''', re.VERBOSE), serialised_re=re.compile(''' + (?P[+-])(?P\d{4})(?P\d{2})(?P\d{2}) + (?P\d{2})(?P\d{2})(?P\d{2})''', re.VERBOSE)): ''' set the date to the value in spec ''' self.year = self.month = self.week = self.day = self.hour = \ self.minute = self.second = 0 self.sign = 1 - m = interval_re.match(spec) + m = serialised_re.match(spec) if not m: - raise ValueError, _('Not an interval spec: [+-] [#y] [#m] [#w] ' - '[#d] [[[H]H:MM]:SS]') + m = interval_re.match(spec) + if not m: + raise ValueError, _('Not an interval spec: [+-] [#y] [#m] [#w] ' + '[#d] [[[H]H:MM]:SS]') info = m.groupdict() for group, attr in {'y':'year', 'm':'month', 'w':'week', 'd':'day', 'H':'hour', 'M':'minute', 'S':'second'}.items(): - if info[group] is not None: + if info.get(group, None) is not None: setattr(self, attr, int(info[group])) if self.week: @@ -366,8 +389,11 @@ class Interval: def pretty(self): ''' print up the date date using one of these nice formats.. ''' - if self.year or self.month > 2: - return None + if self.year: + if self.year == 1: + return _('1 year') + else: + return _('%(number)s years')%{'number': self.year} elif self.month or self.day > 13: days = (self.month * 30) + self.day if days > 28: @@ -411,8 +437,12 @@ class Interval: return s def get_tuple(self): - return (self.year, self.month, self.day, self.hour, self.minute, - self.second) + return (self.sign, self.year, self.month, self.day, self.hour, + self.minute, self.second) + + def serialise(self): + return '%s%4d%02d%02d%02d%02d%02d'%(self.sign, self.year, self.month, + self.day, self.hour, self.minute, self.second) def test(): @@ -435,72 +465,4 @@ def test(): if __name__ == '__main__': test() -# -# $Log: not supported by cvs2svn $ -# Revision 1.19 2002/02/21 23:11:45 richard -# . fixed some problems in date calculations (calendar.py doesn't handle over- -# and under-flow). Also, hour/minute/second intervals may now be more than -# 99 each. -# -# Revision 1.18 2002/01/23 20:00:50 jhermann -# %e is a UNIXism and not documented for Python -# -# Revision 1.17 2002/01/16 07:02:57 richard -# . lots of date/interval related changes: -# - more relaxed date format for input -# -# Revision 1.16 2002/01/08 11:56:24 richard -# missed an import _ -# -# Revision 1.15 2002/01/05 02:27:00 richard -# I18N'ification -# -# Revision 1.14 2001/11/22 15:46:42 jhermann -# Added module docstrings to all modules. -# -# Revision 1.13 2001/09/18 22:58:37 richard -# -# Added some more help to roundu-admin -# -# Revision 1.12 2001/08/17 03:08:11 richard -# fixed prettification of intervals of 1 week -# -# Revision 1.11 2001/08/15 23:43:18 richard -# Fixed some isFooTypes that I missed. -# Refactored some code in the CGI code. -# -# Revision 1.10 2001/08/07 00:24:42 richard -# stupid typo -# -# Revision 1.9 2001/08/07 00:15:51 richard -# Added the copyright/license notice to (nearly) all files at request of -# Bizar Software. -# -# Revision 1.8 2001/08/05 07:46:12 richard -# Changed date.Date to use regular string formatting instead of strftime - -# win32 seems to have problems with %T and no hour... or something... -# -# Revision 1.7 2001/08/02 00:27:04 richard -# Extended the range of intervals that are pretty-printed before actual dates -# are displayed. -# -# Revision 1.6 2001/07/31 09:54:18 richard -# Fixed the 2.1-specific gmtime() (no arg) call in roundup.date. (Paul Wright) -# -# Revision 1.5 2001/07/29 07:01:39 richard -# Added vim command to all source so that we don't get no steenkin' tabs :) -# -# Revision 1.4 2001/07/25 04:09:34 richard -# Fixed offset handling (shoulda read the spec a little better) -# -# Revision 1.3 2001/07/23 07:56:05 richard -# Storing only marshallable data in the db - no nasty pickled class references. -# -# Revision 1.2 2001/07/22 12:09:32 richard -# Final commit of Grande Splite -# -# Revision 1.1 2001/07/22 11:58:35 richard -# More Grande Splite -# -# # vim: set filetype=python ts=4 sw=4 et si