Package parsedatetime :: Module parsedatetime
[hide private]
[frames] | no frames]

Source Code for Module parsedatetime.parsedatetime

   1  #!/usr/bin/env python
 
   2  
 
   3  """
 
   4  Parse human-readable date/time text.
 
   5  """ 
   6  
 
   7  __license__ = """
 
   8  Copyright (c) 2004-2007 Mike Taylor
 
   9  Copyright (c) 2007 Darshana Chhajed
 
  10  All rights reserved.
 
  11  
 
  12  Licensed under the Apache License, Version 2.0 (the "License");
 
  13  you may not use this file except in compliance with the License.
 
  14  You may obtain a copy of the License at
 
  15  
 
  16     http://www.apache.org/licenses/LICENSE-2.0
 
  17  
 
  18  Unless required by applicable law or agreed to in writing, software
 
  19  distributed under the License is distributed on an "AS IS" BASIS,
 
  20  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
  21  See the License for the specific language governing permissions and
 
  22  limitations under the License.
 
  23  """ 
  24  
 
  25  _debug = False 
  26  
 
  27  
 
  28  import re 
  29  import time 
  30  import datetime 
  31  import rfc822 
  32  import parsedatetime_consts 
  33  
 
  34  
 
  35  # Copied from feedparser.py
 
  36  # Universal Feedparser
 
  37  # Copyright (c) 2002-2006, Mark Pilgrim, All rights reserved.
 
  38  # Originally a def inside of _parse_date_w3dtf()
 
