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