strptimerelated classes and functions classes localetime discovers and stores localespecific time information timere creates regexes for pattern matching a string of text containing time information functions getlang figure out what language is being used for the locale strptime calculates the time struct represented by the passedin string figure out what the current language is set to stores and handles localespecific information related to time attributes fweekday full weekday names 7item list aweekday abbreviated weekday names 7item list fmonth full month names 13item list dummy value in 0 which is added by code amonth abbreviated month names 13item list dummy value in 0 which is added by code ampm ampm representation 2item list lcdatetime format string for datetime representation string lcdate format string for date representation string lctime format string for time representation string timezone daylight and nondaylightsavings timezone representation 2item list of sets lang language used by instance 2item tuple set all attributes order of methods called matters for dependency reasons the locale language is set at the offset and then checked again before exiting this is to make sure that the attributes were not set with a mix of information from more than one locale this would most likely happen when using threads where one thread calls a localedependent function while another thread changes the locale while the function in the other thread is still running proper coding would call for locks to prevent changing the locale while localedependent code is running the check here is done in case someone does not think about doing this only other possible issue is if someone changed the timezone and did not call tz tzset that is an issue for the programmer though since changing the timezone is worthless without that call set self aweekday and self fweekday using the calendar module set self fmonth and self amonth using the calendar module set self ampm by using time strftime the magic date 1999 3 17 hour 44 55 2 76 0 is not really that magical just happened to have used it everywhere else where a static date was needed set self datetime self date self time by using time strftime use 1999 3 17 22 44 55 2 76 0 for magic date because the amount of overloaded numbers is minimized the order in which searches for values within the format string is very important it eliminates possible ambiguity for what something represents 3 needed for when no leading zero must deal with possible lack of locale info manifesting itself as the empty string e g swedish s lack of ampm info or a platform returning a tuple of empty strings e g macos 9 having timezone as if w is used then sunday 20050103 will fall on week 0 since 20050103 occurs before the first monday of the year otherwise u is used set self timezone by using time tzname do not worry about possibility of time tzname0 time tzname1 and time daylight handle that in strptime handle conversion from format directives to regexes def initself localetimenone if localetime self localetime localetime else self localetime localetime base super base init the 19 part of the regex is to make c from ansi c work d r pd30112d01919 19 f r pf091 6 h r ph20301dd i r pi10201919 g r pgdddd j r pj3606305d12dd019d001919d01919 m r pm10201919 m r pm05dd s r ps60105dd u r pu50304dd w r pw06 u r pu17 v r pv50301914dd w is set below by using u y r pydd xxx does y need to worry about having less or more than 4 digits y r pydddd z r pzdd 05d 05d d1 6 i z a self seqtoreself localetime fweekday a a self seqtoreself localetime aweekday a b self seqtoreself localetime fmonth1 b b self seqtoreself localetime amonth1 b p self seqtoreself localetime ampm p z self seqtoretz for tznames in self localetime timezone for tz in tznames z base setitem w base getitem u replace u w base setitem c self patternself localetime lcdatetime base setitem x self patternself localetime lcdate base setitem x self patternself localetime lctime def seqtoreself toconvert directive toconvert sortedtoconvert keylen reversetrue for value in toconvert if value break else return regex joinreescapestuff for stuff in toconvert regex pss directive regex return s regex def patternself format processedformat the sub call escapes all characters that might be misconstrued as regex syntax cannot use re escape since we have to deal with format directives m etc regexchars recompiler format regexchars subr1 format whitespacereplacement recompiler s format whitespacereplacement subr s format while in format directiveindex format index 1 processedformat sss processedformat format directiveindex1 selfformatdirectiveindex format formatdirectiveindex1 return ss processedformat format def compileself format do not modify timerecache or regexcache without acquiring the cache lock first calculate the julian day based on the year week of the year and day of the week with weekstartday representing whether the week of the year assumes the week starts on sunday or monday 6 or 0 firstweekday datetimedateyear 1 1 weekday if we are dealing with the u directive week starts on sunday it s easier to just shift the view to sunday being the first day of the week if not weekstartsmon firstweekday firstweekday 1 7 dayofweek dayofweek 1 7 need to watch out for a week 0 when the first day of the year is not the same as that specified by u or w week0length 7 firstweekday 7 if weekofyear 0 return 1 dayofweek firstweekday else daystoweek week0length 7 weekofyear 1 return 1 daystoweek dayofweek def strptimedatastring formata b d h m s y keyerror raised when a bad format is found can be specified as in which case it was a stray but with a space after it indexerror only occurs when the format string is default to 1 to signify that values not known not critical to have though weekday and julian defaulted to none so as to signal need to calculate values directives not explicitly handled below c x x handled by making out of other directives u w worthless without day of the week open group specification for strptime states that a y value in the range of 00 68 is in the century 2000 while 69 99 is in the century 1900 if there was no ampm indicator we ll treat this like am we re in am so the hour is correct unless we re looking at 12 midnight 12 midnight 12 am hour 0 we re in pm so we need to add 12 to the hour unless we re looking at 12 noon 12 noon 12 pm hour 12 pad to always return microseconds u starts week on sunday w starts week on monday pad to always return microseconds since 1 is default value only need to worry about setting tz if it can be something other than 1 deal with bad locale setup where timezone names are the same and yet time daylight is true too ambiguous to be able to tell what timezone has daylight savings deal with the cases where ambiguities arise don t assume default values for iso weekyear if we know the week of the year and what day of that week we can figure out the julian day of the year cannot precalculate datetimedate since can change in julian calculation and thus could have different value for the day of the week calculation need to add 1 to result since first day of the year is 1 not 0 calculated above with yearweekweekday it will be accurate add timezone info the caller didn t supply a year but asked for feb 29th we couldn t use the default of 1900 for computations we set it back to ensure that february 29th is smaller than march 1st return a time struct based on the input string and the format string tt strptimedatastring format0 return time structtimett time structtmitems def strptimedatetimecls datastring formata b d h m s y figure out what the current language is set to stores and handles locale specific information related to time attributes f_weekday full weekday names 7 item list a_weekday abbreviated weekday names 7 item list f_month full month names 13 item list dummy value in 0 which is added by code a_month abbreviated month names 13 item list dummy value in 0 which is added by code am_pm am pm representation 2 item list lc_date_time format string for date time representation string lc_date format string for date representation string lc_time format string for time representation string timezone daylight and non daylight savings timezone representation 2 item list of sets lang language used by instance 2 item tuple set all attributes order of methods called matters for dependency reasons the locale language is set at the offset and then checked again before exiting this is to make sure that the attributes were not set with a mix of information from more than one locale this would most likely happen when using threads where one thread calls a locale dependent function while another thread changes the locale while the function in the other thread is still running proper coding would call for locks to prevent changing the locale while locale dependent code is running the check here is done in case someone does not think about doing this only other possible issue is if someone changed the timezone and did not call tz tzset that is an issue for the programmer though since changing the timezone is worthless without that call set self a_weekday and self f_weekday using the calendar module set self f_month and self a_month using the calendar module set self am_pm by using time strftime the magic date 1999 3 17 hour 44 55 2 76 0 is not really that magical just happened to have used it everywhere else where a static date was needed set self date_time self date self time by using time strftime use 1999 3 17 22 44 55 2 76 0 for magic date because the amount of overloaded numbers is minimized the order in which searches for values within the format string is very important it eliminates possible ambiguity for what something represents 3 needed for when no leading zero must deal with possible lack of locale info manifesting itself as the empty string e g swedish s lack of am pm info or a platform returning a tuple of empty strings e g macos 9 having timezone as if w is used then sunday 2005 01 03 will fall on week 0 since 2005 01 03 occurs before the first monday of the year otherwise u is used set self timezone by using time tzname do not worry about possibility of time tzname 0 time tzname 1 and time daylight handle that in strptime handle conversion from format directives to regexes create keys values order of execution is important for dependency reasons the 1 9 part of the regex is to make c from ansi c work w is set below by using u xxx does y need to worry about having less or more than 4 digits convert a list to a regex string for matching a directive want possible matching values to be from longest to shortest this prevents the possibility of a match occurring for a value that also a substring of a larger value that should have matched e g abc matching when abcdef should have been the match return regex pattern for the format string need to make sure that any characters that might be interpreted as regex syntax are escaped the sub call escapes all characters that might be misconstrued as regex syntax cannot use re escape since we have to deal with format directives m etc return a compiled re object for the format string do not modify _timere_cache or _regex_cache without acquiring the cache lock first max number of regexes stored in _regex_cache calculate the julian day based on the year week of the year and day of the week with week_start_day representing whether the week of the year assumes the week starts on sunday or monday 6 or 0 if we are dealing with the u directive week starts on sunday it s easier to just shift the view to sunday being the first day of the week need to watch out for a week 0 when the first day of the year is not the same as that specified by u or w return a 2 tuple consisting of a time struct and an int containing the number of microseconds based on the input string and the format string keyerror raised when a bad format is found can be specified as in which case it was a stray but with a space after it indexerror only occurs when the format string is default to 1 to signify that values not known not critical to have though weekday and julian defaulted to none so as to signal need to calculate values directives not explicitly handled below c x x handled by making out of other directives u w worthless without day of the week open group specification for strptime states that a y value in the range of 00 68 is in the century 2000 while 69 99 is in the century 1900 if there was no am pm indicator we ll treat this like am we re in am so the hour is correct unless we re looking at 12 midnight 12 midnight 12 am hour 0 we re in pm so we need to add 12 to the hour unless we re looking at 12 noon 12 noon 12 pm hour 12 pad to always return microseconds u starts week on sunday w starts week on monday pad to always return microseconds since 1 is default value only need to worry about setting tz if it can be something other than 1 deal with bad locale setup where timezone names are the same and yet time daylight is true too ambiguous to be able to tell what timezone has daylight savings deal with the cases where ambiguities arise don t assume default values for iso week year 1904 is first leap year of 20th century if we know the week of the year and what day of that week we can figure out the julian day of the year cannot pre calculate datetime_date since can change in julian calculation and thus could have different value for the day of the week calculation need to add 1 to result since first day of the year is 1 not 0 assume that if they bothered to include julian day or if it was calculated above with year week weekday it will be accurate add timezone info the caller didn t supply a year but asked for feb 29th we couldn t use the default of 1900 for computations we set it back to ensure that february 29th is smaller than march 1st return a time struct based on the input string and the format string return a class cls instance based on the input string and the format string
import time
import locale
import calendar
from re import compile as re_compile
from re import IGNORECASE
from re import escape as re_escape
from datetime import (date as datetime_date,
                      timedelta as datetime_timedelta,
                      timezone as datetime_timezone)
