1*cda5da8dSAndroid Build Coastguard Worker"""Strptime-related classes and functions. 2*cda5da8dSAndroid Build Coastguard Worker 3*cda5da8dSAndroid Build Coastguard WorkerCLASSES: 4*cda5da8dSAndroid Build Coastguard Worker LocaleTime -- Discovers and stores locale-specific time information 5*cda5da8dSAndroid Build Coastguard Worker TimeRE -- Creates regexes for pattern matching a string of text containing 6*cda5da8dSAndroid Build Coastguard Worker time information 7*cda5da8dSAndroid Build Coastguard Worker 8*cda5da8dSAndroid Build Coastguard WorkerFUNCTIONS: 9*cda5da8dSAndroid Build Coastguard Worker _getlang -- Figure out what language is being used for the locale 10*cda5da8dSAndroid Build Coastguard Worker strptime -- Calculates the time struct represented by the passed-in string 11*cda5da8dSAndroid Build Coastguard Worker 12*cda5da8dSAndroid Build Coastguard Worker""" 13*cda5da8dSAndroid Build Coastguard Workerimport time 14*cda5da8dSAndroid Build Coastguard Workerimport locale 15*cda5da8dSAndroid Build Coastguard Workerimport calendar 16*cda5da8dSAndroid Build Coastguard Workerfrom re import compile as re_compile 17*cda5da8dSAndroid Build Coastguard Workerfrom re import IGNORECASE 18*cda5da8dSAndroid Build Coastguard Workerfrom re import escape as re_escape 19*cda5da8dSAndroid Build Coastguard Workerfrom datetime import (date as datetime_date, 20*cda5da8dSAndroid Build Coastguard Worker timedelta as datetime_timedelta, 21*cda5da8dSAndroid Build Coastguard Worker timezone as datetime_timezone) 22*cda5da8dSAndroid Build Coastguard Workerfrom _thread import allocate_lock as _thread_allocate_lock 23*cda5da8dSAndroid Build Coastguard Worker 24*cda5da8dSAndroid Build Coastguard Worker__all__ = [] 25*cda5da8dSAndroid Build Coastguard Worker 26*cda5da8dSAndroid Build Coastguard Workerdef _getlang(): 27*cda5da8dSAndroid Build Coastguard Worker # Figure out what the current language is set to. 28*cda5da8dSAndroid Build Coastguard Worker return locale.getlocale(locale.LC_TIME) 29*cda5da8dSAndroid Build Coastguard Worker 30*cda5da8dSAndroid Build Coastguard Workerclass LocaleTime(object): 31*cda5da8dSAndroid Build Coastguard Worker """Stores and handles locale-specific information related to time. 32*cda5da8dSAndroid Build Coastguard Worker 33*cda5da8dSAndroid Build Coastguard Worker ATTRIBUTES: 34*cda5da8dSAndroid Build Coastguard Worker f_weekday -- full weekday names (7-item list) 35*cda5da8dSAndroid Build Coastguard Worker a_weekday -- abbreviated weekday names (7-item list) 36*cda5da8dSAndroid Build Coastguard Worker f_month -- full month names (13-item list; dummy value in [0], which 37*cda5da8dSAndroid Build Coastguard Worker is added by code) 38*cda5da8dSAndroid Build Coastguard Worker a_month -- abbreviated month names (13-item list, dummy value in 39*cda5da8dSAndroid Build Coastguard Worker [0], which is added by code) 40*cda5da8dSAndroid Build Coastguard Worker am_pm -- AM/PM representation (2-item list) 41*cda5da8dSAndroid Build Coastguard Worker LC_date_time -- format string for date/time representation (string) 42*cda5da8dSAndroid Build Coastguard Worker LC_date -- format string for date representation (string) 43*cda5da8dSAndroid Build Coastguard Worker LC_time -- format string for time representation (string) 44*cda5da8dSAndroid Build Coastguard Worker timezone -- daylight- and non-daylight-savings timezone representation 45*cda5da8dSAndroid Build Coastguard Worker (2-item list of sets) 46*cda5da8dSAndroid Build Coastguard Worker lang -- Language used by instance (2-item tuple) 47*cda5da8dSAndroid Build Coastguard Worker """ 48*cda5da8dSAndroid Build Coastguard Worker 49*cda5da8dSAndroid Build Coastguard Worker def __init__(self): 50*cda5da8dSAndroid Build Coastguard Worker """Set all attributes. 51*cda5da8dSAndroid Build Coastguard Worker 52*cda5da8dSAndroid Build Coastguard Worker Order of methods called matters for dependency reasons. 53*cda5da8dSAndroid Build Coastguard Worker 54*cda5da8dSAndroid Build Coastguard Worker The locale language is set at the offset and then checked again before 55*cda5da8dSAndroid Build Coastguard Worker exiting. This is to make sure that the attributes were not set with a 56*cda5da8dSAndroid Build Coastguard Worker mix of information from more than one locale. This would most likely 57*cda5da8dSAndroid Build Coastguard Worker happen when using threads where one thread calls a locale-dependent 58*cda5da8dSAndroid Build Coastguard Worker function while another thread changes the locale while the function in 59*cda5da8dSAndroid Build Coastguard Worker the other thread is still running. Proper coding would call for 60*cda5da8dSAndroid Build Coastguard Worker locks to prevent changing the locale while locale-dependent code is 61*cda5da8dSAndroid Build Coastguard Worker running. The check here is done in case someone does not think about 62*cda5da8dSAndroid Build Coastguard Worker doing this. 63*cda5da8dSAndroid Build Coastguard Worker 64*cda5da8dSAndroid Build Coastguard Worker Only other possible issue is if someone changed the timezone and did 65*cda5da8dSAndroid Build Coastguard Worker not call tz.tzset . That is an issue for the programmer, though, 66*cda5da8dSAndroid Build Coastguard Worker since changing the timezone is worthless without that call. 67*cda5da8dSAndroid Build Coastguard Worker 68*cda5da8dSAndroid Build Coastguard Worker """ 69*cda5da8dSAndroid Build Coastguard Worker self.lang = _getlang() 70*cda5da8dSAndroid Build Coastguard Worker self.__calc_weekday() 71*cda5da8dSAndroid Build Coastguard Worker self.__calc_month() 72*cda5da8dSAndroid Build Coastguard Worker self.__calc_am_pm() 73*cda5da8dSAndroid Build Coastguard Worker self.__calc_timezone() 74*cda5da8dSAndroid Build Coastguard Worker self.__calc_date_time() 75*cda5da8dSAndroid Build Coastguard Worker if _getlang() != self.lang: 76*cda5da8dSAndroid Build Coastguard Worker raise ValueError("locale changed during initialization") 77*cda5da8dSAndroid Build Coastguard Worker if time.tzname != self.tzname or time.daylight != self.daylight: 78*cda5da8dSAndroid Build Coastguard Worker raise ValueError("timezone changed during initialization") 79*cda5da8dSAndroid Build Coastguard Worker 80*cda5da8dSAndroid Build Coastguard Worker def __calc_weekday(self): 81*cda5da8dSAndroid Build Coastguard Worker # Set self.a_weekday and self.f_weekday using the calendar 82*cda5da8dSAndroid Build Coastguard Worker # module. 83*cda5da8dSAndroid Build Coastguard Worker a_weekday = [calendar.day_abbr[i].lower() for i in range(7)] 84*cda5da8dSAndroid Build Coastguard Worker f_weekday = [calendar.day_name[i].lower() for i in range(7)] 85*cda5da8dSAndroid Build Coastguard Worker self.a_weekday = a_weekday 86*cda5da8dSAndroid Build Coastguard Worker self.f_weekday = f_weekday 87*cda5da8dSAndroid Build Coastguard Worker 88*cda5da8dSAndroid Build Coastguard Worker def __calc_month(self): 89*cda5da8dSAndroid Build Coastguard Worker # Set self.f_month and self.a_month using the calendar module. 90*cda5da8dSAndroid Build Coastguard Worker a_month = [calendar.month_abbr[i].lower() for i in range(13)] 91*cda5da8dSAndroid Build Coastguard Worker f_month = [calendar.month_name[i].lower() for i in range(13)] 92*cda5da8dSAndroid Build Coastguard Worker self.a_month = a_month 93*cda5da8dSAndroid Build Coastguard Worker self.f_month = f_month 94*cda5da8dSAndroid Build Coastguard Worker 95*cda5da8dSAndroid Build Coastguard Worker def __calc_am_pm(self): 96*cda5da8dSAndroid Build Coastguard Worker # Set self.am_pm by using time.strftime(). 97*cda5da8dSAndroid Build Coastguard Worker 98*cda5da8dSAndroid Build Coastguard Worker # The magic date (1999,3,17,hour,44,55,2,76,0) is not really that 99*cda5da8dSAndroid Build Coastguard Worker # magical; just happened to have used it everywhere else where a 100*cda5da8dSAndroid Build Coastguard Worker # static date was needed. 101*cda5da8dSAndroid Build Coastguard Worker am_pm = [] 102*cda5da8dSAndroid Build Coastguard Worker for hour in (1, 22): 103*cda5da8dSAndroid Build Coastguard Worker time_tuple = time.struct_time((1999,3,17,hour,44,55,2,76,0)) 104*cda5da8dSAndroid Build Coastguard Worker am_pm.append(time.strftime("%p", time_tuple).lower()) 105*cda5da8dSAndroid Build Coastguard Worker self.am_pm = am_pm 106*cda5da8dSAndroid Build Coastguard Worker 107*cda5da8dSAndroid Build Coastguard Worker def __calc_date_time(self): 108*cda5da8dSAndroid Build Coastguard Worker # Set self.date_time, self.date, & self.time by using 109*cda5da8dSAndroid Build Coastguard Worker # time.strftime(). 110*cda5da8dSAndroid Build Coastguard Worker 111*cda5da8dSAndroid Build Coastguard Worker # Use (1999,3,17,22,44,55,2,76,0) for magic date because the amount of 112*cda5da8dSAndroid Build Coastguard Worker # overloaded numbers is minimized. The order in which searches for 113*cda5da8dSAndroid Build Coastguard Worker # values within the format string is very important; it eliminates 114*cda5da8dSAndroid Build Coastguard Worker # possible ambiguity for what something represents. 115*cda5da8dSAndroid Build Coastguard Worker time_tuple = time.struct_time((1999,3,17,22,44,55,2,76,0)) 116*cda5da8dSAndroid Build Coastguard Worker date_time = [None, None, None] 117*cda5da8dSAndroid Build Coastguard Worker date_time[0] = time.strftime("%c", time_tuple).lower() 118*cda5da8dSAndroid Build Coastguard Worker date_time[1] = time.strftime("%x", time_tuple).lower() 119*cda5da8dSAndroid Build Coastguard Worker date_time[2] = time.strftime("%X", time_tuple).lower() 120*cda5da8dSAndroid Build Coastguard Worker replacement_pairs = [('%', '%%'), (self.f_weekday[2], '%A'), 121*cda5da8dSAndroid Build Coastguard Worker (self.f_month[3], '%B'), (self.a_weekday[2], '%a'), 122*cda5da8dSAndroid Build Coastguard Worker (self.a_month[3], '%b'), (self.am_pm[1], '%p'), 123*cda5da8dSAndroid Build Coastguard Worker ('1999', '%Y'), ('99', '%y'), ('22', '%H'), 124*cda5da8dSAndroid Build Coastguard Worker ('44', '%M'), ('55', '%S'), ('76', '%j'), 125*cda5da8dSAndroid Build Coastguard Worker ('17', '%d'), ('03', '%m'), ('3', '%m'), 126*cda5da8dSAndroid Build Coastguard Worker # '3' needed for when no leading zero. 127*cda5da8dSAndroid Build Coastguard Worker ('2', '%w'), ('10', '%I')] 128*cda5da8dSAndroid Build Coastguard Worker replacement_pairs.extend([(tz, "%Z") for tz_values in self.timezone 129*cda5da8dSAndroid Build Coastguard Worker for tz in tz_values]) 130*cda5da8dSAndroid Build Coastguard Worker for offset,directive in ((0,'%c'), (1,'%x'), (2,'%X')): 131*cda5da8dSAndroid Build Coastguard Worker current_format = date_time[offset] 132*cda5da8dSAndroid Build Coastguard Worker for old, new in replacement_pairs: 133*cda5da8dSAndroid Build Coastguard Worker # Must deal with possible lack of locale info 134*cda5da8dSAndroid Build Coastguard Worker # manifesting itself as the empty string (e.g., Swedish's 135*cda5da8dSAndroid Build Coastguard Worker # lack of AM/PM info) or a platform returning a tuple of empty 136*cda5da8dSAndroid Build Coastguard Worker # strings (e.g., MacOS 9 having timezone as ('','')). 137*cda5da8dSAndroid Build Coastguard Worker if old: 138*cda5da8dSAndroid Build Coastguard Worker current_format = current_format.replace(old, new) 139*cda5da8dSAndroid Build Coastguard Worker # If %W is used, then Sunday, 2005-01-03 will fall on week 0 since 140*cda5da8dSAndroid Build Coastguard Worker # 2005-01-03 occurs before the first Monday of the year. Otherwise 141*cda5da8dSAndroid Build Coastguard Worker # %U is used. 142*cda5da8dSAndroid Build Coastguard Worker time_tuple = time.struct_time((1999,1,3,1,1,1,6,3,0)) 143*cda5da8dSAndroid Build Coastguard Worker if '00' in time.strftime(directive, time_tuple): 144*cda5da8dSAndroid Build Coastguard Worker U_W = '%W' 145*cda5da8dSAndroid Build Coastguard Worker else: 146*cda5da8dSAndroid Build Coastguard Worker U_W = '%U' 147*cda5da8dSAndroid Build Coastguard Worker date_time[offset] = current_format.replace('11', U_W) 148*cda5da8dSAndroid Build Coastguard Worker self.LC_date_time = date_time[0] 149*cda5da8dSAndroid Build Coastguard Worker self.LC_date = date_time[1] 150*cda5da8dSAndroid Build Coastguard Worker self.LC_time = date_time[2] 151*cda5da8dSAndroid Build Coastguard Worker 152*cda5da8dSAndroid Build Coastguard Worker def __calc_timezone(self): 153*cda5da8dSAndroid Build Coastguard Worker # Set self.timezone by using time.tzname. 154*cda5da8dSAndroid Build Coastguard Worker # Do not worry about possibility of time.tzname[0] == time.tzname[1] 155*cda5da8dSAndroid Build Coastguard Worker # and time.daylight; handle that in strptime. 156*cda5da8dSAndroid Build Coastguard Worker try: 157*cda5da8dSAndroid Build Coastguard Worker time.tzset() 158*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 159*cda5da8dSAndroid Build Coastguard Worker pass 160*cda5da8dSAndroid Build Coastguard Worker self.tzname = time.tzname 161*cda5da8dSAndroid Build Coastguard Worker self.daylight = time.daylight 162*cda5da8dSAndroid Build Coastguard Worker no_saving = frozenset({"utc", "gmt", self.tzname[0].lower()}) 163*cda5da8dSAndroid Build Coastguard Worker if self.daylight: 164*cda5da8dSAndroid Build Coastguard Worker has_saving = frozenset({self.tzname[1].lower()}) 165*cda5da8dSAndroid Build Coastguard Worker else: 166*cda5da8dSAndroid Build Coastguard Worker has_saving = frozenset() 167*cda5da8dSAndroid Build Coastguard Worker self.timezone = (no_saving, has_saving) 168*cda5da8dSAndroid Build Coastguard Worker 169*cda5da8dSAndroid Build Coastguard Worker 170*cda5da8dSAndroid Build Coastguard Workerclass TimeRE(dict): 171*cda5da8dSAndroid Build Coastguard Worker """Handle conversion from format directives to regexes.""" 172*cda5da8dSAndroid Build Coastguard Worker 173*cda5da8dSAndroid Build Coastguard Worker def __init__(self, locale_time=None): 174*cda5da8dSAndroid Build Coastguard Worker """Create keys/values. 175*cda5da8dSAndroid Build Coastguard Worker 176*cda5da8dSAndroid Build Coastguard Worker Order of execution is important for dependency reasons. 177*cda5da8dSAndroid Build Coastguard Worker 178*cda5da8dSAndroid Build Coastguard Worker """ 179*cda5da8dSAndroid Build Coastguard Worker if locale_time: 180*cda5da8dSAndroid Build Coastguard Worker self.locale_time = locale_time 181*cda5da8dSAndroid Build Coastguard Worker else: 182*cda5da8dSAndroid Build Coastguard Worker self.locale_time = LocaleTime() 183*cda5da8dSAndroid Build Coastguard Worker base = super() 184*cda5da8dSAndroid Build Coastguard Worker base.__init__({ 185*cda5da8dSAndroid Build Coastguard Worker # The " [1-9]" part of the regex is to make %c from ANSI C work 186*cda5da8dSAndroid Build Coastguard Worker 'd': r"(?P<d>3[0-1]|[1-2]\d|0[1-9]|[1-9]| [1-9])", 187*cda5da8dSAndroid Build Coastguard Worker 'f': r"(?P<f>[0-9]{1,6})", 188*cda5da8dSAndroid Build Coastguard Worker 'H': r"(?P<H>2[0-3]|[0-1]\d|\d)", 189*cda5da8dSAndroid Build Coastguard Worker 'I': r"(?P<I>1[0-2]|0[1-9]|[1-9])", 190*cda5da8dSAndroid Build Coastguard Worker 'G': r"(?P<G>\d\d\d\d)", 191*cda5da8dSAndroid Build Coastguard Worker 'j': r"(?P<j>36[0-6]|3[0-5]\d|[1-2]\d\d|0[1-9]\d|00[1-9]|[1-9]\d|0[1-9]|[1-9])", 192*cda5da8dSAndroid Build Coastguard Worker 'm': r"(?P<m>1[0-2]|0[1-9]|[1-9])", 193*cda5da8dSAndroid Build Coastguard Worker 'M': r"(?P<M>[0-5]\d|\d)", 194*cda5da8dSAndroid Build Coastguard Worker 'S': r"(?P<S>6[0-1]|[0-5]\d|\d)", 195*cda5da8dSAndroid Build Coastguard Worker 'U': r"(?P<U>5[0-3]|[0-4]\d|\d)", 196*cda5da8dSAndroid Build Coastguard Worker 'w': r"(?P<w>[0-6])", 197*cda5da8dSAndroid Build Coastguard Worker 'u': r"(?P<u>[1-7])", 198*cda5da8dSAndroid Build Coastguard Worker 'V': r"(?P<V>5[0-3]|0[1-9]|[1-4]\d|\d)", 199*cda5da8dSAndroid Build Coastguard Worker # W is set below by using 'U' 200*cda5da8dSAndroid Build Coastguard Worker 'y': r"(?P<y>\d\d)", 201*cda5da8dSAndroid Build Coastguard Worker #XXX: Does 'Y' need to worry about having less or more than 202*cda5da8dSAndroid Build Coastguard Worker # 4 digits? 203*cda5da8dSAndroid Build Coastguard Worker 'Y': r"(?P<Y>\d\d\d\d)", 204*cda5da8dSAndroid Build Coastguard Worker 'z': r"(?P<z>[+-]\d\d:?[0-5]\d(:?[0-5]\d(\.\d{1,6})?)?|(?-i:Z))", 205*cda5da8dSAndroid Build Coastguard Worker 'A': self.__seqToRE(self.locale_time.f_weekday, 'A'), 206*cda5da8dSAndroid Build Coastguard Worker 'a': self.__seqToRE(self.locale_time.a_weekday, 'a'), 207*cda5da8dSAndroid Build Coastguard Worker 'B': self.__seqToRE(self.locale_time.f_month[1:], 'B'), 208*cda5da8dSAndroid Build Coastguard Worker 'b': self.__seqToRE(self.locale_time.a_month[1:], 'b'), 209*cda5da8dSAndroid Build Coastguard Worker 'p': self.__seqToRE(self.locale_time.am_pm, 'p'), 210*cda5da8dSAndroid Build Coastguard Worker 'Z': self.__seqToRE((tz for tz_names in self.locale_time.timezone 211*cda5da8dSAndroid Build Coastguard Worker for tz in tz_names), 212*cda5da8dSAndroid Build Coastguard Worker 'Z'), 213*cda5da8dSAndroid Build Coastguard Worker '%': '%'}) 214*cda5da8dSAndroid Build Coastguard Worker base.__setitem__('W', base.__getitem__('U').replace('U', 'W')) 215*cda5da8dSAndroid Build Coastguard Worker base.__setitem__('c', self.pattern(self.locale_time.LC_date_time)) 216*cda5da8dSAndroid Build Coastguard Worker base.__setitem__('x', self.pattern(self.locale_time.LC_date)) 217*cda5da8dSAndroid Build Coastguard Worker base.__setitem__('X', self.pattern(self.locale_time.LC_time)) 218*cda5da8dSAndroid Build Coastguard Worker 219*cda5da8dSAndroid Build Coastguard Worker def __seqToRE(self, to_convert, directive): 220*cda5da8dSAndroid Build Coastguard Worker """Convert a list to a regex string for matching a directive. 221*cda5da8dSAndroid Build Coastguard Worker 222*cda5da8dSAndroid Build Coastguard Worker Want possible matching values to be from longest to shortest. This 223*cda5da8dSAndroid Build Coastguard Worker prevents the possibility of a match occurring for a value that also 224*cda5da8dSAndroid Build Coastguard Worker a substring of a larger value that should have matched (e.g., 'abc' 225*cda5da8dSAndroid Build Coastguard Worker matching when 'abcdef' should have been the match). 226*cda5da8dSAndroid Build Coastguard Worker 227*cda5da8dSAndroid Build Coastguard Worker """ 228*cda5da8dSAndroid Build Coastguard Worker to_convert = sorted(to_convert, key=len, reverse=True) 229*cda5da8dSAndroid Build Coastguard Worker for value in to_convert: 230*cda5da8dSAndroid Build Coastguard Worker if value != '': 231*cda5da8dSAndroid Build Coastguard Worker break 232*cda5da8dSAndroid Build Coastguard Worker else: 233*cda5da8dSAndroid Build Coastguard Worker return '' 234*cda5da8dSAndroid Build Coastguard Worker regex = '|'.join(re_escape(stuff) for stuff in to_convert) 235*cda5da8dSAndroid Build Coastguard Worker regex = '(?P<%s>%s' % (directive, regex) 236*cda5da8dSAndroid Build Coastguard Worker return '%s)' % regex 237*cda5da8dSAndroid Build Coastguard Worker 238*cda5da8dSAndroid Build Coastguard Worker def pattern(self, format): 239*cda5da8dSAndroid Build Coastguard Worker """Return regex pattern for the format string. 240*cda5da8dSAndroid Build Coastguard Worker 241*cda5da8dSAndroid Build Coastguard Worker Need to make sure that any characters that might be interpreted as 242*cda5da8dSAndroid Build Coastguard Worker regex syntax are escaped. 243*cda5da8dSAndroid Build Coastguard Worker 244*cda5da8dSAndroid Build Coastguard Worker """ 245*cda5da8dSAndroid Build Coastguard Worker processed_format = '' 246*cda5da8dSAndroid Build Coastguard Worker # The sub() call escapes all characters that might be misconstrued 247*cda5da8dSAndroid Build Coastguard Worker # as regex syntax. Cannot use re.escape since we have to deal with 248*cda5da8dSAndroid Build Coastguard Worker # format directives (%m, etc.). 249*cda5da8dSAndroid Build Coastguard Worker regex_chars = re_compile(r"([\\.^$*+?\(\){}\[\]|])") 250*cda5da8dSAndroid Build Coastguard Worker format = regex_chars.sub(r"\\\1", format) 251*cda5da8dSAndroid Build Coastguard Worker whitespace_replacement = re_compile(r'\s+') 252*cda5da8dSAndroid Build Coastguard Worker format = whitespace_replacement.sub(r'\\s+', format) 253*cda5da8dSAndroid Build Coastguard Worker while '%' in format: 254*cda5da8dSAndroid Build Coastguard Worker directive_index = format.index('%')+1 255*cda5da8dSAndroid Build Coastguard Worker processed_format = "%s%s%s" % (processed_format, 256*cda5da8dSAndroid Build Coastguard Worker format[:directive_index-1], 257*cda5da8dSAndroid Build Coastguard Worker self[format[directive_index]]) 258*cda5da8dSAndroid Build Coastguard Worker format = format[directive_index+1:] 259*cda5da8dSAndroid Build Coastguard Worker return "%s%s" % (processed_format, format) 260*cda5da8dSAndroid Build Coastguard Worker 261*cda5da8dSAndroid Build Coastguard Worker def compile(self, format): 262*cda5da8dSAndroid Build Coastguard Worker """Return a compiled re object for the format string.""" 263*cda5da8dSAndroid Build Coastguard Worker return re_compile(self.pattern(format), IGNORECASE) 264*cda5da8dSAndroid Build Coastguard Worker 265*cda5da8dSAndroid Build Coastguard Worker_cache_lock = _thread_allocate_lock() 266*cda5da8dSAndroid Build Coastguard Worker# DO NOT modify _TimeRE_cache or _regex_cache without acquiring the cache lock 267*cda5da8dSAndroid Build Coastguard Worker# first! 268*cda5da8dSAndroid Build Coastguard Worker_TimeRE_cache = TimeRE() 269*cda5da8dSAndroid Build Coastguard Worker_CACHE_MAX_SIZE = 5 # Max number of regexes stored in _regex_cache 270*cda5da8dSAndroid Build Coastguard Worker_regex_cache = {} 271*cda5da8dSAndroid Build Coastguard Worker 272*cda5da8dSAndroid Build Coastguard Workerdef _calc_julian_from_U_or_W(year, week_of_year, day_of_week, week_starts_Mon): 273*cda5da8dSAndroid Build Coastguard Worker """Calculate the Julian day based on the year, week of the year, and day of 274*cda5da8dSAndroid Build Coastguard Worker the week, with week_start_day representing whether the week of the year 275*cda5da8dSAndroid Build Coastguard Worker assumes the week starts on Sunday or Monday (6 or 0).""" 276*cda5da8dSAndroid Build Coastguard Worker first_weekday = datetime_date(year, 1, 1).weekday() 277*cda5da8dSAndroid Build Coastguard Worker # If we are dealing with the %U directive (week starts on Sunday), it's 278*cda5da8dSAndroid Build Coastguard Worker # easier to just shift the view to Sunday being the first day of the 279*cda5da8dSAndroid Build Coastguard Worker # week. 280*cda5da8dSAndroid Build Coastguard Worker if not week_starts_Mon: 281*cda5da8dSAndroid Build Coastguard Worker first_weekday = (first_weekday + 1) % 7 282*cda5da8dSAndroid Build Coastguard Worker day_of_week = (day_of_week + 1) % 7 283*cda5da8dSAndroid Build Coastguard Worker # Need to watch out for a week 0 (when the first day of the year is not 284*cda5da8dSAndroid Build Coastguard Worker # the same as that specified by %U or %W). 285*cda5da8dSAndroid Build Coastguard Worker week_0_length = (7 - first_weekday) % 7 286*cda5da8dSAndroid Build Coastguard Worker if week_of_year == 0: 287*cda5da8dSAndroid Build Coastguard Worker return 1 + day_of_week - first_weekday 288*cda5da8dSAndroid Build Coastguard Worker else: 289*cda5da8dSAndroid Build Coastguard Worker days_to_week = week_0_length + (7 * (week_of_year - 1)) 290*cda5da8dSAndroid Build Coastguard Worker return 1 + days_to_week + day_of_week 291*cda5da8dSAndroid Build Coastguard Worker 292*cda5da8dSAndroid Build Coastguard Worker 293*cda5da8dSAndroid Build Coastguard Workerdef _calc_julian_from_V(iso_year, iso_week, iso_weekday): 294*cda5da8dSAndroid Build Coastguard Worker """Calculate the Julian day based on the ISO 8601 year, week, and weekday. 295*cda5da8dSAndroid Build Coastguard Worker ISO weeks start on Mondays, with week 01 being the week containing 4 Jan. 296*cda5da8dSAndroid Build Coastguard Worker ISO week days range from 1 (Monday) to 7 (Sunday). 297*cda5da8dSAndroid Build Coastguard Worker """ 298*cda5da8dSAndroid Build Coastguard Worker correction = datetime_date(iso_year, 1, 4).isoweekday() + 3 299*cda5da8dSAndroid Build Coastguard Worker ordinal = (iso_week * 7) + iso_weekday - correction 300*cda5da8dSAndroid Build Coastguard Worker # ordinal may be negative or 0 now, which means the date is in the previous 301*cda5da8dSAndroid Build Coastguard Worker # calendar year 302*cda5da8dSAndroid Build Coastguard Worker if ordinal < 1: 303*cda5da8dSAndroid Build Coastguard Worker ordinal += datetime_date(iso_year, 1, 1).toordinal() 304*cda5da8dSAndroid Build Coastguard Worker iso_year -= 1 305*cda5da8dSAndroid Build Coastguard Worker ordinal -= datetime_date(iso_year, 1, 1).toordinal() 306*cda5da8dSAndroid Build Coastguard Worker return iso_year, ordinal 307*cda5da8dSAndroid Build Coastguard Worker 308*cda5da8dSAndroid Build Coastguard Worker 309*cda5da8dSAndroid Build Coastguard Workerdef _strptime(data_string, format="%a %b %d %H:%M:%S %Y"): 310*cda5da8dSAndroid Build Coastguard Worker """Return a 2-tuple consisting of a time struct and an int containing 311*cda5da8dSAndroid Build Coastguard Worker the number of microseconds based on the input string and the 312*cda5da8dSAndroid Build Coastguard Worker format string.""" 313*cda5da8dSAndroid Build Coastguard Worker 314*cda5da8dSAndroid Build Coastguard Worker for index, arg in enumerate([data_string, format]): 315*cda5da8dSAndroid Build Coastguard Worker if not isinstance(arg, str): 316*cda5da8dSAndroid Build Coastguard Worker msg = "strptime() argument {} must be str, not {}" 317*cda5da8dSAndroid Build Coastguard Worker raise TypeError(msg.format(index, type(arg))) 318*cda5da8dSAndroid Build Coastguard Worker 319*cda5da8dSAndroid Build Coastguard Worker global _TimeRE_cache, _regex_cache 320*cda5da8dSAndroid Build Coastguard Worker with _cache_lock: 321*cda5da8dSAndroid Build Coastguard Worker locale_time = _TimeRE_cache.locale_time 322*cda5da8dSAndroid Build Coastguard Worker if (_getlang() != locale_time.lang or 323*cda5da8dSAndroid Build Coastguard Worker time.tzname != locale_time.tzname or 324*cda5da8dSAndroid Build Coastguard Worker time.daylight != locale_time.daylight): 325*cda5da8dSAndroid Build Coastguard Worker _TimeRE_cache = TimeRE() 326*cda5da8dSAndroid Build Coastguard Worker _regex_cache.clear() 327*cda5da8dSAndroid Build Coastguard Worker locale_time = _TimeRE_cache.locale_time 328*cda5da8dSAndroid Build Coastguard Worker if len(_regex_cache) > _CACHE_MAX_SIZE: 329*cda5da8dSAndroid Build Coastguard Worker _regex_cache.clear() 330*cda5da8dSAndroid Build Coastguard Worker format_regex = _regex_cache.get(format) 331*cda5da8dSAndroid Build Coastguard Worker if not format_regex: 332*cda5da8dSAndroid Build Coastguard Worker try: 333*cda5da8dSAndroid Build Coastguard Worker format_regex = _TimeRE_cache.compile(format) 334*cda5da8dSAndroid Build Coastguard Worker # KeyError raised when a bad format is found; can be specified as 335*cda5da8dSAndroid Build Coastguard Worker # \\, in which case it was a stray % but with a space after it 336*cda5da8dSAndroid Build Coastguard Worker except KeyError as err: 337*cda5da8dSAndroid Build Coastguard Worker bad_directive = err.args[0] 338*cda5da8dSAndroid Build Coastguard Worker if bad_directive == "\\": 339*cda5da8dSAndroid Build Coastguard Worker bad_directive = "%" 340*cda5da8dSAndroid Build Coastguard Worker del err 341*cda5da8dSAndroid Build Coastguard Worker raise ValueError("'%s' is a bad directive in format '%s'" % 342*cda5da8dSAndroid Build Coastguard Worker (bad_directive, format)) from None 343*cda5da8dSAndroid Build Coastguard Worker # IndexError only occurs when the format string is "%" 344*cda5da8dSAndroid Build Coastguard Worker except IndexError: 345*cda5da8dSAndroid Build Coastguard Worker raise ValueError("stray %% in format '%s'" % format) from None 346*cda5da8dSAndroid Build Coastguard Worker _regex_cache[format] = format_regex 347*cda5da8dSAndroid Build Coastguard Worker found = format_regex.match(data_string) 348*cda5da8dSAndroid Build Coastguard Worker if not found: 349*cda5da8dSAndroid Build Coastguard Worker raise ValueError("time data %r does not match format %r" % 350*cda5da8dSAndroid Build Coastguard Worker (data_string, format)) 351*cda5da8dSAndroid Build Coastguard Worker if len(data_string) != found.end(): 352*cda5da8dSAndroid Build Coastguard Worker raise ValueError("unconverted data remains: %s" % 353*cda5da8dSAndroid Build Coastguard Worker data_string[found.end():]) 354*cda5da8dSAndroid Build Coastguard Worker 355*cda5da8dSAndroid Build Coastguard Worker iso_year = year = None 356*cda5da8dSAndroid Build Coastguard Worker month = day = 1 357*cda5da8dSAndroid Build Coastguard Worker hour = minute = second = fraction = 0 358*cda5da8dSAndroid Build Coastguard Worker tz = -1 359*cda5da8dSAndroid Build Coastguard Worker gmtoff = None 360*cda5da8dSAndroid Build Coastguard Worker gmtoff_fraction = 0 361*cda5da8dSAndroid Build Coastguard Worker # Default to -1 to signify that values not known; not critical to have, 362*cda5da8dSAndroid Build Coastguard Worker # though 363*cda5da8dSAndroid Build Coastguard Worker iso_week = week_of_year = None 364*cda5da8dSAndroid Build Coastguard Worker week_of_year_start = None 365*cda5da8dSAndroid Build Coastguard Worker # weekday and julian defaulted to None so as to signal need to calculate 366*cda5da8dSAndroid Build Coastguard Worker # values 367*cda5da8dSAndroid Build Coastguard Worker weekday = julian = None 368*cda5da8dSAndroid Build Coastguard Worker found_dict = found.groupdict() 369*cda5da8dSAndroid Build Coastguard Worker for group_key in found_dict.keys(): 370*cda5da8dSAndroid Build Coastguard Worker # Directives not explicitly handled below: 371*cda5da8dSAndroid Build Coastguard Worker # c, x, X 372*cda5da8dSAndroid Build Coastguard Worker # handled by making out of other directives 373*cda5da8dSAndroid Build Coastguard Worker # U, W 374*cda5da8dSAndroid Build Coastguard Worker # worthless without day of the week 375*cda5da8dSAndroid Build Coastguard Worker if group_key == 'y': 376*cda5da8dSAndroid Build Coastguard Worker year = int(found_dict['y']) 377*cda5da8dSAndroid Build Coastguard Worker # Open Group specification for strptime() states that a %y 378*cda5da8dSAndroid Build Coastguard Worker #value in the range of [00, 68] is in the century 2000, while 379*cda5da8dSAndroid Build Coastguard Worker #[69,99] is in the century 1900 380*cda5da8dSAndroid Build Coastguard Worker if year <= 68: 381*cda5da8dSAndroid Build Coastguard Worker year += 2000 382*cda5da8dSAndroid Build Coastguard Worker else: 383*cda5da8dSAndroid Build Coastguard Worker year += 1900 384*cda5da8dSAndroid Build Coastguard Worker elif group_key == 'Y': 385*cda5da8dSAndroid Build Coastguard Worker year = int(found_dict['Y']) 386*cda5da8dSAndroid Build Coastguard Worker elif group_key == 'G': 387*cda5da8dSAndroid Build Coastguard Worker iso_year = int(found_dict['G']) 388*cda5da8dSAndroid Build Coastguard Worker elif group_key == 'm': 389*cda5da8dSAndroid Build Coastguard Worker month = int(found_dict['m']) 390*cda5da8dSAndroid Build Coastguard Worker elif group_key == 'B': 391*cda5da8dSAndroid Build Coastguard Worker month = locale_time.f_month.index(found_dict['B'].lower()) 392*cda5da8dSAndroid Build Coastguard Worker elif group_key == 'b': 393*cda5da8dSAndroid Build Coastguard Worker month = locale_time.a_month.index(found_dict['b'].lower()) 394*cda5da8dSAndroid Build Coastguard Worker elif group_key == 'd': 395*cda5da8dSAndroid Build Coastguard Worker day = int(found_dict['d']) 396*cda5da8dSAndroid Build Coastguard Worker elif group_key == 'H': 397*cda5da8dSAndroid Build Coastguard Worker hour = int(found_dict['H']) 398*cda5da8dSAndroid Build Coastguard Worker elif group_key == 'I': 399*cda5da8dSAndroid Build Coastguard Worker hour = int(found_dict['I']) 400*cda5da8dSAndroid Build Coastguard Worker ampm = found_dict.get('p', '').lower() 401*cda5da8dSAndroid Build Coastguard Worker # If there was no AM/PM indicator, we'll treat this like AM 402*cda5da8dSAndroid Build Coastguard Worker if ampm in ('', locale_time.am_pm[0]): 403*cda5da8dSAndroid Build Coastguard Worker # We're in AM so the hour is correct unless we're 404*cda5da8dSAndroid Build Coastguard Worker # looking at 12 midnight. 405*cda5da8dSAndroid Build Coastguard Worker # 12 midnight == 12 AM == hour 0 406*cda5da8dSAndroid Build Coastguard Worker if hour == 12: 407*cda5da8dSAndroid Build Coastguard Worker hour = 0 408*cda5da8dSAndroid Build Coastguard Worker elif ampm == locale_time.am_pm[1]: 409*cda5da8dSAndroid Build Coastguard Worker # We're in PM so we need to add 12 to the hour unless 410*cda5da8dSAndroid Build Coastguard Worker # we're looking at 12 noon. 411*cda5da8dSAndroid Build Coastguard Worker # 12 noon == 12 PM == hour 12 412*cda5da8dSAndroid Build Coastguard Worker if hour != 12: 413*cda5da8dSAndroid Build Coastguard Worker hour += 12 414*cda5da8dSAndroid Build Coastguard Worker elif group_key == 'M': 415*cda5da8dSAndroid Build Coastguard Worker minute = int(found_dict['M']) 416*cda5da8dSAndroid Build Coastguard Worker elif group_key == 'S': 417*cda5da8dSAndroid Build Coastguard Worker second = int(found_dict['S']) 418*cda5da8dSAndroid Build Coastguard Worker elif group_key == 'f': 419*cda5da8dSAndroid Build Coastguard Worker s = found_dict['f'] 420*cda5da8dSAndroid Build Coastguard Worker # Pad to always return microseconds. 421*cda5da8dSAndroid Build Coastguard Worker s += "0" * (6 - len(s)) 422*cda5da8dSAndroid Build Coastguard Worker fraction = int(s) 423*cda5da8dSAndroid Build Coastguard Worker elif group_key == 'A': 424*cda5da8dSAndroid Build Coastguard Worker weekday = locale_time.f_weekday.index(found_dict['A'].lower()) 425*cda5da8dSAndroid Build Coastguard Worker elif group_key == 'a': 426*cda5da8dSAndroid Build Coastguard Worker weekday = locale_time.a_weekday.index(found_dict['a'].lower()) 427*cda5da8dSAndroid Build Coastguard Worker elif group_key == 'w': 428*cda5da8dSAndroid Build Coastguard Worker weekday = int(found_dict['w']) 429*cda5da8dSAndroid Build Coastguard Worker if weekday == 0: 430*cda5da8dSAndroid Build Coastguard Worker weekday = 6 431*cda5da8dSAndroid Build Coastguard Worker else: 432*cda5da8dSAndroid Build Coastguard Worker weekday -= 1 433*cda5da8dSAndroid Build Coastguard Worker elif group_key == 'u': 434*cda5da8dSAndroid Build Coastguard Worker weekday = int(found_dict['u']) 435*cda5da8dSAndroid Build Coastguard Worker weekday -= 1 436*cda5da8dSAndroid Build Coastguard Worker elif group_key == 'j': 437*cda5da8dSAndroid Build Coastguard Worker julian = int(found_dict['j']) 438*cda5da8dSAndroid Build Coastguard Worker elif group_key in ('U', 'W'): 439*cda5da8dSAndroid Build Coastguard Worker week_of_year = int(found_dict[group_key]) 440*cda5da8dSAndroid Build Coastguard Worker if group_key == 'U': 441*cda5da8dSAndroid Build Coastguard Worker # U starts week on Sunday. 442*cda5da8dSAndroid Build Coastguard Worker week_of_year_start = 6 443*cda5da8dSAndroid Build Coastguard Worker else: 444*cda5da8dSAndroid Build Coastguard Worker # W starts week on Monday. 445*cda5da8dSAndroid Build Coastguard Worker week_of_year_start = 0 446*cda5da8dSAndroid Build Coastguard Worker elif group_key == 'V': 447*cda5da8dSAndroid Build Coastguard Worker iso_week = int(found_dict['V']) 448*cda5da8dSAndroid Build Coastguard Worker elif group_key == 'z': 449*cda5da8dSAndroid Build Coastguard Worker z = found_dict['z'] 450*cda5da8dSAndroid Build Coastguard Worker if z == 'Z': 451*cda5da8dSAndroid Build Coastguard Worker gmtoff = 0 452*cda5da8dSAndroid Build Coastguard Worker else: 453*cda5da8dSAndroid Build Coastguard Worker if z[3] == ':': 454*cda5da8dSAndroid Build Coastguard Worker z = z[:3] + z[4:] 455*cda5da8dSAndroid Build Coastguard Worker if len(z) > 5: 456*cda5da8dSAndroid Build Coastguard Worker if z[5] != ':': 457*cda5da8dSAndroid Build Coastguard Worker msg = f"Inconsistent use of : in {found_dict['z']}" 458*cda5da8dSAndroid Build Coastguard Worker raise ValueError(msg) 459*cda5da8dSAndroid Build Coastguard Worker z = z[:5] + z[6:] 460*cda5da8dSAndroid Build Coastguard Worker hours = int(z[1:3]) 461*cda5da8dSAndroid Build Coastguard Worker minutes = int(z[3:5]) 462*cda5da8dSAndroid Build Coastguard Worker seconds = int(z[5:7] or 0) 463*cda5da8dSAndroid Build Coastguard Worker gmtoff = (hours * 60 * 60) + (minutes * 60) + seconds 464*cda5da8dSAndroid Build Coastguard Worker gmtoff_remainder = z[8:] 465*cda5da8dSAndroid Build Coastguard Worker # Pad to always return microseconds. 466*cda5da8dSAndroid Build Coastguard Worker gmtoff_remainder_padding = "0" * (6 - len(gmtoff_remainder)) 467*cda5da8dSAndroid Build Coastguard Worker gmtoff_fraction = int(gmtoff_remainder + gmtoff_remainder_padding) 468*cda5da8dSAndroid Build Coastguard Worker if z.startswith("-"): 469*cda5da8dSAndroid Build Coastguard Worker gmtoff = -gmtoff 470*cda5da8dSAndroid Build Coastguard Worker gmtoff_fraction = -gmtoff_fraction 471*cda5da8dSAndroid Build Coastguard Worker elif group_key == 'Z': 472*cda5da8dSAndroid Build Coastguard Worker # Since -1 is default value only need to worry about setting tz if 473*cda5da8dSAndroid Build Coastguard Worker # it can be something other than -1. 474*cda5da8dSAndroid Build Coastguard Worker found_zone = found_dict['Z'].lower() 475*cda5da8dSAndroid Build Coastguard Worker for value, tz_values in enumerate(locale_time.timezone): 476*cda5da8dSAndroid Build Coastguard Worker if found_zone in tz_values: 477*cda5da8dSAndroid Build Coastguard Worker # Deal with bad locale setup where timezone names are the 478*cda5da8dSAndroid Build Coastguard Worker # same and yet time.daylight is true; too ambiguous to 479*cda5da8dSAndroid Build Coastguard Worker # be able to tell what timezone has daylight savings 480*cda5da8dSAndroid Build Coastguard Worker if (time.tzname[0] == time.tzname[1] and 481*cda5da8dSAndroid Build Coastguard Worker time.daylight and found_zone not in ("utc", "gmt")): 482*cda5da8dSAndroid Build Coastguard Worker break 483*cda5da8dSAndroid Build Coastguard Worker else: 484*cda5da8dSAndroid Build Coastguard Worker tz = value 485*cda5da8dSAndroid Build Coastguard Worker break 486*cda5da8dSAndroid Build Coastguard Worker # Deal with the cases where ambiguities arize 487*cda5da8dSAndroid Build Coastguard Worker # don't assume default values for ISO week/year 488*cda5da8dSAndroid Build Coastguard Worker if year is None and iso_year is not None: 489*cda5da8dSAndroid Build Coastguard Worker if iso_week is None or weekday is None: 490*cda5da8dSAndroid Build Coastguard Worker raise ValueError("ISO year directive '%G' must be used with " 491*cda5da8dSAndroid Build Coastguard Worker "the ISO week directive '%V' and a weekday " 492*cda5da8dSAndroid Build Coastguard Worker "directive ('%A', '%a', '%w', or '%u').") 493*cda5da8dSAndroid Build Coastguard Worker if julian is not None: 494*cda5da8dSAndroid Build Coastguard Worker raise ValueError("Day of the year directive '%j' is not " 495*cda5da8dSAndroid Build Coastguard Worker "compatible with ISO year directive '%G'. " 496*cda5da8dSAndroid Build Coastguard Worker "Use '%Y' instead.") 497*cda5da8dSAndroid Build Coastguard Worker elif week_of_year is None and iso_week is not None: 498*cda5da8dSAndroid Build Coastguard Worker if weekday is None: 499*cda5da8dSAndroid Build Coastguard Worker raise ValueError("ISO week directive '%V' must be used with " 500*cda5da8dSAndroid Build Coastguard Worker "the ISO year directive '%G' and a weekday " 501*cda5da8dSAndroid Build Coastguard Worker "directive ('%A', '%a', '%w', or '%u').") 502*cda5da8dSAndroid Build Coastguard Worker else: 503*cda5da8dSAndroid Build Coastguard Worker raise ValueError("ISO week directive '%V' is incompatible with " 504*cda5da8dSAndroid Build Coastguard Worker "the year directive '%Y'. Use the ISO year '%G' " 505*cda5da8dSAndroid Build Coastguard Worker "instead.") 506*cda5da8dSAndroid Build Coastguard Worker 507*cda5da8dSAndroid Build Coastguard Worker leap_year_fix = False 508*cda5da8dSAndroid Build Coastguard Worker if year is None and month == 2 and day == 29: 509*cda5da8dSAndroid Build Coastguard Worker year = 1904 # 1904 is first leap year of 20th century 510*cda5da8dSAndroid Build Coastguard Worker leap_year_fix = True 511*cda5da8dSAndroid Build Coastguard Worker elif year is None: 512*cda5da8dSAndroid Build Coastguard Worker year = 1900 513*cda5da8dSAndroid Build Coastguard Worker 514*cda5da8dSAndroid Build Coastguard Worker 515*cda5da8dSAndroid Build Coastguard Worker # If we know the week of the year and what day of that week, we can figure 516*cda5da8dSAndroid Build Coastguard Worker # out the Julian day of the year. 517*cda5da8dSAndroid Build Coastguard Worker if julian is None and weekday is not None: 518*cda5da8dSAndroid Build Coastguard Worker if week_of_year is not None: 519*cda5da8dSAndroid Build Coastguard Worker week_starts_Mon = True if week_of_year_start == 0 else False 520*cda5da8dSAndroid Build Coastguard Worker julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, 521*cda5da8dSAndroid Build Coastguard Worker week_starts_Mon) 522*cda5da8dSAndroid Build Coastguard Worker elif iso_year is not None and iso_week is not None: 523*cda5da8dSAndroid Build Coastguard Worker year, julian = _calc_julian_from_V(iso_year, iso_week, weekday + 1) 524*cda5da8dSAndroid Build Coastguard Worker if julian is not None and julian <= 0: 525*cda5da8dSAndroid Build Coastguard Worker year -= 1 526*cda5da8dSAndroid Build Coastguard Worker yday = 366 if calendar.isleap(year) else 365 527*cda5da8dSAndroid Build Coastguard Worker julian += yday 528*cda5da8dSAndroid Build Coastguard Worker 529*cda5da8dSAndroid Build Coastguard Worker if julian is None: 530*cda5da8dSAndroid Build Coastguard Worker # Cannot pre-calculate datetime_date() since can change in Julian 531*cda5da8dSAndroid Build Coastguard Worker # calculation and thus could have different value for the day of 532*cda5da8dSAndroid Build Coastguard Worker # the week calculation. 533*cda5da8dSAndroid Build Coastguard Worker # Need to add 1 to result since first day of the year is 1, not 0. 534*cda5da8dSAndroid Build Coastguard Worker julian = datetime_date(year, month, day).toordinal() - \ 535*cda5da8dSAndroid Build Coastguard Worker datetime_date(year, 1, 1).toordinal() + 1 536*cda5da8dSAndroid Build Coastguard Worker else: # Assume that if they bothered to include Julian day (or if it was 537*cda5da8dSAndroid Build Coastguard Worker # calculated above with year/week/weekday) it will be accurate. 538*cda5da8dSAndroid Build Coastguard Worker datetime_result = datetime_date.fromordinal( 539*cda5da8dSAndroid Build Coastguard Worker (julian - 1) + 540*cda5da8dSAndroid Build Coastguard Worker datetime_date(year, 1, 1).toordinal()) 541*cda5da8dSAndroid Build Coastguard Worker year = datetime_result.year 542*cda5da8dSAndroid Build Coastguard Worker month = datetime_result.month 543*cda5da8dSAndroid Build Coastguard Worker day = datetime_result.day 544*cda5da8dSAndroid Build Coastguard Worker if weekday is None: 545*cda5da8dSAndroid Build Coastguard Worker weekday = datetime_date(year, month, day).weekday() 546*cda5da8dSAndroid Build Coastguard Worker # Add timezone info 547*cda5da8dSAndroid Build Coastguard Worker tzname = found_dict.get("Z") 548*cda5da8dSAndroid Build Coastguard Worker 549*cda5da8dSAndroid Build Coastguard Worker if leap_year_fix: 550*cda5da8dSAndroid Build Coastguard Worker # the caller didn't supply a year but asked for Feb 29th. We couldn't 551*cda5da8dSAndroid Build Coastguard Worker # use the default of 1900 for computations. We set it back to ensure 552*cda5da8dSAndroid Build Coastguard Worker # that February 29th is smaller than March 1st. 553*cda5da8dSAndroid Build Coastguard Worker year = 1900 554*cda5da8dSAndroid Build Coastguard Worker 555*cda5da8dSAndroid Build Coastguard Worker return (year, month, day, 556*cda5da8dSAndroid Build Coastguard Worker hour, minute, second, 557*cda5da8dSAndroid Build Coastguard Worker weekday, julian, tz, tzname, gmtoff), fraction, gmtoff_fraction 558*cda5da8dSAndroid Build Coastguard Worker 559*cda5da8dSAndroid Build Coastguard Workerdef _strptime_time(data_string, format="%a %b %d %H:%M:%S %Y"): 560*cda5da8dSAndroid Build Coastguard Worker """Return a time struct based on the input string and the 561*cda5da8dSAndroid Build Coastguard Worker format string.""" 562*cda5da8dSAndroid Build Coastguard Worker tt = _strptime(data_string, format)[0] 563*cda5da8dSAndroid Build Coastguard Worker return time.struct_time(tt[:time._STRUCT_TM_ITEMS]) 564*cda5da8dSAndroid Build Coastguard Worker 565*cda5da8dSAndroid Build Coastguard Workerdef _strptime_datetime(cls, data_string, format="%a %b %d %H:%M:%S %Y"): 566*cda5da8dSAndroid Build Coastguard Worker """Return a class cls instance based on the input string and the 567*cda5da8dSAndroid Build Coastguard Worker format string.""" 568*cda5da8dSAndroid Build Coastguard Worker tt, fraction, gmtoff_fraction = _strptime(data_string, format) 569*cda5da8dSAndroid Build Coastguard Worker tzname, gmtoff = tt[-2:] 570*cda5da8dSAndroid Build Coastguard Worker args = tt[:6] + (fraction,) 571*cda5da8dSAndroid Build Coastguard Worker if gmtoff is not None: 572*cda5da8dSAndroid Build Coastguard Worker tzdelta = datetime_timedelta(seconds=gmtoff, microseconds=gmtoff_fraction) 573*cda5da8dSAndroid Build Coastguard Worker if tzname: 574*cda5da8dSAndroid Build Coastguard Worker tz = datetime_timezone(tzdelta, tzname) 575*cda5da8dSAndroid Build Coastguard Worker else: 576*cda5da8dSAndroid Build Coastguard Worker tz = datetime_timezone(tzdelta) 577*cda5da8dSAndroid Build Coastguard Worker args += (tz,) 578*cda5da8dSAndroid Build Coastguard Worker 579*cda5da8dSAndroid Build Coastguard Worker return cls(*args) 580