39 -def _extract_date(m):
40 year = int(m.group('year')) 41 if year < 100: 42 year = 100 * int(time.gmtime()[0] / 100) + int(year) 43 if year < 1000: 44 return 0, 0, 0 45 julian = m.group('julian') 46 if julian: 47 julian = int(julian) 48 month = julian / 30 + 1 49 day = julian % 30 + 1 50 jday = None 51 while jday != julian: 52 t = time.mktime((year, month, day, 0, 0, 0, 0, 0, 0)) 53 jday = time.gmtime(t)[-2] 54 diff = abs(jday - julian) 55 if jday > julian: 56 if diff < day: 57 day = day - diff 58 else: 59 month = month - 1 60 day = 31 61 elif jday < julian: 62 if day + diff < 28: 63 day = day + diff 64 else: 65 month = month + 1 66 return year, month, day 67 month = m.group('month') 68 day = 1 69 if month is None: 70 month = 1 71 else: 72 month = int(month) 73 day = m.group('day') 74 if day: 75 day = int(day) 76 else: 77 day = 1 78 return year, month, day
79 80 # Copied from feedparser.py 81 # Universal Feedparser 82 # Copyright (c) 2002-2006, Mark Pilgrim, All rights reserved. 83 # Originally a def inside of _parse_date_w3dtf()
84 -def _extract_time(m):
85 if not m: 86 return 0, 0, 0 87 hours = m.group('hours') 88 if not hours: 89 return 0, 0, 0 90 hours = int(hours) 91 minutes = int(m.group('minutes')) 92 seconds = m.group('seconds') 93 if seconds: 94 seconds = int(seconds) 95 else: 96 seconds = 0 97 return hours, minutes, seconds
98 99 100 # Copied from feedparser.py 101 # Universal Feedparser 102 # Copyright (c) 2002-2006, Mark Pilgrim, All rights reserved. 103 # Modified to return a tuple instead of mktime 104 # 105 # Original comment: 106 # W3DTF-style date parsing adapted from PyXML xml.utils.iso8601, written by 107 # Drake and licensed under the Python license. Removed all range checking 108 # for month, day, hour, minute, and second, since mktime will normalize 109 # these later
110 -def _parse_date_w3dtf(dateString):
111 # the __extract_date and __extract_time methods were 112 # copied-out so they could be used by my code --bear 113 def __extract_tzd(m): 114 '''Return the Time Zone Designator as an offset in seconds from UTC.''' 115 if not m: 116 return 0 117 tzd = m.group('tzd') 118 if not tzd: 119 return 0 120 if tzd == 'Z': 121 return 0 122 hours = int(m.group('tzdhours')) 123 minutes = m.group('tzdminutes') 124 if minutes: 125 minutes = int(minutes) 126 else: 127 minutes = 0 128 offset = (hours*60 + minutes) * 60 129 if tzd[0] == '+': 130 return -offset 131 return offset
132 133 __date_re = ('(?P<year>\d\d\d\d)' 134 '(?:(?P<dsep>-|)' 135 '(?:(?P<julian>\d\d\d)' 136 '|(?P<month>\d\d)(?:(?P=dsep)(?P<day>\d\d))?))?') 137 __tzd_re = '(?P<tzd>[-+](?P<tzdhours>\d\d)(?::?(?P<tzdminutes>\d\d))|Z)' 138 __tzd_rx = re.compile(__tzd_re) 139 __time_re = ('(?P<hours>\d\d)(?P<tsep>:|)(?P<minutes>\d\d)' 140 '(?:(?P=tsep)(?P<seconds>\d\d(?:[.,]\d+)?))?' 141 + __tzd_re) 142 __datetime_re = '%s(?:T%s)?' % (__date_re, __time_re) 143 __datetime_rx = re.compile(__datetime_re) 144 m = __datetime_rx.match(dateString) 145 if (m is None) or (m.group() != dateString): return 146 return _extract_date(m) + _extract_time(m) + (0, 0, 0) 147 148 149 # Copied from feedparser.py 150 # Universal Feedparser 151 # Copyright (c) 2002-2006, Mark Pilgrim, All rights reserved. 152 # Modified to return a tuple instead of mktime 153 #
154 -def _parse_date_rfc822(dateString):
155 '''Parse an RFC822, RFC1123, RFC2822, or asctime-style date''' 156 data = dateString.split() 157 if data[0][-1] in (',', '.') or data[0].lower() in rfc822._daynames: 158 del data[0] 159 if len(data) == 4: 160 s = data[3] 161 i = s.find('+') 162 if i > 0: 163 data[3:] = [s[:i], s[i+1:]] 164 else: 165 data.append('') 166 dateString = " ".join(data) 167 if len(data) < 5: 168 dateString += ' 00:00:00 GMT' 169 return rfc822.parsedate_tz(dateString)
170 171 # rfc822.py defines several time zones, but we define some extra ones. 172 # 'ET' is equivalent to 'EST', etc. 173 _additional_timezones = {'AT': -400, 'ET': -500, 174 'CT': -600, 'MT': -700, 175 'PT': -800} 176 rfc822._timezones.update(_additional_timezones) 177 178
179 -class Calendar:
180 """ 181 A collection of routines to input, parse and manipulate date and times. 182 The text can either be 'normal' date values or it can be human readable. 183 """ 184
185 - def __init__(self, constants=None):
186 """ 187 Default constructor for the L{Calendar} class. 188 189 @type constants: object 190 @param constants: Instance of the class L{parsedatetime_consts.Constants} 191 192 @rtype: object 193 @return: L{Calendar} instance 194 """ 195 # if a constants reference is not included, use default 196 if constants is None: 197 self.ptc = parsedatetime_consts.Constants() 198 else: 199 self.ptc = constants 200 201 self.weekdyFlag = False # monday/tuesday/... 202 self.dateStdFlag = False # 07/21/06 203 self.dateStrFlag = False # July 21st, 2006 204 self.timeStdFlag = False # 5:50 205 self.meridianFlag = False # am/pm 206 self.dayStrFlag = False # tomorrow/yesterday/today/.. 207 self.timeStrFlag = False # lunch/noon/breakfast/... 208 self.modifierFlag = False # after/before/prev/next/.. 209 self.modifier2Flag = False # after/before/prev/next/.. 210 self.unitsFlag = False # hrs/weeks/yrs/min/.. 211 self.qunitsFlag = False # h/m/t/d.. 212 213 self.timeFlag = 0 214 self.dateFlag = 0
215 216
217 - def _convertUnitAsWords(self, unitText):
218 """ 219 Converts text units into their number value 220 221 Five = 5 222 Twenty Five = 25 223 Two hundred twenty five = 225 224 Two thousand and twenty five = 2025 225 Two thousand twenty five = 2025 226 227 @type unitText: string 228 @param unitText: number text to convert 229 230 @rtype: integer 231 @return: numerical value of unitText 232 """ 233 # TODO: implement this 234 pass
235 236
237 - def _buildTime(self, source, quantity, modifier, units):
238 """ 239 Take C{quantity}, C{modifier} and C{unit} strings and convert them into values. 240 After converting, calcuate the time and return the adjusted sourceTime. 241 242 @type source: time 243 @param source: time to use as the base (or source) 244 @type quantity: string 245 @param quantity: quantity string 246 @type modifier: string 247 @param modifier: how quantity and units modify the source time 248 @type units: string 249 @param units: unit of the quantity (i.e. hours, days, months, etc) 250 251 @rtype: struct_time 252 @return: C{struct_time} of the calculated time 253 """ 254 if _debug: 255 print '_buildTime: [%s][%s][%s]' % (quantity, modifier, units) 256 257 if source is None: 258 source = time.localtime() 259 260 if quantity is None: 261 quantity = '' 262 else: 263 quantity = quantity.strip() 264 265 if len(quantity) == 0: 266 qty = 1 267 else: 268 try: 269 qty = int(quantity) 270 except ValueError: 271 qty = 0 272 273 if modifier in self.ptc.Modifiers: 274 qty = qty * self.ptc.Modifiers[modifier] 275 276 if units is None or units == '': 277 units = 'dy' 278 279 # plurals are handled by regex's (could be a bug tho) 280 281 (yr, mth, dy, hr, mn, sec, _, _, _) = source 282 283 start = datetime.datetime(yr, mth, dy, hr, mn, sec) 284 target = start 285 286 if units.startswith('y'): 287 target = self.inc(start, year=qty) 288 self.dateFlag = 1 289 elif units.endswith('th') or units.endswith('ths'): 290 target = self.inc(start, month=qty) 291 self.dateFlag = 1 292 else: 293 if units.startswith('d'): 294 target = start + datetime.timedelta(days=qty) 295 self.dateFlag = 1 296 elif units.startswith('h'): 297 target = start + datetime.timedelta(hours=qty) 298 self.timeFlag = 2 299 elif units.startswith('m'): 300 target = start + datetime.timedelta(minutes=qty) 301 self.timeFlag = 2 302 elif units.startswith('s'): 303 target = start + datetime.timedelta(seconds=qty) 304 self.timeFlag = 2 305 elif units.startswith('w'): 306 target = start + datetime.timedelta(weeks=qty) 307 self.dateFlag = 1 308 309 return target.timetuple()
310 311
312 - def parseDate(self, dateString):
313 """ 314 Parse short-form date strings:: 315 316 '05/28/2006' or '04.21' 317 318 @type dateString: string 319 @param dateString: text to convert to a C{datetime} 320 321 @rtype: struct_time 322 @return: calculated C{struct_time} value of dateString 323 """ 324 yr, mth, dy, hr, mn, sec, wd, yd, isdst = time.localtime() 325 326 # values pulled from regex's will be stored here and later 327 # assigned to mth, dy, yr based on information from the locale 328 # -1 is used as the marker value because we want zero values 329 # to be passed thru so they can be flagged as errors later 330 v1 = -1 331 v2 = -1 332 v3 = -1 333 334 s = dateString 335 m = self.ptc.CRE_DATE2.search(s) 336 if m is not None: 337 index = m.start() 338 v1 = int(s[:index]) 339 s = s[index + 1:] 340 341 m = self.ptc.CRE_DATE2.search(s) 342 if m is not None: 343 index = m.start() 344 v2 = int(s[:index]) 345 v3 = int(s[index + 1:]) 346 else: 347 v2 = int(s.strip()) 348 349 v = [ v1, v2, v3 ] 350 d = { 'm': mth, 'd': dy, 'y': yr } 351 352 for i in range(0, 3): 353 n = v[i] 354 c = self.ptc.dp_order[i] 355 if n >= 0: 356 d[c] = n 357 358 # if the year is not specified and the date has already 359 # passed, increment the year 360 if v3 == -1 and ((mth > d['m']) or (mth == d['m'] and dy > d['d'])): 361 yr = d['y'] + 1 362 else: 363 yr = d['y'] 364 365 mth = d['m'] 366 dy = d['d'] 367 368 # birthday epoch constraint 369 if yr < self.ptc.BirthdayEpoch: 370 yr += 2000 371 elif yr < 100: 372 yr += 1900 373 374 if (mth > 0 and mth <= 12) and \ 375 (dy > 0 and dy <= self.ptc.DaysInMonthList[mth - 1]): 376 sourceTime = (yr, mth, dy, hr, mn, sec, wd, yd, isdst) 377 else: 378 self.dateFlag = 0 379 self.timeFlag = 0 380 sourceTime = time.localtime() # return current time if date 381 # string is invalid 382 383 return sourceTime
384 385
386 - def parseDateText(self, dateString):
387 """ 388 Parse long-form date strings:: 389 390 'May 31st, 2006' 391 'Jan 1st' 392 'July 2006' 393 394 @type dateString: string 395 @param dateString: text to convert to a datetime 396 397 @rtype: struct_time 398 @return: calculated C{struct_time} value of dateString 399 """ 400 yr, mth, dy, hr, mn, sec, wd, yd, isdst = time.localtime() 401 402 currentMth = mth 403 currentDy = dy 404 405 s = dateString.lower() 406 m = self.ptc.CRE_DATE3.search(s) 407 mth = m.group('mthname') 408 mth = self.ptc.MonthOffsets[mth] 409 410 if m.group('day') != None: 411 dy = int(m.group('day')) 412 else: 413 dy = 1 414 415 if m.group('year') != None: 416 yr = int(m.group('year')) 417 418 # birthday epoch constraint 419 if yr < self.ptc.BirthdayEpoch: 420 yr += 2000 421 elif yr < 100: 422 yr += 1900 423 424 elif (mth < currentMth) or (mth == currentMth and dy < currentDy): 425 # if that day and month have already passed in this year, 426 # then increment the year by 1 427 yr += 1 428 429 if dy > 0 and dy <= self.ptc.DaysInMonthList[mth - 1]: 430 sourceTime = (yr, mth, dy, hr, mn, sec, wd, yd, isdst) 431 else: 432 # Return current time if date string is invalid 433 self.dateFlag = 0 434 self.timeFlag = 0 435 sourceTime = time.localtime() 436 437 return sourceTime
438 439
440 - def evalRanges(self, datetimeString, sourceTime=None):
441 """ 442 Evaluate the C{datetimeString} text and determine if 443 it represents a date or time range. 444 445 @type datetimeString: string 446 @param datetimeString: datetime text to evaluate 447 @type sourceTime: struct_time 448 @param sourceTime: C{struct_time} value to use as the base 449 450 @rtype: tuple 451 @return: tuple of: start datetime, end datetime and the invalid flag 452 """ 453 startTime = '' 454 endTime = '' 455 startDate = '' 456 endDate = '' 457 rangeFlag = 0 458 459 s = datetimeString.strip().lower() 460 461 m = self.ptc.CRE_TIMERNG1.search(s) 462 if m is not None: 463 rangeFlag = 1 464 else: 465 m = self.ptc.CRE_TIMERNG2.search(s) 466 if m is not None: 467 rangeFlag = 2 468 else: 469 m = self.ptc.CRE_TIMERNG3.search(s) 470 if m is not None: 471 rangeFlag = 3 472 else: 473 m = self.ptc.CRE_DATERNG1.search(s) 474 if m is not None: 475 rangeFlag = 4 476 else: 477 m = self.ptc.CRE_DATERNG2.search(s) 478 if m is not None: 479 rangeFlag = 5 480 else: 481 m = self.ptc.CRE_DATERNG3.search(s) 482 if m is not None: 483 rangeFlag = 6 484 485 if _debug: 486 print 'evalRanges: rangeFlag =', rangeFlag, '[%s]' % s 487 488 if m is not None: 489 if (m.group() != s): 490 # capture remaining string 491 parseStr = m.group() 492 chunk1 = s[:m.start()] 493 chunk2 = s[m.end():] 494 s = '%s %s' % (chunk1, chunk2) 495 flag = 1 496 497 sourceTime, flag = self.parse(s, sourceTime) 498 499 if flag == 0: 500 sourceTime = None 501 else: 502 parseStr = s 503 504 if rangeFlag == 1: 505 m = re.search(self.ptc.rangeSep, parseStr) 506 startTime, sflag = self.parse((parseStr[:m.start()]), sourceTime) 507 endTime, eflag = self.parse((parseStr[(m.start() + 1):]), sourceTime) 508 509 if (eflag != 0) and (sflag != 0): 510 return (startTime, endTime, 2) 511 512 elif rangeFlag == 2: 513 m = re.search(self.ptc.rangeSep, parseStr) 514 startTime, sflag = self.parse((parseStr[:m.start()]), sourceTime) 515 endTime, eflag = self.parse((parseStr[(m.start() + 1):]), sourceTime) 516 517 if (eflag != 0) and (sflag != 0): 518 return (startTime, endTime, 2) 519 520 elif rangeFlag == 3: 521 m = re.search(self.ptc.rangeSep, parseStr) 522 523 # capturing the meridian from the end time 524 if self.ptc.usesMeridian: 525 ampm = re.search(self.ptc.am[0], parseStr) 526 527 # appending the meridian to the start time 528 if ampm is not None: 529 startTime, sflag = self.parse((parseStr[:m.start()] + self.ptc.meridian[0]), sourceTime) 530 else: 531 startTime, sflag = self.parse((parseStr[:m.start()] + self.ptc.meridian[1]), sourceTime) 532 else: 533 startTime, sflag = self.parse((parseStr[:m.start()]), sourceTime) 534 535 endTime, eflag = self.parse(parseStr[(m.start() + 1):], sourceTime) 536 537 if (eflag != 0) and (sflag != 0): 538 return (startTime, endTime, 2) 539 540 elif rangeFlag == 4: 541 m = re.search(self.ptc.rangeSep, parseStr) 542 startDate, sflag = self.parse((parseStr[:m.start()]), sourceTime) 543 endDate, eflag = self.parse((parseStr[(m.start() + 1):]), sourceTime) 544 545 if (eflag != 0) and (sflag != 0): 546 return (startDate, endDate, 1) 547 548 elif rangeFlag == 5: 549 m = re.search(self.ptc.rangeSep, parseStr) 550 endDate = parseStr[(m.start() + 1):] 551 552 # capturing the year from the end date 553 date = self.ptc.CRE_DATE3.search(endDate) 554 endYear = date.group('year') 555 556 # appending the year to the start date if the start date 557 # does not have year information and the end date does. 558 # eg : "Aug 21 - Sep 4, 2007" 559 if endYear is not None: 560 startDate = (parseStr[:m.start()]).strip() 561 date = self.ptc.CRE_DATE3.search(startDate) 562 startYear = date.group('year') 563 564 if startYear is None: 565 startDate = startDate + ', ' + endYear 566 else: 567 startDate = parseStr[:m.start()] 568 569 startDate, sflag = self.parse(startDate, sourceTime) 570 endDate, eflag = self.parse(endDate, sourceTime) 571 572 if (eflag != 0) and (sflag != 0): 573 return (startDate, endDate, 1) 574 575 elif rangeFlag == 6: 576 m = re.search(self.ptc.rangeSep, parseStr) 577 578 startDate = parseStr[:m.start()] 579 580 # capturing the month from the start date 581 mth = self.ptc.CRE_DATE3.search(startDate) 582 mth = mth.group('mthname') 583 584 # appending the month name to the end date 585 endDate = mth + parseStr[(m.start() + 1):] 586 587 startDate, sflag = self.parse(startDate, sourceTime) 588 endDate, eflag = self.parse(endDate, sourceTime) 589 590 if (eflag != 0) and (sflag != 0): 591 return (startDate, endDate, 1) 592 else: 593 # if range is not found 594 sourceTime = time.localtime() 595 596 return (sourceTime, sourceTime, 0)
597 598
599 - def _CalculateDOWDelta(self, wd, wkdy, offset, style, currentDayStyle):
600 """ 601 Based on the C{style} and C{currentDayStyle} determine what 602 day-of-week value is to be returned. 603 604 @type wd: integer 605 @param wd: day-of-week value for the current day 606 @type wkdy: integer 607 @param wkdy: day-of-week value for the parsed day 608 @type offset: integer 609 @param offset: offset direction for any modifiers (-1, 0, 1) 610 @type style: integer 611 @param style: normally the value set in C{Constants.DOWParseStyle} 612 @type currentDayStyle: integer 613 @param currentDayStyle: normally the value set in C{Constants.CurrentDOWParseStyle} 614 615 @rtype: integer 616 @return: calculated day-of-week 617 """ 618 if offset == 1: 619 # modifier is indicating future week eg: "next". 620 # DOW is calculated as DOW of next week 621 diff = 7 - wd + wkdy 622 623 elif offset == -1: 624 # modifier is indicating past week eg: "last","previous" 625 # DOW is calculated as DOW of previous week 626 diff = wkdy - wd - 7 627 628 elif offset == 0: 629 # modifier is indiacting current week eg: "this" 630 # DOW is calculated as DOW of this week 631 diff = wkdy - wd 632 633 elif offset == 2: 634 # no modifier is present. 635 # i.e. string to be parsed is just DOW 636 if style == 1: 637 # next occurance of the DOW is calculated 638 if currentDayStyle == True: 639 if wkdy >= wd: 640 diff = wkdy - wd 641 else: 642 diff = 7 - wd + wkdy 643 else: 644 if wkdy > wd: 645 diff = wkdy - wd 646 else: 647 diff = 7 - wd + wkdy 648 649 elif style == -1: 650 # last occurance of the DOW is calculated 651 if currentDayStyle == True: 652 if wkdy <= wd: 653 diff = wkdy - wd 654 else: 655 diff = wkdy - wd - 7 656 else: 657 if wkdy < wd: 658 diff = wkdy - wd 659 else: 660 diff = wkdy - wd - 7 661 else: 662 # occurance of the DOW in the current week is calculated 663 diff = wkdy - wd 664 665 if _debug: 666 print "wd %s, wkdy %s, offset %d, style %d\n" % (wd, wkdy, offset, style) 667 668 return diff
669 670
671 - def _evalModifier(self, modifier, chunk1, chunk2, sourceTime):
672 """ 673 Evaluate the C{modifier} string and following text (passed in 674 as C{chunk1} and C{chunk2}) and if they match any known modifiers 675 calculate the delta and apply it to C{sourceTime}. 676 677 @type modifier: string 678 @param modifier: modifier text to apply to sourceTime 679 @type chunk1: string 680 @param chunk1: first text chunk that followed modifier (if any) 681 @type chunk2: string 682 @param chunk2: second text chunk that followed modifier (if any) 683 @type sourceTime: struct_time 684 @param sourceTime: C{struct_time} value to use as the base 685 686 @rtype: tuple 687 @return: tuple of: remaining text and the modified sourceTime 688 """ 689 offset = self.ptc.Modifiers[modifier] 690 691 if sourceTime is not None: 692 (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = sourceTime 693 else: 694 (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = time.localtime() 695 696 # capture the units after the modifier and the remaining 697 # string after the unit 698 m = self.ptc.CRE_REMAINING.search(chunk2) 699 if m is not None: 700 index = m.start() + 1 701 unit = chunk2[:m.start()] 702 chunk2 = chunk2[index:] 703 else: 704 unit = chunk2 705 chunk2 = '' 706 707 flag = False 708 709 if unit == 'month' or \ 710 unit == 'mth' or \ 711 unit == 'm': 712 if offset == 0: 713 dy = self.ptc.DaysInMonthList[mth - 1] 714 sourceTime = (yr, mth, dy, 9, 0, 0, wd, yd, isdst) 715 elif offset == 2: 716 # if day is the last day of the month, calculate the last day 717 # of the next month 718 if dy == self.ptc.DaysInMonthList[mth - 1]: 719 dy = self.ptc.DaysInMonthList[mth] 720 721 start = datetime.datetime(yr, mth, dy, 9, 0, 0) 722 target = self.inc(start, month=1) 723 sourceTime = target.timetuple() 724 else: 725 start = datetime.datetime(yr, mth, 1, 9, 0, 0) 726 target = self.inc(start, month=offset) 727 sourceTime = target.timetuple() 728 729 flag = True 730 self.dateFlag = 1 731 732 if unit == 'week' or \ 733 unit == 'wk' or \ 734 unit == 'w': 735 if offset == 0: 736 start = datetime.datetime(yr, mth, dy, 17, 0, 0) 737 target = start + datetime.timedelta(days=(4 - wd)) 738 sourceTime = target.timetuple() 739 elif offset == 2: 740 start = datetime.datetime(yr, mth, dy, 9, 0, 0) 741 target = start + datetime.timedelta(days=7) 742 sourceTime = target.timetuple() 743 else: 744 return self._evalModifier(modifier, chunk1, "monday " + chunk2, sourceTime) 745 746 flag = True 747 self.dateFlag = 1 748 749 if unit == 'day' or \ 750 unit == 'dy' or \ 751 unit == 'd': 752 if offset == 0: 753 sourceTime = (yr, mth, dy, 17, 0, 0, wd, yd, isdst) 754 self.timeFlag = 2 755 elif offset == 2: 756 start = datetime.datetime(yr, mth, dy, hr, mn, sec) 757 target = start + datetime.timedelta(days=1) 758 sourceTime = target.timetuple() 759 else: 760 start = datetime.datetime(yr, mth, dy, 9, 0, 0) 761 target = start + datetime.timedelta(days=offset) 762 sourceTime = target.timetuple() 763 764 flag = True 765 self.dateFlag = 1 766 767 if unit == 'hour' or \ 768 unit == 'hr': 769 if offset == 0: 770 sourceTime = (yr, mth, dy, hr, 0, 0, wd, yd, isdst) 771 else: 772 start = datetime.datetime(yr, mth, dy, hr, 0, 0) 773 target = start + datetime.timedelta(hours=offset) 774 sourceTime = target.timetuple() 775 776 flag = True 777 self.timeFlag = 2 778 779 if unit == 'year' or \ 780 unit == 'yr' or \ 781 unit == 'y': 782 if offset == 0: 783 sourceTime = (yr, 12, 31, hr, mn, sec, wd, yd, isdst) 784 elif offset == 2: 785 sourceTime = (yr + 1, mth, dy, hr, mn, sec, wd, yd, isdst) 786 else: 787 sourceTime = (yr + offset, 1, 1, 9, 0, 0, wd, yd, isdst) 788 789 flag = True 790 self.dateFlag = 1 791 792 if flag == False: 793 m = self.ptc.CRE_WEEKDAY.match(unit) 794 if m is not None: 795 wkdy = m.group() 796 self.dateFlag = 1 797 798 if modifier == 'eod': 799 # Calculate the upcoming weekday 800 self.modifierFlag = False 801 (sourceTime, _) = self.parse(wkdy, sourceTime) 802 sources = self.ptc.buildSources(sourceTime) 803 self.timeFlag = 2 804 805 if modifier in sources: 806 sourceTime = sources[modifier] 807 808 else: 809 wkdy = self.ptc.WeekdayOffsets[wkdy] 810 diff = self._CalculateDOWDelta(wd, wkdy, offset, 811 self.ptc.DOWParseStyle, 812 self.ptc.CurrentDOWParseStyle) 813 start = datetime.datetime(yr, mth, dy, 9, 0, 0) 814 target = start + datetime.timedelta(days=diff) 815 sourceTime = target.timetuple() 816 817 flag = True 818 self.dateFlag = 1 819 820 if not flag: 821 m = self.ptc.CRE_TIME.match(unit) 822 if m is not None: 823 self.modifierFlag = False 824 (yr, mth, dy, hr, mn, sec, wd, yd, isdst), _ = self.parse(unit) 825 826 start = datetime.datetime(yr, mth, dy, hr, mn, sec) 827 target = start + datetime.timedelta(days=offset) 828 sourceTime = target.timetuple() 829 flag = True 830 else: 831 self.modifierFlag = False 832 833 # check if the remaining text is parsable and if so, 834 # use it as the base time for the modifier source time 835 t, flag2 = self.parse('%s %s' % (chunk1, unit), sourceTime) 836 837 if flag2 != 0: 838 sourceTime = t 839 840 sources = self.ptc.buildSources(sourceTime) 841 842 if modifier in sources: 843 sourceTime = sources[modifier] 844 flag = True 845 self.timeFlag = 2 846 847 # if the word after next is a number, the string is more than likely 848 # to be "next 4 hrs" which we will have to combine the units with the 849 # rest of the string 850 if not flag: 851 if offset < 0: 852 # if offset is negative, the unit has to be made negative 853 unit = '-%s' % unit 854 855 chunk2 = '%s %s' % (unit, chunk2) 856 857 self.modifierFlag = False 858 859 #return '%s %s' % (chunk1, chunk2), sourceTime 860 return '%s' % chunk2, sourceTime
861
862 - def _evalModifier2(self, modifier, chunk1 , chunk2, sourceTime):
863 """ 864 Evaluate the C{modifier} string and following text (passed in 865 as C{chunk1} and C{chunk2}) and if they match any known modifiers 866 calculate the delta and apply it to C{sourceTime}. 867 868 @type modifier: string 869 @param modifier: modifier text to apply to C{sourceTime} 870 @type chunk1: string 871 @param chunk1: first text chunk that followed modifier (if any) 872 @type chunk2: string 873 @param chunk2: second text chunk that followed modifier (if any) 874 @type sourceTime: struct_time 875 @param sourceTime: C{struct_time} value to use as the base 876 877 @rtype: tuple 878 @return: tuple of: remaining text and the modified sourceTime 879 """ 880 offset = self.ptc.Modifiers[modifier] 881 digit = r'\d+' 882 883 self.modifier2Flag = False 884 885 # If the string after the negative modifier starts with digits, 886 # then it is likely that the string is similar to ' before 3 days' 887 # or 'evening prior to 3 days'. 888 # In this case, the total time is calculated by subtracting '3 days' 889 # from the current date. 890 # So, we have to identify the quantity and negate it before parsing 891 # the string. 892 # This is not required for strings not starting with digits since the 893 # string is enough to calculate the sourceTime 894 if chunk2 != '': 895 if offset < 0: 896 m = re.match(digit, chunk2.strip()) 897 if m is not None: 898 qty = int(m.group()) * -1 899 chunk2 = chunk2[m.end():] 900 chunk2 = '%d%s' % (qty, chunk2) 901 902 sourceTime, flag1 = self.parse(chunk2, sourceTime) 903 if flag1 == 0: 904 flag1 = True 905 else: 906 flag1 = False 907 flag2 = False 908 else: 909 flag1 = False 910 911 if chunk1 != '': 912 if offset < 0: 913 m = re.search(digit, chunk1.strip()) 914 if m is not None: 915 qty = int(m.group()) * -1 916 chunk1 = chunk1[m.end():] 917 chunk1 = '%d%s' % (qty, chunk1) 918 919 tempDateFlag = self.dateFlag 920 tempTimeFlag = self.timeFlag 921 sourceTime2, flag2 = self.parse(chunk1, sourceTime) 922 else: 923 return sourceTime, (flag1 and flag2) 924 925 # if chunk1 is not a datetime and chunk2 is then do not use datetime 926 # value returned by parsing chunk1 927 if not (flag1 == False and flag2 == 0): 928 sourceTime = sourceTime2 929 else: 930 self.timeFlag = tempTimeFlag 931 self.dateFlag = tempDateFlag 932 933 return sourceTime, (flag1 and flag2)
934 935
936 - def _evalString(self, datetimeString, sourceTime=None):
937 """ 938 Calculate the datetime based on flags set by the L{parse()} routine 939 940 Examples handled:: 941 RFC822, W3CDTF formatted dates 942 HH:MM[:SS][ am/pm] 943 MM/DD/YYYY 944 DD MMMM YYYY 945 946 @type datetimeString: string 947 @param datetimeString: text to try and parse as more "traditional" 948 date/time text 949 @type sourceTime: struct_time 950 @param sourceTime: C{struct_time} value to use as the base 951 952 @rtype: datetime 953 @return: calculated C{struct_time} value or current C{struct_time} 954 if not parsed 955 """ 956 s = datetimeString.strip() 957 now = time.localtime() 958 959 # Given string date is a RFC822 date 960 if sourceTime is None: 961 sourceTime = _parse_date_rfc822(s) 962 963 if sourceTime is not None: 964 (yr, mth, dy, hr, mn, sec, wd, yd, isdst, _) = sourceTime 965 self.dateFlag = 1 966 967 if (hr != 0) and (mn != 0) and (sec != 0): 968 self.timeFlag = 2 969 970 sourceTime = (yr, mth, dy, hr, mn, sec, wd, yd, isdst) 971 972 # Given string date is a W3CDTF date 973 if sourceTime is None: 974 sourceTime = _parse_date_w3dtf(s) 975 976 if sourceTime is not None: 977 self.dateFlag = 1 978 self.timeFlag = 2 979 980 if sourceTime is None: 981 s = s.lower() 982 983 # Given string is in the format HH:MM(:SS)(am/pm) 984 if self.meridianFlag: 985 if sourceTime is None: 986 (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = now 987 else: 988 (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = sourceTime 989 990 m = self.ptc.CRE_TIMEHMS2.search(s) 991 if m is not None: 992 dt = s[:m.start('meridian')].strip() 993 if len(dt) <= 2: 994 hr = int(dt) 995 mn = 0 996 sec = 0 997 else: 998 hr, mn, sec = _extract_time(m) 999 1000 if hr == 24: 1001 hr = 0 1002 1003 sourceTime = (yr, mth, dy, hr, mn, sec, wd, yd, isdst) 1004 meridian = m.group('meridian').lower() 1005 1006 # if 'am' found and hour is 12 - force hour to 0 (midnight) 1007 if (meridian in self.ptc.am) and hr == 12: 1008 sourceTime = (yr, mth, dy, 0, mn, sec, wd, yd, isdst) 1009 1010 # if 'pm' found and hour < 12, add 12 to shift to evening 1011 if (meridian in self.ptc.pm) and hr < 12: 1012 sourceTime = (yr, mth, dy, hr + 12, mn, sec, wd, yd, isdst) 1013 1014 # invalid time 1015 if hr > 24 or mn > 59 or sec > 59: 1016 sourceTime = now 1017 self.dateFlag = 0 1018 self.timeFlag = 0 1019 1020 self.meridianFlag = False 1021 1022 # Given string is in the format HH:MM(:SS) 1023 if self.timeStdFlag: 1024 if sourceTime is None: 1025 (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = now 1026 else: 1027 (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = sourceTime 1028 1029 m = self.ptc.CRE_TIMEHMS.search(s) 1030 if m is not None: 1031 hr, mn, sec = _extract_time(m) 1032 if hr == 24: 1033 hr = 0 1034 1035 if hr > 24 or mn > 59 or sec > 59: 1036 # invalid time 1037 sourceTime = now 1038 self.dateFlag = 0 1039 self.timeFlag = 0 1040 else: 1041 sourceTime = (yr, mth, dy, hr, mn, sec, wd, yd, isdst) 1042 1043 self.timeStdFlag = False 1044 1045 # Given string is in the format 07/21/2006 1046 if self.dateStdFlag: 1047 sourceTime = self.parseDate(s) 1048 self.dateStdFlag = False 1049 1050 # Given string is in the format "May 23rd, 2005" 1051 if self.dateStrFlag: 1052 sourceTime = self.parseDateText(s) 1053 self.dateStrFlag = False 1054 1055 # Given string is a weekday 1056 if self.weekdyFlag: 1057 (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = now 1058 1059 start = datetime.datetime(yr, mth, dy, hr, mn, sec) 1060 wkdy = self.ptc.WeekdayOffsets[s] 1061 1062 if wkdy > wd: 1063 qty = self._CalculateDOWDelta(wd, wkdy, 2, 1064 self.ptc.DOWParseStyle, 1065 self.ptc.CurrentDOWParseStyle) 1066 else: 1067 qty = self._CalculateDOWDelta(wd, wkdy, 2, 1068 self.ptc.DOWParseStyle, 1069 self.ptc.CurrentDOWParseStyle) 1070 1071 target = start + datetime.timedelta(days=qty) 1072 wd = wkdy 1073 1074 sourceTime = target.timetuple() 1075 self.weekdyFlag = False 1076 1077 # Given string is a natural language time string like 1078 # lunch, midnight, etc 1079 if self.timeStrFlag: 1080 if s in self.ptc.re_values['now']: 1081 sourceTime = now 1082 else: 1083 sources = self.ptc.buildSources(sourceTime) 1084 1085 if s in sources: 1086 sourceTime = sources[s] 1087 else: 1088 sourceTime = now 1089 self.dateFlag = 0 1090 self.timeFlag = 0 1091 1092 self.timeStrFlag = False 1093 1094 # Given string is a natural language date string like today, tomorrow.. 1095 if self.dayStrFlag: 1096 if sourceTime is None: 1097 sourceTime = now 1098 1099 (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = sourceTime 1100 1101 if s in self.ptc.dayOffsets: 1102 offset = self.ptc.dayOffsets[s] 1103 else: 1104 offset = 0 1105 1106 start = datetime.datetime(yr, mth, dy, 9, 0, 0) 1107 target = start + datetime.timedelta(days=offset) 1108 sourceTime = target.timetuple() 1109 1110 self.dayStrFlag = False 1111 1112 # Given string is a time string with units like "5 hrs 30 min" 1113 if self.unitsFlag: 1114 modifier = '' # TODO 1115 1116 if sourceTime is None: 1117 sourceTime = now 1118 1119 m = self.ptc.CRE_UNITS.search(s) 1120 if m is not None: 1121 units = m.group('units') 1122 quantity = s[:m.start('units')] 1123 1124 sourceTime = self._buildTime(sourceTime, quantity, modifier, units) 1125 self.unitsFlag = False 1126 1127 # Given string is a time string with single char units like "5 h 30 m" 1128 if self.qunitsFlag: 1129 modifier = '' # TODO 1130 1131 if sourceTime is None: 1132 sourceTime = now 1133 1134 m = self.ptc.CRE_QUNITS.search(s) 1135 if m is not None: 1136 units = m.group('qunits') 1137 quantity = s[:m.start('qunits')] 1138 1139 sourceTime = self._buildTime(sourceTime, quantity, modifier, units) 1140 self.qunitsFlag = False 1141 1142 # Given string does not match anything 1143 if sourceTime is None: 1144 sourceTime = now 1145 self.dateFlag = 0 1146 self.timeFlag = 0 1147 1148 return sourceTime
1149 1150
1151 - def parse(self, datetimeString, sourceTime=None):
1152 """ 1153 Splits the given C{datetimeString} into tokens, finds the regex 1154 patterns that match and then calculates a C{struct_time} value from 1155 the chunks. 1156 1157 If C{sourceTime} is given then the C{struct_time} value will be 1158 calculated from that value, otherwise from the current date/time. 1159 1160 If the C{datetimeString} is parsed and date/time value found then 1161 the second item of the returned tuple will be a flag to let you know 1162 what kind of C{struct_time} value is being returned:: 1163 1164 0 = not parsed at all 1165 1 = parsed as a C{date} 1166 2 = parsed as a C{time} 1167 3 = parsed as a C{datetime} 1168 1169 @type datetimeString: string 1170 @param datetimeString: date/time text to evaluate 1171 @type sourceTime: struct_time 1172 @param sourceTime: C{struct_time} value to use as the base 1173 1174 @rtype: tuple 1175 @return: tuple of: modified C{sourceTime} and the result flag 1176 """ 1177 1178 if sourceTime: 1179 if isinstance(sourceTime, datetime.datetime): 1180 if _debug: 1181 print 'coercing datetime to timetuple' 1182 sourceTime = sourceTime.timetuple() 1183 else: 1184 if not isinstance(sourceTime, time.struct_time) and \ 1185 not isinstance(sourceTime, tuple): 1186 raise Exception('sourceTime is not a struct_time') 1187 1188 s = datetimeString.strip().lower() 1189 parseStr = '' 1190 totalTime = sourceTime 1191 1192 if s == '' : 1193 if sourceTime is not None: 1194 return (sourceTime, self.dateFlag + self.timeFlag) 1195 else: 1196 return (time.localtime(), 0) 1197 1198 self.timeFlag = 0 1199 self.dateFlag = 0 1200 1201 while len(s) > 0: 1202 flag = False 1203 chunk1 = '' 1204 chunk2 = '' 1205 1206 if _debug: 1207 print 'parse (top of loop): [%s][%s]' % (s, parseStr) 1208 1209 if parseStr == '': 1210 # Modifier like next\prev.. 1211 m = self.ptc.CRE_MODIFIER.search(s) 1212 if m is not None: 1213 self.modifierFlag = True 1214 if (m.group('modifier') != s): 1215 # capture remaining string 1216 parseStr = m.group('modifier') 1217 chunk1 = s[:m.start('modifier')].strip() 1218 chunk2 = s[m.end('modifier'):].strip() 1219 flag = True 1220 else: 1221 parseStr = s 1222 1223 if parseStr == '': 1224 # Modifier like from\after\prior.. 1225 m = self.ptc.CRE_MODIFIER2.search(s) 1226 if m is not None: 1227 self.modifier2Flag = True 1228 if (m.group('modifier') != s): 1229 # capture remaining string 1230 parseStr = m.group('modifier') 1231 chunk1 = s[:m.start('modifier')].strip() 1232 chunk2 = s[m.end('modifier'):].strip() 1233 flag = True 1234 else: 1235 parseStr = s 1236 1237 if parseStr == '': 1238 # String date format 1239 m = self.ptc.CRE_DATE3.search(s) 1240 if m is not None: 1241 self.dateStrFlag = True 1242 self.dateFlag = 1 1243 if (m.group('date') != s): 1244 # capture remaining string 1245 parseStr = m.group('date') 1246 chunk1 = s[:m.start('date')] 1247 chunk2 = s[m.end('date'):] 1248 s = '%s %s' % (chunk1, chunk2) 1249 flag = True 1250 else: 1251 parseStr = s 1252 1253 if parseStr == '': 1254 # Standard date format 1255 m = self.ptc.CRE_DATE.search(s) 1256 if m is not None: 1257 self.dateStdFlag = True 1258 self.dateFlag = 1 1259 if (m.group('date') != s): 1260 # capture remaining string 1261 parseStr = m.group('date') 1262 chunk1 = s[:m.start('date')] 1263 chunk2 = s[m.end('date'):] 1264 s = '%s %s' % (chunk1, chunk2) 1265 flag = True 1266 else: 1267 parseStr = s 1268 1269 if parseStr == '': 1270 # Natural language day strings 1271 m = self.ptc.CRE_DAY.search(s) 1272 if m is not None: 1273 self.dayStrFlag = True 1274 self.dateFlag = 1 1275 if (m.group('day') != s): 1276 # capture remaining string 1277 parseStr = m.group('day') 1278 chunk1 = s[:m.start('day')] 1279 chunk2 = s[m.end('day'):] 1280 s = '%s %s' % (chunk1, chunk2) 1281 flag = True 1282 else: 1283 parseStr = s 1284 1285 if parseStr == '': 1286 # Quantity + Units 1287 m = self.ptc.CRE_UNITS.search(s) 1288 if m is not None: 1289 self.unitsFlag = True 1290 if (m.group('qty') != s): 1291 # capture remaining string 1292 parseStr = m.group('qty') 1293 chunk1 = s[:m.start('qty')].strip() 1294 chunk2 = s[m.end('qty'):].strip() 1295 1296 if chunk1[-1:] == '-': 1297 parseStr = '-%s' % parseStr 1298 chunk1 = chunk1[:-1] 1299 1300 s = '%s %s' % (chunk1, chunk2) 1301 flag = True 1302 else: 1303 parseStr = s 1304 1305 if parseStr == '': 1306 # Quantity + Units 1307 m = self.ptc.CRE_QUNITS.search(s) 1308 if m is not None: 1309 self.qunitsFlag = True 1310 1311 if (m.group('qty') != s): 1312 # capture remaining string 1313 parseStr = m.group('qty') 1314 chunk1 = s[:m.start('qty')].strip() 1315 chunk2 = s[m.end('qty'):].strip() 1316 1317 if chunk1[-1:] == '-': 1318 parseStr = '-%s' % parseStr 1319 chunk1 = chunk1[:-1] 1320 1321 s = '%s %s' % (chunk1, chunk2) 1322 flag = True 1323 else: 1324 parseStr = s 1325 1326 if parseStr == '': 1327 # Weekday 1328 m = self.ptc.CRE_WEEKDAY.search(s) 1329 if m is not None: 1330 self.weekdyFlag = True 1331 self.dateFlag = 1 1332 if (m.group('weekday') != s): 1333 # capture remaining string 1334 parseStr = m.group('weekday') 1335 chunk1 = s[:m.start('weekday')] 1336 chunk2 = s[m.end('weekday'):] 1337 s = '%s %s' % (chunk1, chunk2) 1338 flag = True 1339 else: 1340 parseStr = s 1341 1342 if parseStr == '': 1343 # Natural language time strings 1344 m = self.ptc.CRE_TIME.search(s) 1345 if m is not None: 1346 self.timeStrFlag = True 1347 self.timeFlag = 2 1348 if (m.group('time') != s): 1349 # capture remaining string 1350 parseStr = m.group('time') 1351 chunk1 = s[:m.start('time')] 1352 chunk2 = s[m.end('time'):] 1353 s = '%s %s' % (chunk1, chunk2) 1354 flag = True 1355 else: 1356 parseStr = s 1357 1358 if parseStr == '': 1359 # HH:MM(:SS) am/pm time strings 1360 m = self.ptc.CRE_TIMEHMS2.search(s) 1361 if m is not None: 1362 self.meridianFlag = True 1363 self.timeFlag = 2 1364 if m.group('minutes') is not None: 1365 if m.group('seconds') is not None: 1366 parseStr = '%s:%s:%s %s' % (m.group('hours'), 1367 m.group('minutes'), 1368 m.group('seconds'), 1369 m.group('meridian')) 1370 else: 1371 parseStr = '%s:%s %s' % (m.group('hours'), 1372 m.group('minutes'), 1373 m.group('meridian')) 1374 else: 1375 parseStr = '%s %s' % (m.group('hours'), 1376 m.group('meridian')) 1377 1378 chunk1 = s[:m.start('hours')] 1379 chunk2 = s[m.end('meridian'):] 1380 1381 s = '%s %s' % (chunk1, chunk2) 1382 flag = True 1383 1384 if parseStr == '': 1385 # HH:MM(:SS) time strings 1386 m = self.ptc.CRE_TIMEHMS.search(s) 1387 if m is not None: 1388 self.timeStdFlag = True 1389 self.timeFlag = 2 1390 if m.group('seconds') is not None: 1391 parseStr = '%s:%s:%s' % (m.group('hours'), 1392 m.group('minutes'), 1393 m.group('seconds')) 1394 chunk1 = s[:m.start('hours')] 1395 chunk2 = s[m.end('seconds'):] 1396 else: 1397 parseStr = '%s:%s' % (m.group('hours'), 1398 m.group('minutes')) 1399 chunk1 = s[:m.start('hours')] 1400 chunk2 = s[m.end('minutes'):] 1401 1402 s = '%s %s' % (chunk1, chunk2) 1403 flag = True 1404 1405 # if string does not match any regex, empty string to 1406 # come out of the while loop 1407 if not flag: 1408 s = '' 1409 1410 if _debug: 1411 print 'parse (bottom) [%s][%s][%s][%s]' % (s, parseStr, chunk1, chunk2) 1412 print 'weekday %s, dateStd %s, dateStr %s, time %s, timeStr %s, meridian %s' % \ 1413 (self.weekdyFlag, self.dateStdFlag, self.dateStrFlag, self.timeStdFlag, self.timeStrFlag, self.meridianFlag) 1414 print 'dayStr %s, modifier %s, modifier2 %s, units %s, qunits %s' % \ 1415 (self.dayStrFlag, self.modifierFlag, self.modifier2Flag, self.unitsFlag, self.qunitsFlag) 1416 1417 # evaluate the matched string 1418 if parseStr != '': 1419 if self.modifierFlag == True: 1420 t, totalTime = self._evalModifier(parseStr, chunk1, chunk2, totalTime) 1421 # t is the unparsed part of the chunks. 1422 # If it is not date/time, return current 1423 # totalTime as it is; else return the output 1424 # after parsing t. 1425 if (t != '') and (t != None): 1426 tempDateFlag = self.dateFlag 1427 tempTimeFlag = self.timeFlag 1428 (totalTime2, flag) = self.parse(t, totalTime) 1429 1430 if flag == 0 and totalTime is not None: 1431 self.timeFlag = tempTimeFlag 1432 self.dateFlag = tempDateFlag 1433 1434 return (totalTime, self.dateFlag + self.timeFlag) 1435 else: 1436 return (totalTime2, self.dateFlag + self.timeFlag) 1437 1438 elif self.modifier2Flag == True: 1439 totalTime, invalidFlag = self._evalModifier2(parseStr, chunk1, chunk2, totalTime) 1440 1441 if invalidFlag == True: 1442 self.dateFlag = 0 1443 self.timeFlag = 0 1444 1445 else: 1446 totalTime = self._evalString(parseStr, totalTime) 1447 parseStr = '' 1448 1449 # String is not parsed at all 1450 if totalTime is None or totalTime == sourceTime: 1451 totalTime = time.localtime() 1452 self.dateFlag = 0 1453 self.timeFlag = 0 1454 1455 return (totalTime, self.dateFlag + self.timeFlag)
1456 1457
1458 - def inc(self, source, month=None, year=None):
1459 """ 1460 Takes the given C{source} date, or current date if none is 1461 passed, and increments it according to the values passed in 1462 by month and/or year. 1463 1464 This routine is needed because Python's C{timedelta()} function 1465 does not allow for month or year increments. 1466 1467 @type source: struct_time 1468 @param source: C{struct_time} value to increment 1469 @type month: integer 1470 @param month: optional number of months to increment 1471 @type year: integer 1472 @param year: optional number of years to increment 1473 1474 @rtype: datetime 1475 @return: C{source} incremented by the number of months and/or years 1476 """ 1477 yr = source.year 1478 mth = source.month 1479 dy = source.day 1480 1481 if year: 1482 try: 1483 yi = int(year) 1484 except ValueError: 1485 yi = 0 1486 1487 yr += yi 1488 1489 if month: 1490 try: 1491 mi = int(month) 1492 except ValueError: 1493 mi = 0 1494 1495 m = abs(mi) 1496 y = m / 12 # how many years are in month increment 1497 m = m % 12 # get remaining months 1498 1499 if mi < 0: 1500 mth = mth - m # sub months from start month 1501 if mth < 1: # cross start-of-year? 1502 y -= 1 # yes - decrement year 1503 mth += 12 # and fix month 1504 else: 1505 mth = mth + m # add months to start month 1506 if mth > 12: # cross end-of-year? 1507 y += 1 # yes - increment year 1508 mth -= 12 # and fix month 1509 1510 yr += y 1511 1512 # if the day ends up past the last day of 1513 # the new month, set it to the last day 1514 if dy > self.ptc.DaysInMonthList[mth - 1]: 1515 dy = self.ptc.DaysInMonthList[mth - 1] 1516 1517 d = source.replace(year=yr, month=mth, day=dy) 1518 1519 return source + (d - source)
1520