Mercurial > p > roundup > code
comparison roundup/date.py @ 964:832d1209aaa2
Preparing to turn back on link/unlink journal events.
By default these are turned off. I've:
- fixed back_anydbm so it can journal those events again (had broken it
with recent changes)
- changed the serialisation format for dates and intervals to use a
numbers-only (and sign for Intervals) string instead of tuple-of-ints.
Much smaller.
| author | Richard Jones <richard@users.sourceforge.net> |
|---|---|
| date | Wed, 21 Aug 2002 07:07:27 +0000 |
| parents | a568596dbea7 |
| children | 5252cc77eaa0 |
comparison
equal
deleted
inserted
replaced
| 963:6010f0078800 | 964:832d1209aaa2 |
|---|---|
| 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.23 2002-07-18 23:07:08 richard Exp $ | 18 # $Id: date.py,v 1.24 2002-08-21 07:07:27 richard 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 |
| 73 <Date 2000-01-25.00:00:00> | 73 <Date 2000-01-25.00:00:00> |
| 74 >>> Date("08-13.22:13", -5) | 74 >>> Date("08-13.22:13", -5) |
| 75 <Date 2000-08-14.03:13:00> | 75 <Date 2000-08-14.03:13:00> |
| 76 >>> Date("14:25", -5) | 76 >>> Date("14:25", -5) |
| 77 <Date 2000-06-25.19:25:00> | 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. | |
| 78 ''' | 82 ''' |
| 79 def __init__(self, spec='.', offset=0): | 83 def __init__(self, spec='.', offset=0): |
| 80 """Construct a date given a specification and a time zone offset. | 84 """Construct a date given a specification and a time zone offset. |
| 81 | 85 |
| 82 'spec' is a full date or a partial form, with an optional | 86 'spec' is a full date or a partial form, with an optional |
| 208 self.day, self.hour, self.minute, self.second, 0, 0, 0)) | 212 self.day, self.hour, self.minute, self.second, 0, 0, 0)) |
| 209 if str[0] == '0': return ' ' + str[1:] | 213 if str[0] == '0': return ' ' + str[1:] |
| 210 return str | 214 return str |
| 211 | 215 |
| 212 def set(self, spec, offset=0, date_re=re.compile(r''' | 216 def set(self, spec, offset=0, date_re=re.compile(r''' |
| 213 (((?P<y>\d\d\d\d)-)?((?P<m>\d\d?)-(?P<d>\d\d?))?)? # yyyy-mm-dd | 217 (((?P<y>\d\d\d\d)-)?((?P<m>\d\d?)-(?P<d>\d\d?))?)? # yyyy-mm-dd |
| 214 (?P<n>\.)? # . | 218 (?P<n>\.)? # . |
| 215 (((?P<H>\d?\d):(?P<M>\d\d))?(:(?P<S>\d\d))?)? # hh:mm:ss | 219 (((?P<H>\d?\d):(?P<M>\d\d))?(:(?P<S>\d\d))?)? # hh:mm:ss |
| 216 (?P<o>.+)? # offset | 220 (?P<o>.+)? # offset |
| 217 ''', re.VERBOSE)): | 221 ''', re.VERBOSE), serialised_re=re.compile(''' |
| 222 (?P<y>\d{4})(?P<m>\d{2})(?P<d>\d{2}) # yyyymmdd | |
| 223 (?P<H>\d{2})(?P<M>\d{2})(?P<S>\d{2}) # HHMMSS | |
| 224 ''', re.VERBOSE)): | |
| 218 ''' set the date to the value in spec | 225 ''' set the date to the value in spec |
| 219 ''' | 226 ''' |
| 220 m = date_re.match(spec) | 227 m = serialised_re.match(spec) |
| 221 if not m: | 228 if not m: |
| 222 raise ValueError, _('Not a date spec: [[yyyy-]mm-dd].[[h]h:mm[:ss]]' | 229 m = date_re.match(spec) |
| 223 '[offset]') | 230 if not m: |
| 231 raise ValueError, _('Not a date spec: [[yyyy-]mm-dd].' | |
| 232 '[[h]h:mm[:ss]][offset]') | |
| 233 | |
| 224 info = m.groupdict() | 234 info = m.groupdict() |
| 225 | 235 |
| 226 # get the current date/time using the offset | 236 # get the current date/time using the offset |
| 227 y,m,d,H,M,S,x,x,x = time.gmtime(time.time()) | 237 y,m,d,H,M,S,x,x,x = time.gmtime(time.time()) |
| 228 | 238 |
| 243 # now handle the adjustment of hour | 253 # now handle the adjustment of hour |
| 244 ts = calendar.timegm((y,m,d,H,M,S,0,0,0)) | 254 ts = calendar.timegm((y,m,d,H,M,S,0,0,0)) |
| 245 self.year, self.month, self.day, self.hour, self.minute, \ | 255 self.year, self.month, self.day, self.hour, self.minute, \ |
| 246 self.second, x, x, x = time.gmtime(ts) | 256 self.second, x, x, x = time.gmtime(ts) |
| 247 | 257 |
| 248 if info['o']: | 258 if info.get('o', None): |
| 249 self.applyInterval(Interval(info['o'])) | 259 self.applyInterval(Interval(info['o'])) |
| 250 | 260 |
| 251 def __repr__(self): | 261 def __repr__(self): |
| 252 return '<Date %s>'%self.__str__() | 262 return '<Date %s>'%self.__str__() |
| 253 | 263 |
| 259 self.second, x, x, x = time.gmtime(calendar.timegm(t)) | 269 self.second, x, x, x = time.gmtime(calendar.timegm(t)) |
| 260 | 270 |
| 261 def get_tuple(self): | 271 def get_tuple(self): |
| 262 return (self.year, self.month, self.day, self.hour, self.minute, | 272 return (self.year, self.month, self.day, self.hour, self.minute, |
| 263 self.second, 0, 0, 0) | 273 self.second, 0, 0, 0) |
| 274 | |
| 275 def serialise(self): | |
| 276 return '%4d%02d%02d%02d%02d%02d'%(self.year, self.month, | |
| 277 self.day, self.hour, self.minute, self.second) | |
| 264 | 278 |
| 265 class Interval: | 279 class Interval: |
| 266 ''' | 280 ''' |
| 267 Date intervals are specified using the suffixes "y", "m", and "d". The | 281 Date intervals are specified using the suffixes "y", "m", and "d". The |
| 268 suffix "w" (for "week") means 7 days. Time intervals are specified in | 282 suffix "w" (for "week") means 7 days. Time intervals are specified in |
| 287 seconds, minutes, hours, years, months, days | 301 seconds, minutes, hours, years, months, days |
| 288 | 302 |
| 289 Calculations involving monts (eg '+2m') have no effect on days - only | 303 Calculations involving monts (eg '+2m') have no effect on days - only |
| 290 days (or over/underflow from hours/mins/secs) will do that, and | 304 days (or over/underflow from hours/mins/secs) will do that, and |
| 291 days-per-month and leap years are accounted for. Leap seconds are not. | 305 days-per-month and leap years are accounted for. Leap seconds are not. |
| 306 | |
| 307 The interval format 'syyyymmddHHMMSS' (sign, year, month, day, hour, | |
| 308 minute, second) is the serialisation format returned by the serialise() | |
| 309 method, and is accepted as an argument on instatiation. | |
| 292 | 310 |
| 293 TODO: more examples, showing the order of addition operation | 311 TODO: more examples, showing the order of addition operation |
| 294 ''' | 312 ''' |
| 295 def __init__(self, spec, sign=1): | 313 def __init__(self, spec, sign=1): |
| 296 """Construct an interval given a specification.""" | 314 """Construct an interval given a specification.""" |
| 309 if not hasattr(other, attr): | 327 if not hasattr(other, attr): |
| 310 return 1 | 328 return 1 |
| 311 r = cmp(getattr(self, attr), getattr(other, attr)) | 329 r = cmp(getattr(self, attr), getattr(other, attr)) |
| 312 if r: return r | 330 if r: return r |
| 313 return 0 | 331 return 0 |
| 314 | 332 |
| 315 def __str__(self): | 333 def __str__(self): |
| 316 """Return this interval as a string.""" | 334 """Return this interval as a string.""" |
| 317 sign = {1:'+', -1:'-'}[self.sign] | 335 sign = {1:'+', -1:'-'}[self.sign] |
| 318 l = [sign] | 336 l = [sign] |
| 319 if self.year: l.append('%sy'%self.year) | 337 if self.year: l.append('%sy'%self.year) |
| 323 l.append('%d:%02d:%02d'%(self.hour, self.minute, self.second)) | 341 l.append('%d:%02d:%02d'%(self.hour, self.minute, self.second)) |
| 324 elif self.hour or self.minute: | 342 elif self.hour or self.minute: |
| 325 l.append('%d:%02d'%(self.hour, self.minute)) | 343 l.append('%d:%02d'%(self.hour, self.minute)) |
| 326 return ' '.join(l) | 344 return ' '.join(l) |
| 327 | 345 |
| 328 def set(self, spec, interval_re = re.compile(''' | 346 def set(self, spec, interval_re=re.compile(''' |
| 329 \s* | 347 \s*(?P<s>[-+])? # + or - |
| 330 (?P<s>[-+])? # + or - | 348 \s*((?P<y>\d+\s*)y)? # year |
| 331 \s* | 349 \s*((?P<m>\d+\s*)m)? # month |
| 332 ((?P<y>\d+\s*)y)? # year | 350 \s*((?P<w>\d+\s*)w)? # week |
| 333 \s* | 351 \s*((?P<d>\d+\s*)d)? # day |
| 334 ((?P<m>\d+\s*)m)? # month | 352 \s*(((?P<H>\d+):(?P<M>\d+))?(:(?P<S>\d+))?)? # time |
| 335 \s* | 353 \s*''', re.VERBOSE), serialised_re=re.compile(''' |
| 336 ((?P<w>\d+\s*)w)? # week | 354 (?P<s>[+-])(?P<y>\d{4})(?P<m>\d{2})(?P<d>\d{2}) |
| 337 \s* | 355 (?P<H>\d{2})(?P<M>\d{2})(?P<S>\d{2})''', re.VERBOSE)): |
| 338 ((?P<d>\d+\s*)d)? # day | |
| 339 \s* | |
| 340 (((?P<H>\d+):(?P<M>\d+))?(:(?P<S>\d+))?)? # time | |
| 341 \s* | |
| 342 ''', re.VERBOSE)): | |
| 343 ''' set the date to the value in spec | 356 ''' set the date to the value in spec |
| 344 ''' | 357 ''' |
| 345 self.year = self.month = self.week = self.day = self.hour = \ | 358 self.year = self.month = self.week = self.day = self.hour = \ |
| 346 self.minute = self.second = 0 | 359 self.minute = self.second = 0 |
| 347 self.sign = 1 | 360 self.sign = 1 |
| 348 m = interval_re.match(spec) | 361 m = serialised_re.match(spec) |
| 349 if not m: | 362 if not m: |
| 350 raise ValueError, _('Not an interval spec: [+-] [#y] [#m] [#w] ' | 363 m = interval_re.match(spec) |
| 351 '[#d] [[[H]H:MM]:SS]') | 364 if not m: |
| 365 raise ValueError, _('Not an interval spec: [+-] [#y] [#m] [#w] ' | |
| 366 '[#d] [[[H]H:MM]:SS]') | |
| 352 | 367 |
| 353 info = m.groupdict() | 368 info = m.groupdict() |
| 354 for group, attr in {'y':'year', 'm':'month', 'w':'week', 'd':'day', | 369 for group, attr in {'y':'year', 'm':'month', 'w':'week', 'd':'day', |
| 355 'H':'hour', 'M':'minute', 'S':'second'}.items(): | 370 'H':'hour', 'M':'minute', 'S':'second'}.items(): |
| 356 if info[group] is not None: | 371 if info.getr(group, None) is not None: |
| 357 setattr(self, attr, int(info[group])) | 372 setattr(self, attr, int(info[group])) |
| 358 | 373 |
| 359 if self.week: | 374 if self.week: |
| 360 self.day = self.day + self.week*7 | 375 self.day = self.day + self.week*7 |
| 361 | 376 |
| 414 else: | 429 else: |
| 415 s = _('%(number)s/4 hour')%{'number': int(self.minute/15)} | 430 s = _('%(number)s/4 hour')%{'number': int(self.minute/15)} |
| 416 return s | 431 return s |
| 417 | 432 |
| 418 def get_tuple(self): | 433 def get_tuple(self): |
| 419 return (self.year, self.month, self.day, self.hour, self.minute, | 434 return (self.sign, self.year, self.month, self.day, self.hour, |
| 420 self.second) | 435 self.minute, self.second) |
| 436 | |
| 437 def serialise(self): | |
| 438 return '%s%4d%02d%02d%02d%02d%02d'%(self.sign, self.year, self.month, | |
| 439 self.day, self.hour, self.minute, self.second) | |
| 421 | 440 |
| 422 | 441 |
| 423 def test(): | 442 def test(): |
| 424 intervals = (" 3w 1 d 2:00", " + 2d", "3w") | 443 intervals = (" 3w 1 d 2:00", " + 2d", "3w") |
| 425 for interval in intervals: | 444 for interval in intervals: |
| 440 if __name__ == '__main__': | 459 if __name__ == '__main__': |
| 441 test() | 460 test() |
| 442 | 461 |
| 443 # | 462 # |
| 444 # $Log: not supported by cvs2svn $ | 463 # $Log: not supported by cvs2svn $ |
| 464 # Revision 1.23 2002/07/18 23:07:08 richard | |
| 465 # Unit tests and a few fixes. | |
| 466 # | |
| 445 # Revision 1.22 2002/07/14 06:05:50 richard | 467 # Revision 1.22 2002/07/14 06:05:50 richard |
| 446 # . fixed the date module so that Date(". - 2d") works | 468 # . fixed the date module so that Date(". - 2d") works |
| 447 # | 469 # |
| 448 # Revision 1.21 2002/05/15 06:32:46 richard | 470 # Revision 1.21 2002/05/15 06:32:46 richard |
| 449 # . reverting to dates for intervals > 2 months sucks | 471 # . reverting to dates for intervals > 2 months sucks |
