Mercurial > p > roundup > code
comparison roundup/date.py @ 1356:83f33642d220 maint-0.5
[[Metadata associated with this commit was garbled during conversion from CVS
to Subversion.]]
| author | Richard Jones <richard@users.sourceforge.net> |
|---|---|
| date | Thu, 09 Jan 2003 22:59:22 +0000 |
| parents | |
| children | f0da50a17e3c |
comparison
equal
deleted
inserted
replaced
| 1242:3d0158c8c32b | 1356:83f33642d220 |
|---|---|
| 1 # | |
| 2 # Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/) | |
| 3 # This module is free software, and you may redistribute it and/or modify | |
| 4 # under the same terms as Python, so long as this copyright message and | |
| 5 # disclaimer are retained in their original form. | |
| 6 # | |
| 7 # IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR | |
| 8 # DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING | |
| 9 # OUT OF THE USE OF THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE | |
| 10 # POSSIBILITY OF SUCH DAMAGE. | |
| 11 # | |
| 12 # BIZAR SOFTWARE PTY LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, | |
| 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" | |
| 15 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, | |
| 16 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | |
| 17 # | |
| 18 # $Id: date.py,v 1.40 2002-12-18 00:15:53 richard Exp $ | |
| 19 | |
| 20 __doc__ = """ | |
| 21 Date, time and time interval handling. | |
| 22 """ | |
| 23 | |
| 24 import time, re, calendar | |
| 25 from i18n import _ | |
| 26 | |
| 27 class Date: | |
| 28 ''' | |
| 29 As strings, date-and-time stamps are specified with the date in | |
| 30 international standard format (yyyy-mm-dd) joined to the time | |
| 31 (hh:mm:ss) by a period ("."). Dates in this form can be easily compared | |
| 32 and are fairly readable when printed. An example of a valid stamp is | |
| 33 "2000-06-24.13:03:59". We'll call this the "full date format". When | |
| 34 Timestamp objects are printed as strings, they appear in the full date | |
| 35 format with the time always given in GMT. The full date format is | |
| 36 always exactly 19 characters long. | |
| 37 | |
| 38 For user input, some partial forms are also permitted: the whole time | |
| 39 or just the seconds may be omitted; and the whole date may be omitted | |
| 40 or just the year may be omitted. If the time is given, the time is | |
| 41 interpreted in the user's local time zone. The Date constructor takes | |
| 42 care of these conversions. In the following examples, suppose that yyyy | |
| 43 is the current year, mm is the current month, and dd is the current day | |
| 44 of the month; and suppose that the user is on Eastern Standard Time. | |
| 45 | |
| 46 "2000-04-17" means <Date 2000-04-17.00:00:00> | |
| 47 "01-25" means <Date yyyy-01-25.00:00:00> | |
| 48 "2000-04-17.03:45" means <Date 2000-04-17.08:45:00> | |
| 49 "08-13.22:13" means <Date yyyy-08-14.03:13:00> | |
| 50 "11-07.09:32:43" means <Date yyyy-11-07.14:32:43> | |
| 51 "14:25" means <Date yyyy-mm-dd.19:25:00> | |
| 52 "8:47:11" means <Date yyyy-mm-dd.13:47:11> | |
| 53 "." means "right now" | |
| 54 | |
| 55 The Date class should understand simple date expressions of the form | |
| 56 stamp + interval and stamp - interval. When adding or subtracting | |
| 57 intervals involving months or years, the components are handled | |
| 58 separately. For example, when evaluating "2000-06-25 + 1m 10d", we | |
| 59 first add one month to get 2000-07-25, then add 10 days to get | |
| 60 2000-08-04 (rather than trying to decide whether 1m 10d means 38 or 40 | |
| 61 or 41 days). | |
| 62 | |
| 63 Example usage: | |
| 64 >>> Date(".") | |
| 65 <Date 2000-06-26.00:34:02> | |
| 66 >>> _.local(-5) | |
| 67 "2000-06-25.19:34:02" | |
| 68 >>> Date(". + 2d") | |
| 69 <Date 2000-06-28.00:34:02> | |
| 70 >>> Date("1997-04-17", -5) | |
| 71 <Date 1997-04-17.00:00:00> | |
| 72 >>> Date("01-25", -5) | |
| 73 <Date 2000-01-25.00:00:00> | |
| 74 >>> Date("08-13.22:13", -5) | |
| 75 <Date 2000-08-14.03:13:00> | |
| 76 >>> Date("14:25", -5) | |
| 77 <Date 2000-06-25.19:25:00> | |
| 78 | |
| 79 The date format 'yyyymmddHHMMSS' (year, month, day, hour, | |
| 80 minute, second) is the serialisation format returned by the serialise() | |
| 81 method, and is accepted as an argument on instatiation. | |
| 82 ''' | |
| 83 def __init__(self, spec='.', offset=0): | |
| 84 """Construct a date given a specification and a time zone offset. | |
| 85 | |
| 86 'spec' is a full date or a partial form, with an optional | |
| 87 added or subtracted interval. Or a date 9-tuple. | |
| 88 'offset' is the local time zone offset from GMT in hours. | |
| 89 """ | |
| 90 if type(spec) == type(''): | |
| 91 self.set(spec, offset=offset) | |
| 92 else: | |
| 93 y,m,d,H,M,S,x,x,x = spec | |
| 94 ts = calendar.timegm((y,m,d,H+offset,M,S,0,0,0)) | |
| 95 self.year, self.month, self.day, self.hour, self.minute, \ | |
| 96 self.second, x, x, x = time.gmtime(ts) | |
| 97 | |
| 98 def addInterval(self, interval): | |
| 99 ''' Add the interval to this date, returning the date tuple | |
| 100 ''' | |
| 101 # do the basic calc | |
| 102 sign = interval.sign | |
| 103 year = self.year + sign * interval.year | |
| 104 month = self.month + sign * interval.month | |
| 105 day = self.day + sign * interval.day | |
| 106 hour = self.hour + sign * interval.hour | |
| 107 minute = self.minute + sign * interval.minute | |
| 108 second = self.second + sign * interval.second | |
| 109 | |
| 110 # now cope with under- and over-flow | |
| 111 # first do the time | |
| 112 while (second < 0 or second > 59 or minute < 0 or minute > 59 or | |
| 113 hour < 0 or hour > 59): | |
| 114 if second < 0: minute -= 1; second += 60 | |
| 115 elif second > 59: minute += 1; second -= 60 | |
| 116 if minute < 0: hour -= 1; minute += 60 | |
| 117 elif minute > 59: hour += 1; minute -= 60 | |
| 118 if hour < 0: day -= 1; hour += 24 | |
| 119 elif hour > 59: day += 1; hour -= 24 | |
| 120 | |
| 121 # fix up the month so we're within range | |
| 122 while month < 1 or month > 12: | |
| 123 if month < 1: year -= 1; month += 12 | |
| 124 if month > 12: year += 1; month -= 12 | |
| 125 | |
| 126 # now do the days, now that we know what month we're in | |
| 127 mdays = calendar.mdays | |
| 128 if month == 2 and calendar.isleap(year): month_days = 29 | |
| 129 else: month_days = mdays[month] | |
| 130 while month < 1 or month > 12 or day < 0 or day > month_days: | |
| 131 # now to day under/over | |
| 132 if day < 0: month -= 1; day += month_days | |
| 133 elif day > month_days: month += 1; day -= month_days | |
| 134 | |
| 135 # possibly fix up the month so we're within range | |
| 136 while month < 1 or month > 12: | |
| 137 if month < 1: year -= 1; month += 12 | |
| 138 if month > 12: year += 1; month -= 12 | |
| 139 | |
| 140 # re-figure the number of days for this month | |
| 141 if month == 2 and calendar.isleap(year): month_days = 29 | |
| 142 else: month_days = mdays[month] | |
| 143 return (year, month, day, hour, minute, second, 0, 0, 0) | |
| 144 | |
| 145 def applyInterval(self, interval): | |
| 146 ''' Apply the interval to this date | |
| 147 ''' | |
| 148 self.year, self.month, self.day, self.hour, self.minute, \ | |
| 149 self.second, x, x, x = self.addInterval(interval) | |
| 150 | |
| 151 def __add__(self, interval): | |
| 152 """Add an interval to this date to produce another date. | |
| 153 """ | |
| 154 return Date(self.addInterval(interval)) | |
| 155 | |
| 156 # deviates from spec to allow subtraction of dates as well | |
| 157 def __sub__(self, other): | |
| 158 """ Subtract: | |
| 159 1. an interval from this date to produce another date. | |
| 160 2. a date from this date to produce an interval. | |
| 161 """ | |
| 162 if isinstance(other, Interval): | |
| 163 other = Interval(other.get_tuple()) | |
| 164 other.sign *= -1 | |
| 165 return self.__add__(other) | |
| 166 | |
| 167 assert isinstance(other, Date), 'May only subtract Dates or Intervals' | |
| 168 | |
| 169 # TODO this code will fall over laughing if the dates cross | |
| 170 # leap years, phases of the moon, .... | |
| 171 a = calendar.timegm((self.year, self.month, self.day, self.hour, | |
| 172 self.minute, self.second, 0, 0, 0)) | |
| 173 b = calendar.timegm((other.year, other.month, other.day, | |
| 174 other.hour, other.minute, other.second, 0, 0, 0)) | |
| 175 diff = a - b | |
| 176 if diff < 0: | |
| 177 sign = 1 | |
| 178 diff = -diff | |
| 179 else: | |
| 180 sign = -1 | |
| 181 S = diff%60 | |
| 182 M = (diff/60)%60 | |
| 183 H = (diff/(60*60))%60 | |
| 184 if H>1: S = 0 | |
| 185 d = (diff/(24*60*60))%30 | |
| 186 if d>1: H = S = M = 0 | |
| 187 m = (diff/(30*24*60*60))%12 | |
| 188 if m>1: H = S = M = 0 | |
| 189 y = (diff/(365*24*60*60)) | |
| 190 if y>1: d = H = S = M = 0 | |
| 191 return Interval((y, m, d, H, M, S), sign=sign) | |
| 192 | |
| 193 def __cmp__(self, other): | |
| 194 """Compare this date to another date.""" | |
| 195 if other is None: | |
| 196 return 1 | |
| 197 for attr in ('year', 'month', 'day', 'hour', 'minute', 'second'): | |
| 198 if not hasattr(other, attr): | |
| 199 return 1 | |
| 200 r = cmp(getattr(self, attr), getattr(other, attr)) | |
| 201 if r: return r | |
| 202 return 0 | |
| 203 | |
| 204 def __str__(self): | |
| 205 """Return this date as a string in the yyyy-mm-dd.hh:mm:ss format.""" | |
| 206 return '%4d-%02d-%02d.%02d:%02d:%02d'%(self.year, self.month, self.day, | |
| 207 self.hour, self.minute, self.second) | |
| 208 | |
| 209 def pretty(self, format='%d %B %Y'): | |
| 210 ''' print up the date date using a pretty format... | |
| 211 | |
| 212 Note that if the day is zero, and the day appears first in the | |
| 213 format, then the day number will be removed from output. | |
| 214 ''' | |
| 215 str = time.strftime(format, (self.year, self.month, self.day, | |
| 216 self.hour, self.minute, self.second, 0, 0, 0)) | |
| 217 # handle zero day by removing it | |
| 218 if format.startswith('%d') and str[0] == '0': | |
| 219 return ' ' + str[1:] | |
| 220 return str | |
| 221 | |
| 222 def set(self, spec, offset=0, date_re=re.compile(r''' | |
| 223 (((?P<y>\d\d\d\d)-)?((?P<m>\d\d?)-(?P<d>\d\d?))?)? # yyyy-mm-dd | |
| 224 (?P<n>\.)? # . | |
| 225 (((?P<H>\d?\d):(?P<M>\d\d))?(:(?P<S>\d\d))?)? # hh:mm:ss | |
| 226 (?P<o>.+)? # offset | |
| 227 ''', re.VERBOSE), serialised_re=re.compile(r''' | |
| 228 (\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d) | |
| 229 ''', re.VERBOSE)): | |
| 230 ''' set the date to the value in spec | |
| 231 ''' | |
| 232 m = serialised_re.match(spec) | |
| 233 if m is not None: | |
| 234 # we're serialised - easy! | |
| 235 self.year, self.month, self.day, self.hour, self.minute, \ | |
| 236 self.second = map(int, m.groups()[:6]) | |
| 237 return | |
| 238 | |
| 239 # not serialised data, try usual format | |
| 240 m = date_re.match(spec) | |
| 241 if m is None: | |
| 242 raise ValueError, _('Not a date spec: [[yyyy-]mm-dd].' | |
| 243 '[[h]h:mm[:ss]][offset]') | |
| 244 | |
| 245 info = m.groupdict() | |
| 246 | |
| 247 # get the current date as our default | |
| 248 y,m,d,H,M,S,x,x,x = time.gmtime(time.time()) | |
| 249 | |
| 250 # override year, month, day parts | |
| 251 if info['m'] is not None and info['d'] is not None: | |
| 252 m = int(info['m']) | |
| 253 d = int(info['d']) | |
| 254 if info['y'] is not None: | |
| 255 y = int(info['y']) | |
| 256 # time defaults to 00:00:00 now | |
| 257 H = M = S = 0 | |
| 258 | |
| 259 # override hour, minute, second parts | |
| 260 if info['H'] is not None and info['M'] is not None: | |
| 261 H = int(info['H']) - offset | |
| 262 M = int(info['M']) | |
| 263 S = 0 | |
| 264 if info['S'] is not None: S = int(info['S']) | |
| 265 | |
| 266 # now handle the adjustment of hour | |
| 267 ts = calendar.timegm((y,m,d,H,M,S,0,0,0)) | |
| 268 self.year, self.month, self.day, self.hour, self.minute, \ | |
| 269 self.second, x, x, x = time.gmtime(ts) | |
| 270 | |
| 271 if info.get('o', None): | |
| 272 self.applyInterval(Interval(info['o'])) | |
| 273 | |
| 274 def __repr__(self): | |
| 275 return '<Date %s>'%self.__str__() | |
| 276 | |
| 277 def local(self, offset): | |
| 278 """ Return this date as yyyy-mm-dd.hh:mm:ss in a local time zone. | |
| 279 """ | |
| 280 return Date((self.year, self.month, self.day, self.hour + offset, | |
| 281 self.minute, self.second, 0, 0, 0)) | |
| 282 | |
| 283 def get_tuple(self): | |
| 284 return (self.year, self.month, self.day, self.hour, self.minute, | |
| 285 self.second, 0, 0, 0) | |
| 286 | |
| 287 def serialise(self): | |
| 288 return '%4d%02d%02d%02d%02d%02d'%(self.year, self.month, | |
| 289 self.day, self.hour, self.minute, self.second) | |
| 290 | |
| 291 class Interval: | |
| 292 ''' | |
| 293 Date intervals are specified using the suffixes "y", "m", and "d". The | |
| 294 suffix "w" (for "week") means 7 days. Time intervals are specified in | |
| 295 hh:mm:ss format (the seconds may be omitted, but the hours and minutes | |
| 296 may not). | |
| 297 | |
| 298 "3y" means three years | |
| 299 "2y 1m" means two years and one month | |
| 300 "1m 25d" means one month and 25 days | |
| 301 "2w 3d" means two weeks and three days | |
| 302 "1d 2:50" means one day, two hours, and 50 minutes | |
| 303 "14:00" means 14 hours | |
| 304 "0:04:33" means four minutes and 33 seconds | |
| 305 | |
| 306 Example usage: | |
| 307 >>> Interval(" 3w 1 d 2:00") | |
| 308 <Interval 22d 2:00> | |
| 309 >>> Date(". + 2d") + Interval("- 3w") | |
| 310 <Date 2000-06-07.00:34:02> | |
| 311 | |
| 312 Intervals are added/subtracted in order of: | |
| 313 seconds, minutes, hours, years, months, days | |
| 314 | |
| 315 Calculations involving monts (eg '+2m') have no effect on days - only | |
| 316 days (or over/underflow from hours/mins/secs) will do that, and | |
| 317 days-per-month and leap years are accounted for. Leap seconds are not. | |
| 318 | |
| 319 The interval format 'syyyymmddHHMMSS' (sign, year, month, day, hour, | |
| 320 minute, second) is the serialisation format returned by the serialise() | |
| 321 method, and is accepted as an argument on instatiation. | |
| 322 | |
| 323 TODO: more examples, showing the order of addition operation | |
| 324 ''' | |
| 325 def __init__(self, spec, sign=1): | |
| 326 """Construct an interval given a specification.""" | |
| 327 if type(spec) == type(''): | |
| 328 self.set(spec) | |
| 329 else: | |
| 330 if len(spec) == 7: | |
| 331 self.sign, self.year, self.month, self.day, self.hour, \ | |
| 332 self.minute, self.second = spec | |
| 333 else: | |
| 334 # old, buggy spec form | |
| 335 self.sign = sign | |
| 336 self.year, self.month, self.day, self.hour, self.minute, \ | |
| 337 self.second = spec | |
| 338 | |
| 339 def __cmp__(self, other): | |
| 340 """Compare this interval to another interval.""" | |
| 341 if other is None: | |
| 342 return 1 | |
| 343 for attr in ('year', 'month', 'day', 'hour', 'minute', 'second'): | |
| 344 if not hasattr(other, attr): | |
| 345 return 1 | |
| 346 r = cmp(getattr(self, attr), getattr(other, attr)) | |
| 347 if r: return r | |
| 348 return 0 | |
| 349 | |
| 350 def __str__(self): | |
| 351 """Return this interval as a string.""" | |
| 352 sign = {1:'+', -1:'-'}[self.sign] | |
| 353 l = [sign] | |
| 354 if self.year: l.append('%sy'%self.year) | |
| 355 if self.month: l.append('%sm'%self.month) | |
| 356 if self.day: l.append('%sd'%self.day) | |
| 357 if self.second: | |
| 358 l.append('%d:%02d:%02d'%(self.hour, self.minute, self.second)) | |
| 359 elif self.hour or self.minute: | |
| 360 l.append('%d:%02d'%(self.hour, self.minute)) | |
| 361 return ' '.join(l) | |
| 362 | |
| 363 def __add__(self, other): | |
| 364 if isinstance(other, Date): | |
| 365 # the other is a Date - produce a Date | |
| 366 return Date(other.addInterval(self)) | |
| 367 elif isinstance(other, Interval): | |
| 368 # add the other Interval to this one | |
| 369 a = self.get_tuple() | |
| 370 b = other.get_tuple() | |
| 371 if b[0] < 0: | |
| 372 i = Interval([x-y for x,y in zip(a[1:],b[1:])]) | |
| 373 else: | |
| 374 i = Interval([x+y for x,y in zip(a[1:],b[1:])]) | |
| 375 return i | |
| 376 # nope, no idea what to do with this other... | |
| 377 raise TypeError, "Can't add %r"%other | |
| 378 | |
| 379 def set(self, spec, interval_re=re.compile(''' | |
| 380 \s*(?P<s>[-+])? # + or - | |
| 381 \s*((?P<y>\d+\s*)y)? # year | |
| 382 \s*((?P<m>\d+\s*)m)? # month | |
| 383 \s*((?P<w>\d+\s*)w)? # week | |
| 384 \s*((?P<d>\d+\s*)d)? # day | |
| 385 \s*(((?P<H>\d+):(?P<M>\d+))?(:(?P<S>\d+))?)? # time | |
| 386 \s*''', re.VERBOSE), serialised_re=re.compile(''' | |
| 387 (?P<s>[+-])?1?(?P<y>([ ]{3}\d|\d{4}))(?P<m>\d{2})(?P<d>\d{2}) | |
| 388 (?P<H>\d{2})(?P<M>\d{2})(?P<S>\d{2})''', re.VERBOSE)): | |
| 389 ''' set the date to the value in spec | |
| 390 ''' | |
| 391 self.year = self.month = self.week = self.day = self.hour = \ | |
| 392 self.minute = self.second = 0 | |
| 393 self.sign = 1 | |
| 394 m = serialised_re.match(spec) | |
| 395 if not m: | |
| 396 m = interval_re.match(spec) | |
| 397 if not m: | |
| 398 raise ValueError, _('Not an interval spec: [+-] [#y] [#m] [#w] ' | |
| 399 '[#d] [[[H]H:MM]:SS]') | |
| 400 | |
| 401 info = m.groupdict() | |
| 402 for group, attr in {'y':'year', 'm':'month', 'w':'week', 'd':'day', | |
| 403 'H':'hour', 'M':'minute', 'S':'second'}.items(): | |
| 404 if info.get(group, None) is not None: | |
| 405 setattr(self, attr, int(info[group])) | |
| 406 | |
| 407 if self.week: | |
| 408 self.day = self.day + self.week*7 | |
| 409 | |
| 410 if info['s'] is not None: | |
| 411 self.sign = {'+':1, '-':-1}[info['s']] | |
| 412 | |
| 413 def __repr__(self): | |
| 414 return '<Interval %s>'%self.__str__() | |
| 415 | |
| 416 def pretty(self): | |
| 417 ''' print up the date date using one of these nice formats.. | |
| 418 ''' | |
| 419 if self.year: | |
| 420 if self.year == 1: | |
| 421 return _('1 year') | |
| 422 else: | |
| 423 return _('%(number)s years')%{'number': self.year} | |
| 424 elif self.month or self.day > 13: | |
| 425 days = (self.month * 30) + self.day | |
| 426 if days > 28: | |
| 427 if int(days/30) > 1: | |
| 428 s = _('%(number)s months')%{'number': int(days/30)} | |
| 429 else: | |
| 430 s = _('1 month') | |
| 431 else: | |
| 432 s = _('%(number)s weeks')%{'number': int(days/7)} | |
| 433 elif self.day > 7: | |
| 434 s = _('1 week') | |
| 435 elif self.day > 1: | |
| 436 s = _('%(number)s days')%{'number': self.day} | |
| 437 elif self.day == 1 or self.hour > 12: | |
| 438 if self.sign > 0: | |
| 439 return _('tomorrow') | |
| 440 else: | |
| 441 return _('yesterday') | |
| 442 elif self.hour > 1: | |
| 443 s = _('%(number)s hours')%{'number': self.hour} | |
| 444 elif self.hour == 1: | |
| 445 if self.minute < 15: | |
| 446 s = _('an hour') | |
| 447 elif self.minute/15 == 2: | |
| 448 s = _('1 1/2 hours') | |
| 449 else: | |
| 450 s = _('1 %(number)s/4 hours')%{'number': self.minute/15} | |
| 451 elif self.minute < 1: | |
| 452 if self.sign > 0: | |
| 453 return _('in a moment') | |
| 454 else: | |
| 455 return _('just now') | |
| 456 elif self.minute == 1: | |
| 457 s = _('1 minute') | |
| 458 elif self.minute < 15: | |
| 459 s = _('%(number)s minutes')%{'number': self.minute} | |
| 460 elif int(self.minute/15) == 2: | |
| 461 s = _('1/2 an hour') | |
| 462 else: | |
| 463 s = _('%(number)s/4 hour')%{'number': int(self.minute/15)} | |
| 464 return s | |
| 465 | |
| 466 def get_tuple(self): | |
| 467 return (self.sign, self.year, self.month, self.day, self.hour, | |
| 468 self.minute, self.second) | |
| 469 | |
| 470 def serialise(self): | |
| 471 sign = self.sign > 0 and '+' or '-' | |
| 472 return '%s%04d%02d%02d%02d%02d%02d'%(sign, self.year, self.month, | |
| 473 self.day, self.hour, self.minute, self.second) | |
| 474 | |
| 475 | |
| 476 def test(): | |
| 477 intervals = (" 3w 1 d 2:00", " + 2d", "3w") | |
| 478 for interval in intervals: | |
| 479 print '>>> Interval("%s")'%interval | |
| 480 print `Interval(interval)` | |
| 481 | |
| 482 dates = (".", "2000-06-25.19:34:02", ". + 2d", "1997-04-17", "01-25", | |
| 483 "08-13.22:13", "14:25") | |
| 484 for date in dates: | |
| 485 print '>>> Date("%s")'%date | |
| 486 print `Date(date)` | |
| 487 | |
| 488 sums = ((". + 2d", "3w"), (".", " 3w 1 d 2:00")) | |
| 489 for date, interval in sums: | |
| 490 print '>>> Date("%s") + Interval("%s")'%(date, interval) | |
| 491 print `Date(date) + Interval(interval)` | |
| 492 | |
| 493 if __name__ == '__main__': | |
| 494 test() | |
| 495 | |
| 496 # vim: set filetype=python ts=4 sw=4 et si |