from _thread import allocate_lock as _thread_allocate_lock

__all__ = []

def _getlang():
    
    return locale.getlocale(locale.LC_TIME)

class LocaleTime(object):
    

    def __init__(self):
        
        self.lang = _getlang()
        self.__calc_weekday()
        self.__calc_month()
        self.__calc_am_pm()
        self.__calc_timezone()
        self.__calc_date_time()
        if _getlang() != self.lang:
            raise ValueError("locale changed during initialization")
        if time.tzname != self.tzname or time.daylight != self.daylight:
            raise ValueError("timezone changed during initialization")

    def __calc_weekday(self):
        
        
        a_weekday = [calendar.day_abbr[i].lower() for i in range(7)]
        f_weekday = [calendar.day_name[i].lower() for i in range(7)]
        self.a_weekday = a_weekday
        self.f_weekday = f_weekday

    def __calc_month(self):
        
        a_month = [calendar.month_abbr[i].lower() for i in range(13)]
        f_month = [calendar.month_name[i].lower() for i in range(13)]
        self.a_month = a_month
        self.f_month = f_month

    def __calc_am_pm(self):
        

        
        
        
        am_pm = []
        for hour in (1, 22):
            time_tuple = time.struct_time((1999,3,17,hour,44,55,2,76,0))
            am_pm.append(time.strftime("%p", time_tuple).lower())
        self.am_pm = am_pm

    def __calc_date_time(self):
        
        

        
        
        
        
        time_tuple = time.struct_time((1999,3,17,22,44,55,2,76,0))
        date_time = [None, None, None]
        date_time[0] = time.strftime("%c", time_tuple).lower()
        date_time[1] = time.strftime("%x", time_tuple).lower()
        date_time[2] = time.strftime("%X", time_tuple).lower()
        replacement_pairs = [('%', '%%'), (self.f_weekday[2], '%A'),
                    (self.f_month[3], '%B'), (self.a_weekday[2], '%a'),
                    (self.a_month[3], '%b'), (self.am_pm[1], '%p'),
                    ('1999', '%Y'), ('99', '%y'), ('22', '%H'),
                    ('44', '%M'), ('55', '%S'), ('76', '%j'),
                    ('17', '%d'), ('03', '%m'), ('3', '%m'),
                    
                    ('2', '%w'), ('10', '%I')]
        replacement_pairs.extend([(tz, "%Z") for tz_values in self.timezone
                                                for tz in tz_values])
        for offset,directive in ((0,'%c'), (1,'%x'), (2,'%X')):
            current_format = date_time[offset]
            for old, new in replacement_pairs:
                
                
                
                
                if old:
                    current_format = current_format.replace(old, new)
            
            
            
            time_tuple = time.struct_time((1999,1,3,1,1,1,6,3,0))
            if '00' in time.strftime(directive, time_tuple):
                U_W = '%W'
            else:
                U_W = '%U'
            date_time[offset] = current_format.replace('11', U_W)
        self.LC_date_time = date_time[0]
        self.LC_date = date_time[1]
        self.LC_time = date_time[2]

    def __calc_timezone(self):
        
        
        
        try:
            time.tzset()
        except AttributeError:
            pass
        self.tzname = time.tzname
        self.daylight = time.daylight
        no_saving = frozenset({"utc", "gmt", self.tzname[0].lower()})
        if self.daylight:
            has_saving = frozenset({self.tzname[1].lower()})
        else:
            has_saving = frozenset()
        self.timezone = (no_saving, has_saving)


