1"""Canonical ISO date format YYYY-MM-DDTHH:mm:SS+HH:mm 2 3This parser is _extremely_ strict, and the dates that match it, 4though really easy to work with for the computer, are not particularly 5readable. See the iso_date_loose module for a slightly relaxed 6definition which allows the "T" character to be replaced by a 7" " character, and allows a space before the timezone offset, as well 8as allowing the integer values to use non-0-padded integers. 9 10 11 ISO_date -- YYYY-MM-DD format, with a month and date optional 12 ISO_time -- HH:mm:SS format, with minutes and seconds optional 13 ISO_date_time -- YYYY-MM-DD HH:mm:SS+HH:mm format, 14 with time optional and TimeZone offset optional 15 16Interpreter: 17 MxInterpreter 18 Interprets the parse tree as mx.DateTime values 19 ISO_date and ISO_time 20 returns DateTime objects 21 Time only 22 returns RelativeDateTime object which, when 23 added to a DateTime gives you the given time 24 within that day 25""" 26try: 27 from mx import DateTime 28 haveMX = 1 29except ImportError: 30 haveMX = 0 31from simpleparse.parser import Parser 32from simpleparse import common, objectgenerator 33from simpleparse.common import chartypes, numbers 34from simpleparse.dispatchprocessor import * 35 36c = {} 37 38declaration =""" 39year := digit,digit,digit,digit 40month := digit,digit 41day := digit,digit 42 43hour := digit,digit 44minute := digit,digit 45second := digit,digit 46offset_sign := [-+] 47offset := offset_sign, hour, time_separator?, minute 48 49<date_separator> := '-' 50<time_separator> := ':' 51 52ISO_date := year, (date_separator, month, (date_separator, day)?)? 53ISO_time := hour, (time_separator, minute, (time_separator, second)?)? 54ISO_date_time := ISO_date, ([T], ISO_time)?, offset? 55""" 56 57 58 59 60_p = Parser( declaration ) 61for name in ["ISO_time","ISO_date", "ISO_date_time"]: 62 c[ name ] = objectgenerator.LibraryElement( 63 generator = _p._generator, 64 production = name, 65 ) 66common.share( c ) 67 68if haveMX: 69 class MxInterpreter(DispatchProcessor): 70 """Interpret a parsed ISO_date_time_loose in GMT/UTC time or localtime 71 """ 72 def __init__( 73 self, 74 inputLocal = 1, 75 returnLocal = 1, 76 ): 77 self.inputLocal = inputLocal 78 self.returnLocal = returnLocal 79 dateName = 'ISO_date' 80 timeName = 'ISO_time' 81 def ISO_date_time( self, info, buffer): 82 """Interpret the loose ISO date + time format""" 83 (tag, left, right, sublist) = info 84 set = singleMap( sublist, self, buffer ) 85 base, time, offset = ( 86 set.get(self.dateName), 87 set.get(self.timeName) or DateTime.RelativeDateTime(hour=0,minute=0,second=0), 88 set.get( "offset" ), 89 ) 90 base = base + time 91 offset = set.get( "offset" ) 92 if offset is not None: 93 # an explicit timezone was entered, convert to gmt and return as appropriate... 94 gmt = base - offset 95 if self.returnLocal: 96 return gmt.localtime() 97 else: 98 return gmt 99 # was in the default input locale (either gmt or local) 100 if self.inputLocal and self.returnLocal: 101 return base 102 elif not self.inputLocal and not self.returnLocal: 103 return base 104 elif self.inputLocal and not self.returnLocal: 105 # return gmt from local... 106 return base.gmtime() 107 else: 108 return base.localtime() 109 def ISO_date( self, info, buffer): 110 """Interpret the ISO date format""" 111 (tag, left, right, sublist) = info 112 set = {} 113 for item in sublist: 114 set[ item[0] ] = dispatch( self, item, buffer) 115 return DateTime.DateTime( 116 set.get("year") or now().year, 117 set.get("month") or 1, 118 set.get("day") or 1, 119 ) 120 def ISO_time( self, info, buffer): 121 """Interpret the ISO time format""" 122 (tag, left, right, sublist) = info 123 set = {} 124 for item in sublist: 125 set[ item[0] ] = dispatch( self, item, buffer) 126 return DateTime.RelativeDateTime( 127 hour = set.get("hour") or 0, 128 minute = set.get("minute") or 0, 129 second = set.get("second") or 0, 130 ) 131 132 integer = numbers.IntInterpreter() 133 second = offset_minute = offset_hour = year = month = day = hour =minute =integer 134 135 def offset( self, info, buffer): 136 """Calculate the time zone offset as a date-time delta""" 137 (tag, left, right, sublist) = info 138 set = singleMap( sublist, self, buffer ) 139 direction = set.get('offset_sign',1) 140 hour = set.get( "hour", 0) 141 minute = set.get( "minute", 0) 142 delta = DateTime.DateTimeDelta( 0, hour*direction, minute*direction) 143 return delta 144 145 def offset_sign( self , info, buffer): 146 """Interpret the offset sign as a multiplier""" 147 (tag, left, right, sublist) = info 148 v = buffer [left: right] 149 if v in ' +': 150 return 1 151 else: 152 return -1 153 154