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-2008 Mike Taylor
 
   9  Copyright (c) 2006-2008 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 _debug: 375 print 'parseDate: ', yr, mth, dy, self.ptc.daysInMonth(mth, yr) 376 377 if (mth > 0 and mth <= 12) and \ 378 (dy > 0 and dy <= self.ptc.daysInMonth(mth, yr)): 379 sourceTime = (yr, mth, dy, hr, mn, sec, wd, yd, isdst) 380 else: 381 self.dateFlag = 0 382 self.timeFlag = 0 383 sourceTime = time.localtime() # return current time if date 384 # string is invalid 385 386 return sourceTime
387 388
389 - def parseDateText(self, dateString):
390 """ 391 Parse long-form date strings:: 392 393 'May 31st, 2006' 394 'Jan 1st' 395 'July 2006' 396 397 @type dateString: string 398 @param dateString: text to convert to a datetime 399 400 @rtype: struct_time 401 @return: calculated C{struct_time} value of dateString 402 """ 403 yr, mth, dy, hr, mn, sec, wd, yd, isdst = time.localtime() 404 405 currentMth = mth 406 currentDy = dy 407 408 s = dateString.lower() 409 m = self.ptc.CRE_DATE3.search(s) 410 mth = m.group('mthname') 411 mth = self.ptc.MonthOffsets[mth] 412 413 if m.group('day') != None: 414 dy = int(m.group('day')) 415 else: 416 dy = 1 417 418 if m.group('year') != None: 419 yr = int(m.group('year')) 420 421 # birthday epoch constraint 422 if yr < self.ptc.BirthdayEpoch: 423 yr += 2000 424 elif yr < 100: 425 yr += 1900 426 427 elif (mth < currentMth) or (mth == currentMth and dy < currentDy): 428 # if that day and month have already passed in this year, 429 # then increment the year by 1 430 yr += 1 431 432 if dy > 0 and dy <= self.ptc.daysInMonth(mth, yr): 433 sourceTime = (yr, mth, dy, hr, mn, sec, wd, yd, isdst) 434 else: 435 # Return current time if date string is invalid 436 self.dateFlag = 0 437 self.timeFlag = 0 438 sourceTime = time.localtime() 439 440 return sourceTime
441 442
443 - def evalRanges(self, datetimeString, sourceTime=None):
444 """ 445 Evaluate the C{datetimeString} text and determine if 446 it represents a date or time range. 447 448 @type datetimeString: string 449 @param datetimeString: datetime text to evaluate 450 @type sourceTime: struct_time 451 @param sourceTime: C{struct_time} value to use as the base 452 453 @rtype: tuple 454 @return: tuple of: start datetime, end datetime and the invalid flag 455 """ 456 startTime = '' 457 endTime = '' 458 startDate = '' 459 endDate = '' 460 rangeFlag = 0 461 462 s = datetimeString.strip().lower() 463 464 if self.ptc.rangeSep in s: 465 s = s.replace(self.ptc.rangeSep, ' %s ' % self.ptc.rangeSep) 466 s = s.replace(' ', ' ') 467 468 m = self.ptc.CRE_TIMERNG1.search(s) 469 if m is not None: 470 rangeFlag = 1 471 else: 472 m = self.ptc.CRE_TIMERNG2.search(s) 473 if m is not None: 474 rangeFlag = 2 475 else: 476 m = self.ptc.CRE_TIMERNG4.search(s) 477 if m is not None: 478 rangeFlag = 7 479 else: 480 m = self.ptc.CRE_TIMERNG3.search(s) 481 if m is not None: 482 rangeFlag = 3 483 else: 484 m = self.ptc.CRE_DATERNG1.search(s) 485 if m is not None: 486 rangeFlag = 4 487 else: 488 m = self.ptc.CRE_DATERNG2.search(s) 489 if m is not None: 490 rangeFlag = 5 491 else: 492 m = self.ptc.CRE_DATERNG3.search(s) 493 if m is not None: 494 rangeFlag = 6 495 496 if _debug: 497 print 'evalRanges: rangeFlag =', rangeFlag, '[%s]' % s 498 499 if m is not None: 500 if (m.group() != s): 501 # capture remaining string 502 parseStr = m.group() 503 chunk1 = s[:m.start()] 504 chunk2 = s[m.end():] 505 s = '%s %s' % (chunk1, chunk2) 506 flag = 1 507 508 sourceTime, flag = self.parse(s, sourceTime) 509 510 if flag == 0: 511 sourceTime = None 512 else: 513 parseStr = s 514 515 if rangeFlag == 1: 516 m = re.search(self.ptc.rangeSep, parseStr) 517 startTime, sflag = self.parse((parseStr[:m.start()]), sourceTime) 518 endTime, eflag = self.parse((parseStr[(m.start() + 1):]), sourceTime) 519 520 if (eflag != 0) and (sflag != 0): 521 return (startTime, endTime, 2) 522 523 elif rangeFlag == 2: 524 m = re.search(self.ptc.rangeSep, parseStr) 525 startTime, sflag = self.parse((parseStr[:m.start()]), sourceTime) 526 endTime, eflag = self.parse((parseStr[(m.start() + 1):]), sourceTime) 527 528 if (eflag != 0) and (sflag != 0): 529 return (startTime, endTime, 2) 530 531 elif rangeFlag == 3 or rangeFlag == 7: 532 m = re.search(self.ptc.rangeSep, parseStr) 533 # capturing the meridian from the end time 534 if self.ptc.usesMeridian: 535 ampm = re.search(self.ptc.am[0], parseStr) 536 537 # appending the meridian to the start time 538 if ampm is not None: 539 startTime, sflag = self.parse((parseStr[:m.start()] + self.ptc.meridian[0]), sourceTime) 540 else: 541 startTime, sflag = self.parse((parseStr[:m.start()] + self.ptc.meridian[1]), sourceTime) 542 else: 543 startTime, sflag = self.parse((parseStr[:m.start()]), sourceTime) 544 545 endTime, eflag = self.parse(parseStr[(m.start() + 1):], sourceTime) 546 547 if (eflag != 0) and (sflag != 0): 548 return (startTime, endTime, 2) 549 550 elif rangeFlag == 4: 551 m = re.search(self.ptc.rangeSep, parseStr) 552 startDate, sflag = self.parse((parseStr[:m.start()]), sourceTime) 553 endDate, eflag = self.parse((parseStr[(m.start() + 1):]), sourceTime) 554 555 if (eflag != 0) and (sflag != 0): 556 return (startDate, endDate, 1) 557 558 elif rangeFlag == 5: 559 m = re.search(self.ptc.rangeSep, parseStr) 560 endDate = parseStr[(m.start() + 1):] 561 562 # capturing the year from the end date 563 date = self.ptc.CRE_DATE3.search(endDate) 564 endYear = date.group('year') 565 566 # appending the year to the start date if the start date 567 # does not have year information and the end date does. 568 # eg : "Aug 21 - Sep 4, 2007" 569 if endYear is not None: 570 startDate = (parseStr[:m.start()]).strip() 571 date = self.ptc.CRE_DATE3.search(startDate) 572 startYear = date.group('year') 573 574 if startYear is None: 575 startDate = startDate + ', ' + endYear 576 else: 577 startDate = parseStr[:m.start()] 578 579 startDate, sflag = self.parse(startDate, sourceTime) 580 endDate, eflag = self.parse(endDate, sourceTime) 581 582 if (eflag != 0) and (sflag != 0): 583 return (startDate, endDate, 1) 584 585 elif rangeFlag == 6: 586 m = re.search(self.ptc.rangeSep, parseStr) 587 588 startDate = parseStr[:m.start()] 589 590 # capturing the month from the start date 591 mth = self.ptc.CRE_DATE3.search(startDate) 592 mth = mth.group('mthname') 593 594 # appending the month name to the end date 595 endDate = mth + parseStr[(m.start() + 1):] 596 597 startDate, sflag = self.parse(startDate, sourceTime) 598 endDate, eflag = self.parse(endDate, sourceTime) 599 600 if (eflag != 0) and (sflag != 0): 601 return (startDate, endDate, 1) 602 else: 603 # if range is not found 604 sourceTime = time.localtime() 605 606 return (sourceTime, sourceTime, 0)
607 608
609 - def _CalculateDOWDelta(self, wd, wkdy, offset, style, currentDayStyle):
610 """ 611 Based on the C{style} and C{currentDayStyle} determine what 612 day-of-week value is to be returned. 613 614 @type wd: integer 615 @param wd: day-of-week value for the current day 616 @type wkdy: integer 617 @param wkdy: day-of-week value for the parsed day 618 @type offset: integer 619 @param offset: offset direction for any modifiers (-1, 0, 1) 620 @type style: integer 621 @param style: normally the value set in C{Constants.DOWParseStyle} 622 @type currentDayStyle: integer 623 @param currentDayStyle: normally the value set in C{Constants.CurrentDOWParseStyle} 624 625 @rtype: integer 626 @return: calculated day-of-week 627 """ 628 if offset == 1: 629 # modifier is indicating future week eg: "next". 630 # DOW is calculated as DOW of next week 631 diff = 7 - wd + wkdy 632 633 elif offset == -1: 634 # modifier is indicating past week eg: "last","previous" 635 # DOW is calculated as DOW of previous week 636 diff = wkdy - wd - 7 637 638 elif offset == 0: 639 # modifier is indiacting current week eg: "this" 640 # DOW is calculated as DOW of this week 641 diff = wkdy - wd 642 643 elif offset == 2: 644 # no modifier is present. 645 # i.e. string to be parsed is just DOW 646 if style == 1: 647 # next occurance of the DOW is calculated 648 if currentDayStyle == True: 649 if wkdy >= wd: 650 diff = wkdy - wd 651 else: 652 diff = 7 - wd + wkdy 653 else: 654 if wkdy > wd: 655 diff = wkdy - wd 656 else: 657 diff = 7 - wd + wkdy 658 659 elif style == -1: 660 # last occurance of the DOW is calculated 661 if currentDayStyle == True: 662 if wkdy <= wd: 663 diff = wkdy - wd 664 else: 665 diff = wkdy - wd - 7 666 else: 667 if wkdy < wd: 668 diff = wkdy - wd 669 else: 670 diff = wkdy - wd - 7 671 else: 672 # occurance of the DOW in the current week is calculated 673 diff = wkdy - wd 674 675 if _debug: 676 print "wd %s, wkdy %s, offset %d, style %d\n" % (wd, wkdy, offset, style) 677 678 return diff
679 680
681 - def _evalModifier(self, modifier, chunk1, chunk2, sourceTime):
682 """ 683 Evaluate the C{modifier} string and following text (passed in 684 as C{chunk1} and C{chunk2}) and if they match any known modifiers 685 calculate the delta and apply it to C{sourceTime}. 686 687 @type modifier: string 688 @param modifier: modifier text to apply to sourceTime 689 @type chunk1: string 690 @param chunk1: first text chunk that followed modifier (if any) 691 @type chunk2: string 692 @param chunk2: second text chunk that followed modifier (if any) 693 @type sourceTime: struct_time 694 @param sourceTime: C{struct_time} value to use as the base 695 696 @rtype: tuple 697 @return: tuple of: remaining text and the modified sourceTime 698 """ 699 offset = self.ptc.Modifiers[modifier] 700 701 if sourceTime is not None: 702 (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = sourceTime 703 else: 704 (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = time.localtime() 705 706 # capture the units after the modifier and the remaining 707 # string after the unit 708 m = self.ptc.CRE_REMAINING.search(chunk2) 709 if m is not None: 710 index = m.start() + 1 711 unit = chunk2[:m.start()] 712 chunk2 = chunk2[index:] 713 else: 714 unit = chunk2 715 chunk2 = '' 716 717 flag = False 718 719 if unit == 'month' or \ 720 unit == 'mth' or \ 721 unit == 'm': 722 if offset == 0: 723 dy = self.ptc.daysInMonth(mth, yr) 724 sourceTime = (yr, mth, dy, 9, 0, 0, wd, yd, isdst) 725 elif offset == 2: 726 # if day is the last day of the month, calculate the last day 727 # of the next month 728 if dy == self.ptc.daysInMonth(mth, yr): 729 dy = self.ptc.daysInMonth(mth + 1, yr) 730 731 start = datetime.datetime(yr, mth, dy, 9, 0, 0) 732 target = self.inc(start, month=1) 733 sourceTime = target.timetuple() 734 else: 735 start = datetime.datetime(yr, mth, 1, 9, 0, 0) 736 target = self.inc(start, month=offset) 737 sourceTime = target.timetuple() 738 739 flag = True 740 self.dateFlag = 1 741 742 if unit == 'week' or \ 743 unit == 'wk' or \ 744 unit == 'w': 745 if offset == 0: 746 start = datetime.datetime(yr, mth, dy, 17, 0, 0) 747 target = start + datetime.timedelta(days=(4 - wd)) 748 sourceTime = target.timetuple() 749 elif offset == 2: 750 start = datetime.datetime(yr, mth, dy, 9, 0, 0) 751 target = start + datetime.timedelta(days=7) 752 sourceTime = target.timetuple() 753 else: 754 return self._evalModifier(modifier, chunk1, "monday " + chunk2, sourceTime) 755 756 flag = True 757 self.dateFlag = 1 758 759 if unit == 'day' or \ 760 unit == 'dy' or \ 761 unit == 'd': 762 if offset == 0: 763 sourceTime = (yr, mth, dy, 17, 0, 0, wd, yd, isdst) 764 self.timeFlag = 2 765 elif offset == 2: 766 start = datetime.datetime(yr, mth, dy, hr, mn, sec) 767 target = start + datetime.timedelta(days=1) 768 sourceTime = target.timetuple() 769 else: 770 start = datetime.datetime(yr, mth, dy, 9, 0, 0) 771 target = start + datetime.timedelta(days=offset) 772 sourceTime = target.timetuple() 773 774 flag = True 775 self.dateFlag = 1 776 777 if unit == 'hour' or \ 778 unit == 'hr': 779 if offset == 0: 780 sourceTime = (yr, mth, dy, hr, 0, 0, wd, yd, isdst) 781 else: 782 start = datetime.datetime(yr, mth, dy, hr, 0, 0) 783 target = start + datetime.timedelta(hours=offset) 784 sourceTime = target.timetuple() 785 786 flag = True 787 self.timeFlag = 2 788 789 if unit == 'year' or \ 790 unit == 'yr' or \ 791 unit == 'y': 792 if offset == 0: 793 sourceTime = (yr, 12, 31, hr, mn, sec, wd, yd, isdst) 794 elif offset == 2: 795 sourceTime = (yr + 1, mth, dy, hr, mn, sec, wd, yd, isdst) 796 else: 797 sourceTime = (yr + offset, 1, 1, 9, 0, 0, wd, yd, isdst) 798 799 flag = True 800 self.dateFlag = 1 801 802 if flag == False: 803 m = self.ptc.CRE_WEEKDAY.match(unit) 804 if m is not None: 805 wkdy = m.group() 806 self.dateFlag = 1 807 808 if modifier == 'eod': 809 # Calculate the upcoming weekday 810 self.modifierFlag = False 811 (sourceTime, _) = self.parse(wkdy, sourceTime) 812 sources = self.ptc.buildSources(sourceTime) 813 self.timeFlag = 2 814 815 if modifier in sources: 816 sourceTime = sources[modifier] 817 818 else: 819 wkdy = self.ptc.WeekdayOffsets[wkdy] 820 diff = self._CalculateDOWDelta(wd, wkdy, offset, 821 self.ptc.DOWParseStyle, 822 self.ptc.CurrentDOWParseStyle) 823 start = datetime.datetime(yr, mth, dy, 9, 0, 0) 824 target = start + datetime.timedelta(days=diff) 825 sourceTime = target.timetuple() 826 827 flag = True 828 self.dateFlag = 1 829 830 if not flag: 831 m = self.ptc.CRE_TIME.match(unit) 832 if m is not None: 833 self.modifierFlag = False 834 (yr, mth, dy, hr, mn, sec, wd, yd, isdst), _ = self.parse(unit) 835 836 start = datetime.datetime(yr, mth, dy, hr, mn, sec) 837 target = start + datetime.timedelta(days=offset) 838 sourceTime = target.timetuple() 839 flag = True 840 else: 841 self.modifierFlag = False 842 843 # check if the remaining text is parsable and if so, 844 # use it as the base time for the modifier source time 845 t, flag2 = self.parse('%s %s' % (chunk1, unit), sourceTime) 846 847 if flag2 != 0: 848 sourceTime = t 849 850 sources = self.ptc.buildSources(sourceTime) 851 852 if modifier in sources: 853 sourceTime = sources[modifier] 854 flag = True 855 self.timeFlag = 2 856 857 # if the word after next is a number, the string is more than likely 858 # to be "next 4 hrs" which we will have to combine the units with the 859 # rest of the string 860 if not flag: 861 if offset < 0: 862 # if offset is negative, the unit has to be made negative 863 unit = '-%s' % unit 864 865 chunk2 = '%s %s' % (unit, chunk2) 866 867 self.modifierFlag = False 868 869 #return '%s %s' % (chunk1, chunk2), sourceTime 870 return '%s' % chunk2, sourceTime
871
872 - def _evalModifier2(self, modifier, chunk1 , chunk2, sourceTime):
873 """ 874 Evaluate the C{modifier} string and following text (passed in 875 as C{chunk1} and C{chunk2}) and if they match any known modifiers 876 calculate the delta and apply it to C{sourceTime}. 877 878 @type modifier: string 879 @param modifier: modifier text to apply to C{sourceTime} 880 @type chunk1: string 881 @param chunk1: first text chunk that followed modifier (if any) 882 @type chunk2: string 883 @param chunk2: second text chunk that followed modifier (if any) 884 @type sourceTime: struct_time 885 @param sourceTime: C{struct_time} value to use as the base 886 887 @rtype: tuple 888 @return: tuple of: remaining text and the modified sourceTime 889 """ 890 offset = self.ptc.Modifiers[modifier] 891 digit = r'\d+' 892 893 self.modifier2Flag = False 894 895 # If the string after the negative modifier starts with digits, 896 # then it is likely that the string is similar to ' before 3 days' 897 # or 'evening prior to 3 days'. 898 # In this case, the total time is calculated by subtracting '3 days' 899 # from the current date. 900 # So, we have to identify the quantity and negate it before parsing 901 # the string. 902 # This is not required for strings not starting with digits since the 903 # string is enough to calculate the sourceTime 904 if chunk2 != '': 905 if offset < 0: 906 m = re.match(digit, chunk2.strip()) 907 if m is not None: 908 qty = int(m.group()) * -1 909 chunk2 = chunk2[m.end():] 910 chunk2 = '%d%s' % (qty, chunk2) 911 912 sourceTime, flag1 = self.parse(chunk2, sourceTime) 913 if flag1 == 0: 914 flag1 = True 915 else: 916 flag1 = False 917 flag2 = False 918 else: 919 flag1 = False 920 921 if chunk1 != '': 922 if offset < 0: 923 m = re.search(digit, chunk1.strip()) 924 if m is not None: 925 qty = int(m.group()) * -1 926 chunk1 = chunk1[m.end():] 927 chunk1 = '%d%s' % (qty, chunk1) 928 929 tempDateFlag = self.dateFlag 930 tempTimeFlag = self.timeFlag 931 sourceTime2, flag2 = self.parse(chunk1, sourceTime) 932 else: 933 return sourceTime, (flag1 and flag2) 934 935 # if chunk1 is not a datetime and chunk2 is then do not use datetime 936 # value returned by parsing chunk1 937 if not (flag1 == False and flag2 == 0): 938 sourceTime = sourceTime2 939 else: 940 self.timeFlag = tempTimeFlag 941 self.dateFlag = tempDateFlag 942 943 return sourceTime, (flag1 and flag2)
944 945
946 - def _evalString(self, datetimeString, sourceTime=None):
947 """ 948 Calculate the datetime based on flags set by the L{parse()} routine 949 950 Examples handled:: 951 RFC822, W3CDTF formatted dates 952 HH:MM[:SS][ am/pm] 953 MM/DD/YYYY 954 DD MMMM YYYY 955 956 @type datetimeString: string 957 @param datetimeString: text to try and parse as more "traditional" 958 date/time text 959 @type sourceTime: struct_time 960 @param sourceTime: C{struct_time} value to use as the base 961 962 @rtype: datetime 963 @return: calculated C{struct_time} value or current C{struct_time} 964 if not parsed 965 """ 966 s = datetimeString.strip() 967 now = time.localtime() 968 969 # Given string date is a RFC822 date 970 if sourceTime is None: 971 sourceTime = _parse_date_rfc822(s) 972 973 if sourceTime is not None: 974 (yr, mth, dy, hr, mn, sec, wd, yd, isdst, _) = sourceTime 975 self.dateFlag = 1 976 977 if (hr != 0) and (mn != 0) and (sec != 0): 978 self.timeFlag = 2 979 980 sourceTime = (yr, mth, dy, hr, mn, sec, wd, yd, isdst) 981 982 # Given string date is a W3CDTF date 983 if sourceTime is None: 984 sourceTime = _parse_date_w3dtf(s) 985 986 if sourceTime is not None: 987 self.dateFlag = 1 988 self.timeFlag = 2 989 990 if sourceTime is None: 991 s = s.lower() 992 993 # Given string is in the format HH:MM(:SS)(am/pm) 994 if self.meridianFlag: 995 if sourceTime is None: 996 (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = now 997 else: 998 (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = sourceTime 999 1000 m = self.ptc.CRE_TIMEHMS2.search(s) 1001 if m is not None: 1002 dt = s[:m.start('meridian')].strip() 1003 if len(dt) <= 2: 1004 hr = int(dt) 1005 mn = 0 1006 sec = 0 1007 else: 1008 hr, mn, sec = _extract_time(m) 1009 1010 if hr == 24: 1011 hr = 0 1012 1013 sourceTime = (yr, mth, dy, hr, mn, sec, wd, yd, isdst) 1014 meridian = m.group('meridian').lower() 1015 1016 # if 'am' found and hour is 12 - force hour to 0 (midnight) 1017 if (meridian in self.ptc.am) and hr == 12: 1018 sourceTime = (yr, mth, dy, 0, mn, sec, wd, yd, isdst) 1019 1020 # if 'pm' found and hour < 12, add 12 to shift to evening 1021 if (meridian in self.ptc.pm) and hr < 12: 1022 sourceTime = (yr, mth, dy, hr + 12, mn, sec, wd, yd, isdst) 1023 1024 # invalid time 1025 if hr > 24 or mn > 59 or sec > 59: 1026 sourceTime = now 1027 self.dateFlag = 0 1028 self.timeFlag = 0 1029 1030 self.meridianFlag = False 1031 1032 # Given string is in the format HH:MM(:SS) 1033 if self.timeStdFlag: 1034 if sourceTime is None: 1035 (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = now 1036 else: 1037 (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = sourceTime 1038 1039 m = self.ptc.CRE_TIMEHMS.search(s) 1040 if m is not None: 1041 hr, mn, sec = _extract_time(m) 1042 if hr == 24: 1043 hr = 0 1044 1045 if hr > 24 or mn > 59 or sec > 59: 1046 # invalid time 1047 sourceTime = now 1048 self.dateFlag = 0 1049 self.timeFlag = 0 1050 else: 1051 sourceTime = (yr, mth, dy, hr, mn, sec, wd, yd, isdst) 1052 1053 self.timeStdFlag = False 1054 1055 # Given string is in the format 07/21/2006 1056 if self.dateStdFlag: 1057 sourceTime = self.parseDate(s) 1058 self.dateStdFlag = False 1059 1060 # Given string is in the format "May 23rd, 2005" 1061 if self.dateStrFlag: 1062 sourceTime = self.parseDateText(s) 1063 self.dateStrFlag = False 1064 1065 # Given string is a weekday 1066 if self.weekdyFlag: 1067 (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = now 1068 1069 start = datetime.datetime(yr, mth, dy, hr, mn, sec) 1070 wkdy = self.ptc.WeekdayOffsets[s] 1071 1072 if wkdy > wd: 1073 qty = self._CalculateDOWDelta(wd, wkdy, 2, 1074 self.ptc.DOWParseStyle, 1075 self.ptc.CurrentDOWParseStyle) 1076 else: 1077 qty = self._CalculateDOWDelta(wd, wkdy, 2, 1078 self.ptc.DOWParseStyle, 1079 self.ptc.CurrentDOWParseStyle) 1080 1081 target = start + datetime.timedelta(days=qty) 1082 wd = wkdy 1083 1084 sourceTime = target.timetuple() 1085 self.weekdyFlag = False 1086 1087 # Given string is a natural language time string like 1088 # lunch, midnight, etc 1089 if self.timeStrFlag: 1090 if s in self.ptc.re_values['now']: 1091 sourceTime = now 1092 else: 1093 sources = self.ptc.buildSources(sourceTime) 1094 1095 if s in sources: 1096 sourceTime = sources[s] 1097 else: 1098 sourceTime = now 1099 self.dateFlag = 0 1100 self.timeFlag = 0 1101 1102 self.timeStrFlag = False 1103 1104 # Given string is a natural language date string like today, tomorrow.. 1105 if self.dayStrFlag: 1106 if sourceTime is None: 1107 sourceTime = now 1108 1109 (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = sourceTime 1110 1111 if s in self.ptc.dayOffsets: 1112 offset = self.ptc.dayOffsets[s] 1113 else: 1114 offset = 0 1115 1116 start = datetime.datetime(yr, mth, dy, 9, 0, 0) 1117 target = start + datetime.timedelta(days=offset) 1118 sourceTime = target.timetuple() 1119 1120 self.dayStrFlag = False 1121 1122 # Given string is a time string with units like "5 hrs 30 min" 1123 if self.unitsFlag: 1124 modifier = '' # TODO 1125 1126 if sourceTime is None: 1127 sourceTime = now 1128 1129 m = self.ptc.CRE_UNITS.search(s) 1130 if m is not None: 1131 units = m.group('units') 1132 quantity = s[:m.start('units')] 1133 1134 sourceTime = self._buildTime(sourceTime, quantity, modifier, units) 1135 self.unitsFlag = False 1136 1137 # Given string is a time string with single char units like "5 h 30 m" 1138 if self.qunitsFlag: 1139 modifier = '' # TODO 1140 1141 if sourceTime is None: 1142 sourceTime = now 1143 1144 m = self.ptc.CRE_QUNITS.search(s) 1145 if m is not None: 1146 units = m.group('qunits') 1147 quantity = s[:m.start('qunits')] 1148 1149 sourceTime = self._buildTime(sourceTime, quantity, modifier, units) 1150 self.qunitsFlag = False 1151 1152 # Given string does not match anything 1153 if sourceTime is None: 1154 sourceTime = now 1155 self.dateFlag = 0 1156 self.timeFlag = 0 1157 1158 return sourceTime
1159 1160
1161 - def parse(self, datetimeString, sourceTime=None):
1162 """ 1163 Splits the given C{datetimeString} into tokens, finds the regex 1164 patterns that match and then calculates a C{struct_time} value from 1165 the chunks. 1166 1167 If C{sourceTime} is given then the C{struct_time} value will be 1168 calculated from that value, otherwise from the current date/time. 1169 1170 If the C{datetimeString} is parsed and date/time value found then 1171 the second item of the returned tuple will be a flag to let you know 1172 what kind of C{struct_time} value is being returned:: 1173 1174 0 = not parsed at all 1175 1 = parsed as a C{date} 1176 2 = parsed as a C{time} 1177 3 = parsed as a C{datetime} 1178 1179 @type datetimeString: string 1180 @param datetimeString: date/time text to evaluate 1181 @type sourceTime: struct_time 1182 @param sourceTime: C{struct_time} value to use as the base 1183 1184 @rtype: tuple 1185 @return: tuple of: modified C{sourceTime} and the result flag 1186 """ 1187 1188 if sourceTime: 1189 if isinstance(sourceTime, datetime.datetime): 1190 if _debug: 1191 print 'coercing datetime to timetuple' 1192 sourceTime = sourceTime.timetuple() 1193 else: 1194 if not isinstance(sourceTime, time.struct_time) and \ 1195 not isinstance(sourceTime, tuple): 1196 raise Exception('sourceTime is not a struct_time') 1197 1198 s = datetimeString.strip().lower() 1199 parseStr = '' 1200 totalTime = sourceTime 1201 1202 if s == '' : 1203 if sourceTime is not None: 1204 return (sourceTime, self.dateFlag + self.timeFlag) 1205 else: 1206 return (time.localtime(), 0) 1207 1208 self.timeFlag = 0 1209 self.dateFlag = 0 1210 1211 while len(s) > 0: 1212 flag = False 1213 chunk1 = '' 1214 chunk2 = '' 1215 1216 if _debug: 1217 print 'parse (top of loop): [%s][%s]' % (s, parseStr) 1218 1219 if parseStr == '': 1220 # Modifier like next\prev.. 1221 m = self.ptc.CRE_MODIFIER.search(s) 1222 if m is not None: 1223 self.modifierFlag = True 1224 if (m.group('modifier') != s): 1225 # capture remaining string 1226 parseStr = m.group('modifier') 1227 chunk1 = s[:m.start('modifier')].strip() 1228 chunk2 = s[m.end('modifier'):].strip() 1229 flag = True 1230 else: 1231 parseStr = s 1232 1233 if parseStr == '': 1234 # Modifier like from\after\prior.. 1235 m = self.ptc.CRE_MODIFIER2.search(s) 1236 if m is not None: 1237 self.modifier2Flag = True 1238 if (m.group('modifier') != s): 1239 # capture remaining string 1240 parseStr = m.group('modifier') 1241 chunk1 = s[:m.start('modifier')].strip() 1242 chunk2 = s[m.end('modifier'):].strip() 1243 flag = True 1244 else: 1245 parseStr = s 1246 1247 if parseStr == '': 1248 # String date format 1249 m = self.ptc.CRE_DATE3.search(s) 1250 if m is not None: 1251 self.dateStrFlag = True 1252 self.dateFlag = 1 1253 if (m.group('date') != s): 1254 # capture remaining string 1255 parseStr = m.group('date') 1256 chunk1 = s[:m.start('date')] 1257 chunk2 = s[m.end('date'):] 1258 s = '%s %s' % (chunk1, chunk2) 1259 flag = True 1260 else: 1261 parseStr = s 1262 1263 if parseStr == '': 1264 # Standard date format 1265 m = self.ptc.CRE_DATE.search(s) 1266 if m is not None: 1267 self.dateStdFlag = True 1268 self.dateFlag = 1 1269 if (m.group('date') != s): 1270 # capture remaining string 1271 parseStr = m.group('date') 1272 chunk1 = s[:m.start('date')] 1273 chunk2 = s[m.end('date'):] 1274 s = '%s %s' % (chunk1, chunk2) 1275 flag = True 1276 else: 1277 parseStr = s 1278 1279 if parseStr == '': 1280 # Natural language day strings 1281 m = self.ptc.CRE_DAY.search(s) 1282 if m is not None: 1283 self.dayStrFlag = True 1284 self.dateFlag = 1 1285 if (m.group('day') != s): 1286 # capture remaining string 1287 parseStr = m.group('day') 1288 chunk1 = s[:m.start('day')] 1289 chunk2 = s[m.end('day'):] 1290 s = '%s %s' % (chunk1, chunk2) 1291 flag = True 1292 else: 1293 parseStr = s 1294 1295 if parseStr == '': 1296 # Quantity + Units 1297 m = self.ptc.CRE_UNITS.search(s) 1298 if m is not None: 1299 self.unitsFlag = True 1300 if (m.group('qty') != s): 1301 # capture remaining string 1302 parseStr = m.group('qty') 1303 chunk1 = s[:m.start('qty')].strip() 1304 chunk2 = s[m.end('qty'):].strip() 1305 1306 if chunk1[-1:] == '-': 1307 parseStr = '-%s' % parseStr 1308 chunk1 = chunk1[:-1] 1309 1310 s = '%s %s' % (chunk1, chunk2) 1311 flag = True 1312 else: 1313 parseStr = s 1314 1315 if parseStr == '': 1316 # Quantity + Units 1317 m = self.ptc.CRE_QUNITS.search(s) 1318 if m is not None: 1319 self.qunitsFlag = True 1320 1321 if (m.group('qty') != s): 1322 # capture remaining string 1323 parseStr = m.group('qty') 1324 chunk1 = s[:m.start('qty')].strip() 1325 chunk2 = s[m.end('qty'):].strip() 1326 1327 if chunk1[-1:] == '-': 1328 parseStr = '-%s' % parseStr 1329 chunk1 = chunk1[:-1] 1330 1331 s = '%s %s' % (chunk1, chunk2) 1332 flag = True 1333 else: 1334 parseStr = s 1335 1336 if parseStr == '': 1337 # Weekday 1338 m = self.ptc.CRE_WEEKDAY.search(s) 1339 if m is not None: 1340 gv = m.group('weekday') 1341 if s not in self.ptc.dayOffsets: 1342 self.weekdyFlag = True 1343 self.dateFlag = 1 1344 if (gv != s): 1345 # capture remaining string 1346 parseStr = gv 1347 chunk1 = s[:m.start('weekday')] 1348 chunk2 = s[m.end('weekday'):] 1349 s = '%s %s' % (chunk1, chunk2) 1350 flag = True 1351 else: 1352 parseStr = s 1353 1354 if parseStr == '': 1355 # Natural language time strings 1356 m = self.ptc.CRE_TIME.search(s) 1357 if m is not None: 1358 self.timeStrFlag = True 1359 self.timeFlag = 2 1360 if (m.group('time') != s): 1361 # capture remaining string 1362 parseStr = m.group('time') 1363 chunk1 = s[:m.start('time')] 1364 chunk2 = s[m.end('time'):] 1365 s = '%s %s' % (chunk1, chunk2) 1366 flag = True 1367 else: 1368 parseStr = s 1369 1370 if parseStr == '': 1371 # HH:MM(:SS) am/pm time strings 1372 m = self.ptc.CRE_TIMEHMS2.search(s) 1373 if m is not None: 1374 self.meridianFlag = True 1375 self.timeFlag = 2 1376 if m.group('minutes') is not None: 1377 if m.group('seconds') is not None: 1378 parseStr = '%s:%s:%s %s' % (m.group('hours'), 1379 m.group('minutes'), 1380 m.group('seconds'), 1381 m.group('meridian')) 1382 else: 1383 parseStr = '%s:%s %s' % (m.group('hours'), 1384 m.group('minutes'), 1385 m.group('meridian')) 1386 else: 1387 parseStr = '%s %s' % (m.group('hours'), 1388 m.group('meridian')) 1389 1390 chunk1 = s[:m.start('hours')] 1391 chunk2 = s[m.end('meridian'):] 1392 1393 s = '%s %s' % (chunk1, chunk2) 1394 flag = True 1395 1396 if parseStr == '': 1397 # HH:MM(:SS) time strings 1398 m = self.ptc.CRE_TIMEHMS.search(s) 1399 if m is not None: 1400 self.timeStdFlag = True 1401 self.timeFlag = 2 1402 if m.group('seconds') is not None: 1403 parseStr = '%s:%s:%s' % (m.group('hours'), 1404 m.group('minutes'), 1405 m.group('seconds')) 1406 chunk1 = s[:m.start('hours')] 1407 chunk2 = s[m.end('seconds'):] 1408 else: 1409 parseStr = '%s:%s' % (m.group('hours'), 1410 m.group('minutes')) 1411 chunk1 = s[:m.start('hours')] 1412 chunk2 = s[m.end('minutes'):] 1413 1414 s = '%s %s' % (chunk1, chunk2) 1415 flag = True 1416 1417 # if string does not match any regex, empty string to 1418 # come out of the while loop 1419 if not flag: 1420 s = '' 1421 1422 if _debug: 1423 print 'parse (bottom) [%s][%s][%s][%s]' % (s, parseStr, chunk1, chunk2) 1424 print 'weekday %s, dateStd %s, dateStr %s, time %s, timeStr %s, meridian %s' % \ 1425 (self.weekdyFlag, self.dateStdFlag, self.dateStrFlag, self.timeStdFlag, self.timeStrFlag, self.meridianFlag) 1426 print 'dayStr %s, modifier %s, modifier2 %s, units %s, qunits %s' % \ 1427 (self.dayStrFlag, self.modifierFlag, self.modifier2Flag, self.unitsFlag, self.qunitsFlag) 1428 1429 # evaluate the matched string 1430 if parseStr != '': 1431 if self.modifierFlag == True: 1432 t, totalTime = self._evalModifier(parseStr, chunk1, chunk2, totalTime) 1433 # t is the unparsed part of the chunks. 1434 # If it is not date/time, return current 1435 # totalTime as it is; else return the output 1436 # after parsing t. 1437 if (t != '') and (t != None): 1438 tempDateFlag = self.dateFlag 1439 tempTimeFlag = self.timeFlag 1440 (totalTime2, flag) = self.parse(t, totalTime) 1441 1442 if flag == 0 and totalTime is not None: 1443 self.timeFlag = tempTimeFlag 1444 self.dateFlag = tempDateFlag 1445 1446 return (totalTime, self.dateFlag + self.timeFlag) 1447 else: 1448 return (totalTime2, self.dateFlag + self.timeFlag) 1449 1450 elif self.modifier2Flag == True: 1451 totalTime, invalidFlag = self._evalModifier2(parseStr, chunk1, chunk2, totalTime) 1452 1453 if invalidFlag == True: 1454 self.dateFlag = 0 1455 self.timeFlag = 0 1456 1457 else: 1458 totalTime = self._evalString(parseStr, totalTime) 1459 parseStr = '' 1460 1461 # String is not parsed at all 1462 if totalTime is None or totalTime == sourceTime: 1463 totalTime = time.localtime() 1464 self.dateFlag = 0 1465 self.timeFlag = 0 1466 1467 return (totalTime, self.dateFlag + self.timeFlag)
1468 1469
1470 - def inc(self, source, month=None, year=None):
1471 """ 1472 Takes the given C{source} date, or current date if none is 1473 passed, and increments it according to the values passed in 1474 by month and/or year. 1475 1476 This routine is needed because Python's C{timedelta()} function 1477 does not allow for month or year increments. 1478 1479 @type source: struct_time 1480 @param source: C{struct_time} value to increment 1481 @type month: integer 1482 @param month: optional number of months to increment 1483 @type year: integer 1484 @param year: optional number of years to increment 1485 1486 @rtype: datetime 1487 @return: C{source} incremented by the number of months and/or years 1488 """ 1489 yr = source.year 1490 mth = source.month 1491 dy = source.day 1492 1493 if year: 1494 try: 1495 yi = int(year) 1496 except ValueError: 1497 yi = 0 1498 1499 yr += yi 1500 1501 if month: 1502 try: 1503 mi = int(month) 1504 except ValueError: 1505 mi = 0 1506 1507 m = abs(mi) 1508 y = m / 12 # how many years are in month increment 1509 m = m % 12 # get remaining months 1510 1511 if mi < 0: 1512 mth = mth - m # sub months from start month 1513 if mth < 1: # cross start-of-year? 1514 y -= 1 # yes - decrement year 1515 mth += 12 # and fix month 1516 else: 1517 mth = mth + m # add months to start month 1518 if mth > 12: # cross end-of-year? 1519 y += 1 # yes - increment year 1520 mth -= 12 # and fix month 1521 1522 yr += y 1523 1524 # if the day ends up past the last day of 1525 # the new month, set it to the last day 1526 if dy > self.ptc.daysInMonth(mth, yr): 1527 dy = self.ptc.daysInMonth(mth, yr) 1528 1529 d = source.replace(year=yr, month=mth, day=dy) 1530 1531 return source + (d - source)
1532