class TimeRE(dict):
    

    def __init__(self, locale_time=None):
        
        if locale_time:
            self.locale_time = locale_time
        else:
            self.locale_time = LocaleTime()
        base = super()
        base.__init__({
            
            'd': r"(?P<d>3[0-1]|[1-2]\d|0[1-9]|[1-9]| [1-9])",
            'f': r"(?P<f>[0-9]{1,6})",
            'H': r"(?P<H>2[0-3]|[0-1]\d|\d)",
            'I': r"(?P<I>1[0-2]|0[1-9]|[1-9])",
            'G': r"(?P<G>\d\d\d\d)",
            '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])",
            'm': r"(?P<m>1[0-2]|0[1-9]|[1-9])",
            'M': r"(?P<M>[0-5]\d|\d)",
            'S': r"(?P<S>6[0-1]|[0-5]\d|\d)",
            'U': r"(?P<U>5[0-3]|[0-4]\d|\d)",
            'w': r"(?P<w>[0-6])",
            'u': r"(?P<u>[1-7])",
            'V': r"(?P<V>5[0-3]|0[1-9]|[1-4]\d|\d)",
            
            'y': r"(?P<y>\d\d)",
            
            
            'Y': r"(?P<Y>\d\d\d\d)",
            'z': r"(?P<z>[+-]\d\d:?[0-5]\d(:?[0-5]\d(\.\d{1,6})?)?|(?-i:Z))",
            'A': self.__seqToRE(self.locale_time.f_weekday, 'A'),
            'a': self.__seqToRE(self.locale_time.a_weekday, 'a'),
            'B': self.__seqToRE(self.locale_time.f_month[1:], 'B'),
            'b': self.__seqToRE(self.locale_time.a_month[1:], 'b'),
            'p': self.__seqToRE(self.locale_time.am_pm, 'p'),
            'Z': self.__seqToRE((tz for tz_names in self.locale_time.timezone
                                        for tz in tz_names),
                                'Z'),
            '%': '%'})
        base.__setitem__('W', base.__getitem__('U').replace('U', 'W'))
        base.__setitem__('c', self.pattern(self.locale_time.LC_date_time))
        base.__setitem__('x', self.pattern(self.locale_time.LC_date))
        base.__setitem__('X', self.pattern(self.locale_time.LC_time))

    def __seqToRE(self, to_convert, directive):
        
        to_convert = sorted(to_convert, key=len, reverse=True)
        for value in to_convert:
            if value != '':
                break
        else:
            return ''
        regex = '|'.join(re_escape(stuff) for stuff in to_convert)
        regex = '(?P<%s>%s' % (directive, regex)
        return '%s)' % regex

    def pattern(self, format):
        
        processed_format = ''
        
        
        
        regex_chars = re_compile(r"([\\.^$*+?\(\){}\[\]|])")
        format = regex_chars.sub(r"\\\1", format)
        whitespace_replacement = re_compile(r'\s+')
        format = whitespace_replacement.sub(r'\\s+', format)
        while '%' in format:
            directive_index = format.index('%')+1
            processed_format = "%s%s%s" % (processed_format,
                                           format[:directive_index-1],
                                           self[format[directive_index]])
            format = format[directive_index+1:]
        return "%s%s" % (processed_format, format)

    def compile(self, format):
        
        return re_compile(self.pattern(format), IGNORECASE)

_cache_lock = _thread_allocate_lock()


_TimeRE_cache = TimeRE()
_CACHE_MAX_SIZE = 5 
_regex_cache = {}

def _calc_julian_from_U_or_W(year, week_of_year, day_of_week, week_starts_Mon):
    
    first_weekday = datetime_date(year, 1, 1).weekday()
    
    
    
    if not week_starts_Mon:
        first_weekday = (first_weekday + 1) % 7
        day_of_week = (day_of_week + 1) % 7
    
    
    week_0_length = (7 - first_weekday) % 7
    if week_of_year == 0:
        return 1 + day_of_week - first_weekday
    else:
        days_to_week = week_0_length + (7 * (week_of_year - 1))
        return 1 + days_to_week + day_of_week


def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
    

    for index, arg in enumerate([data_string, format]):
        if not isinstance(arg, str):
            msg = "strptime() argument {} must be str, not {}"
            raise TypeError(msg.format(index, type(arg)))

    global _TimeRE_cache, _regex_cache
    with _cache_lock:
        locale_time = _TimeRE_cache.locale_time
        if (_getlang() != locale_time.lang or
            time.tzname != locale_time.tzname or
            time.daylight != locale_time.daylight):
            _TimeRE_cache = TimeRE()
            _regex_cache.clear()
            locale_time = _TimeRE_cache.locale_time
        if len(_regex_cache) > _CACHE_MAX_SIZE:
            _regex_cache.clear()
        format_regex = _regex_cache.get(format)
        if not format_regex:
            try:
                format_regex = _TimeRE_cache.compile(format)
            
            
            except KeyError as err:
                bad_directive = err.args[0]
                if bad_directive == "\\":
                    bad_directive = "%"
                del err
                raise ValueError("'%s' is a bad directive in format '%s'" %
                                    (bad_directive, format)) from None
            
            except IndexError:
                raise ValueError("stray %% in format '%s'" % format) from None
            _regex_cache[format] = format_regex
    found = format_regex.match(data_string)
    if not found:
        raise ValueError("time data %r does not match format %r" %
                         (data_string, format))
    if len(data_string) != found.end():
        raise ValueError("unconverted data remains: %s" %
                          data_string[found.end():])

    iso_year = year = None
    month = day = 1
    hour = minute = second = fraction = 0
    tz = -1
    gmtoff = None
    gmtoff_fraction = 0
    
    
    iso_week = week_of_year = None
    week_of_year_start = None
    
    
    weekday = julian = None
    found_dict = found.groupdict()
    for group_key in found_dict.keys():
        
        
        
        
        
        if group_key == 'y':
            year = int(found_dict['y'])
            
            
            
            if year <= 68:
                year += 2000
            else:
                year += 1900
        elif group_key == 'Y':
            year = int(found_dict['Y'])
        elif group_key == 'G':
            iso_year = int(found_dict['G'])
        elif group_key == 'm':
            month = int(found_dict['m'])
        elif group_key == 'B':
            month = locale_time.f_month.index(found_dict['B'].lower())
        elif group_key == 'b':
            month = locale_time.a_month.index(found_dict['b'].lower())
        elif group_key == 'd':
            day = int(found_dict['d'])
        elif group_key == 'H':
            hour = int(found_dict['H'])
        elif group_key == 'I':
            hour = int(found_dict['I'])
            ampm = found_dict.get('p', '').lower()
            
            if ampm in ('', locale_time.am_pm[0]):
                
                
                
                if hour == 12:
                    hour = 0
            elif ampm == locale_time.am_pm[1]:
                
                
                
                if hour != 12:
                    hour += 12
        elif group_key == 'M':
            minute = int(found_dict['M'])
        elif group_key == 'S':
            second = int(found_dict['S'])
        elif group_key == 'f':
            s = found_dict['f']
            
            s += "0" * (6 - len(s))
            fraction = int(s)
        elif group_key == 'A':
            weekday = locale_time.f_weekday.index(found_dict['A'].lower())
        elif group_key == 'a':
            weekday = locale_time.a_weekday.index(found_dict['a'].lower())
        elif group_key == 'w':
            weekday = int(found_dict['w'])
            if weekday == 0:
                weekday = 6
            else:
                weekday -= 1
        elif group_key == 'u':
            weekday = int(found_dict['u'])
            weekday -= 1
        elif group_key == 'j':
            julian = int(found_dict['j'])
        elif group_key in ('U', 'W'):
            week_of_year = int(found_dict[group_key])
            if group_key == 'U':
                
                week_of_year_start = 6
            else:
                
                week_of_year_start = 0
        elif group_key == 'V':
            iso_week = int(found_dict['V'])
        elif group_key == 'z':
            z = found_dict['z']
            if z == 'Z':
                gmtoff = 0
            else:
                if z[3] == ':':
                    z = z[:3] + z[4:]
                    if len(z) > 5:
                        if z[5] != ':':
                            msg = f"Inconsistent use of : in {found_dict['z']}"
                            raise ValueError(msg)
                        z = z[:5] + z[6:]
                hours = int(z[1:3])
                minutes = int(z[3:5])
                seconds = int(z[5:7] or 0)
                gmtoff = (hours * 60 * 60) + (minutes * 60) + seconds
                gmtoff_remainder = z[8:]
                
                gmtoff_remainder_padding = "0" * (6 - len(gmtoff_remainder))
                gmtoff_fraction = int(gmtoff_remainder + gmtoff_remainder_padding)
                if z.startswith("-"):
                    gmtoff = -gmtoff
                    gmtoff_fraction = -gmtoff_fraction
        elif group_key == 'Z':
            
            
            found_zone = found_dict['Z'].lower()
            for value, tz_values in enumerate(locale_time.timezone):
                if found_zone in tz_values:
                    
                    
                    
                    if (time.tzname[0] == time.tzname[1] and
                       time.daylight and found_zone not in ("utc", "gmt")):
                        break
                    else:
                        tz = value
                        break

    
    
    if year is None and iso_year is not None:
        if iso_week is None or weekday is None:
            raise ValueError("ISO year directive '%G' must be used with "
                             "the ISO week directive '%V' and a weekday "
                             "directive ('%A', '%a', '%w', or '%u').")
        if julian is not None:
            raise ValueError("Day of the year directive '%j' is not "
                             "compatible with ISO year directive '%G'. "
                             "Use '%Y' instead.")
    elif week_of_year is None and iso_week is not None:
        if weekday is None:
            raise ValueError("ISO week directive '%V' must be used with "
                             "the ISO year directive '%G' and a weekday "
                             "directive ('%A', '%a', '%w', or '%u').")
        else:
            raise ValueError("ISO week directive '%V' is incompatible with "
                             "the year directive '%Y'. Use the ISO year '%G' "
                             "instead.")

    leap_year_fix = False
    if year is None and month == 2 and day == 29:
        year = 1904  
        leap_year_fix = True
    elif year is None:
        year = 1900

    
    
    if julian is None and weekday is not None:
        if week_of_year is not None:
            week_starts_Mon = True if week_of_year_start == 0 else False
            julian = _calc_julian_from_U_or_W(year, week_of_year, weekday,
                                                week_starts_Mon)
        elif iso_year is not None and iso_week is not None:
            datetime_result = datetime_date.fromisocalendar(iso_year, iso_week, weekday + 1)
            year = datetime_result.year
            month = datetime_result.month
            day = datetime_result.day
        if julian is not None and julian <= 0:
            year -= 1
            yday = 366 if calendar.isleap(year) else 365
            julian += yday

    if julian is None:
        
        
        
        
        julian = datetime_date(year, month, day).toordinal() - \
                  datetime_date(year, 1, 1).toordinal() + 1
    else:  
           
        datetime_result = datetime_date.fromordinal(
                            (julian - 1) +
                            datetime_date(year, 1, 1).toordinal())
        year = datetime_result.year
        month = datetime_result.month
        day = datetime_result.day
    if weekday is None:
        weekday = datetime_date(year, month, day).weekday()
    
    tzname = found_dict.get("Z")

    if leap_year_fix:
        
        
        
        year = 1900

    return (year, month, day,
            hour, minute, second,
            weekday, julian, tz, tzname, gmtoff), fraction, gmtoff_fraction

def _strptime_time(data_string, format="%a %b %d %H:%M:%S %Y"):
    
    tt = _strptime(data_string, format)[0]
    return time.struct_time(tt[:time._STRUCT_TM_ITEMS])

def _strptime_datetime(cls, data_string, format="%a %b %d %H:%M:%S %Y"):
    
    tt, fraction, gmtoff_fraction = _strptime(data_string, format)
    tzname, gmtoff = tt[-2:]
    args = tt[:6] + (fraction,)
    if gmtoff is not None:
        tzdelta = datetime_timedelta(seconds=gmtoff, microseconds=gmtoff_fraction)
        if tzname:
            tz = datetime_timezone(tzdelta, tzname)
        else:
            tz = datetime_timezone(tzdelta)
        args += (tz,)

    return cls(*args)