_strptime.py revision 0eadaac7dc3ae49974c105ff9e8c1e98a04d7d5a
166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman"""Strptime-related classes and functions.
266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
366b8ab22586debccb1f787d4d52b7f042d4ddeb8John BaumanCLASSES:
466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    LocaleTime -- Discovers and/or stores locale-specific time information
566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    TimeRE -- Creates regexes for pattern matching a string of text containing
666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman                time information as is returned by time.strftime()
766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
866b8ab22586debccb1f787d4d52b7f042d4ddeb8John BaumanFUNCTIONS:
966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    _getlang -- Figure out what language is being used for the locale
1066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    strptime -- Calculates the time struct represented by the passed-in string
1166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
1266b8ab22586debccb1f787d4d52b7f042d4ddeb8John BaumanRequires Python 2.2.1 or higher (mainly because of the use of property()).
1366b8ab22586debccb1f787d4d52b7f042d4ddeb8John BaumanCan be used in Python 2.2 if the following line is added:
1466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    True = 1; False = 0
1566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman"""
1666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Baumanimport time
1766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Baumanimport locale
1866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Baumanimport calendar
1966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Baumanfrom re import compile as re_compile
2066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Baumanfrom re import IGNORECASE
2166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Baumanfrom datetime import date as datetime_date
2266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
2366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman__author__ = "Brett Cannon"
2466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman__email__ = "brett@python.org"
2566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
2666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman__all__ = ['strptime']
2766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
2866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Baumandef _getlang():
2966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    # Figure out what the current language is set to.
3066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    current_lang = locale.getlocale(locale.LC_TIME)[0]
3166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    if current_lang:
3266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        return current_lang
3366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    else:
3466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        current_lang = locale.getdefaultlocale()[0]
3566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if current_lang:
3666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            return current_lang
3766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        else:
3866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            return ''
3966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
4066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Baumanclass LocaleTime(object):
4166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    """Stores and handles locale-specific information related to time.
4266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
4366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    ATTRIBUTES (all read-only after instance creation! Instance variables that
4466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman                store the values have mangled names):
4566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        f_weekday -- full weekday names (7-item list)
4666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        a_weekday -- abbreviated weekday names (7-item list)
4766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        f_month -- full weekday names (14-item list; dummy value in [0], which
4866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman                    is added by code)
4966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        a_month -- abbreviated weekday names (13-item list, dummy value in
5066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman                    [0], which is added by code)
5166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        am_pm -- AM/PM representation (2-item list)
5266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        LC_date_time -- format string for date/time representation (string)
5366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        LC_date -- format string for date representation (string)
5466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        LC_time -- format string for time representation (string)
5566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        timezone -- daylight- and non-daylight-savings timezone representation
5666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman                    (3-item list; code tacks on blank item at end for
5766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman                    possible lack of timezone such as UTC)
5866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        lang -- Language used by instance (string)
5966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    """
6066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
6166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    def __init__(self, f_weekday=None, a_weekday=None, f_month=None,
6266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman                 a_month=None, am_pm=None, LC_date_time=None, LC_time=None,
6366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman                 LC_date=None, timezone=None, lang=None):
6466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        """Optionally set attributes with passed-in values."""
6566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if f_weekday is None:
6666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__f_weekday = None
6766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        elif len(f_weekday) == 7:
6866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__f_weekday = list(f_weekday)
6966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        else:
7066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            raise TypeError("full weekday names must be a 7-item sequence")
7166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if a_weekday is None:
7266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__a_weekday = None
7366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        elif len(a_weekday) == 7:
7466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__a_weekday = list(a_weekday)
7566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        else:
7666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            raise TypeError(
7766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman                "abbreviated weekday names must be a 7-item  sequence")
7866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if f_month is None:
7966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__f_month = None
8066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        elif len(f_month) == 12:
8166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__f_month = self.__pad(f_month, True)
8266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        else:
8366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            raise TypeError("full month names must be a 12-item sequence")
8466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if a_month is None:
8566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__a_month = None
8666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        elif len(a_month) == 12:
8766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__a_month = self.__pad(a_month, True)
8866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        else:
8966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            raise TypeError(
9066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman                "abbreviated month names must be a 12-item sequence")
9166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if am_pm is None:
9266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__am_pm = None
9366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        elif len(am_pm) == 2:
9466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__am_pm = am_pm
9566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        else:
9666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            raise TypeError("AM/PM representation must be a 2-item sequence")
9766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        self.__LC_date_time = LC_date_time
9866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        self.__LC_time = LC_time
9966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        self.__LC_date = LC_date
10066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        self.__timezone = timezone
10166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if timezone:
10266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            if len(timezone) != 2:
10366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman                raise TypeError("timezone names must contain 2 items")
10466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            else:
10566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman                self.__timezone = self.__pad(timezone, False)
10666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        self.__lang = lang
10766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
10866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    def __pad(self, seq, front):
10966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # Add '' to seq to either front (is True), else the back.
11066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        seq = list(seq)
11166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if front:
11266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            seq.insert(0, '')
11366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        else:
11466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            seq.append('')
11566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        return seq
11666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
11766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    def __set_nothing(self, stuff):
11866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # Raise TypeError when trying to set an attribute.
11966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        raise TypeError("attribute does not support assignment")
12066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
12166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    def __get_f_weekday(self):
12266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # Fetch self.f_weekday.
12366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if not self.__f_weekday:
12466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__calc_weekday()
12566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        return self.__f_weekday
12666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
12766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    def __get_a_weekday(self):
12866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # Fetch self.a_weekday.
12966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if not self.__a_weekday:
13066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__calc_weekday()
13166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        return self.__a_weekday
13266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
13366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    f_weekday = property(__get_f_weekday, __set_nothing,
13466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman                         doc="Full weekday names")
13566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    a_weekday = property(__get_a_weekday, __set_nothing,
13666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman                         doc="Abbreviated weekday names")
13766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
13866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    def __get_f_month(self):
13966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # Fetch self.f_month.
14066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if not self.__f_month:
14166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__calc_month()
14266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        return self.__f_month
14366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
14466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    def __get_a_month(self):
14566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # Fetch self.a_month.
14666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if not self.__a_month:
14766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__calc_month()
14866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        return self.__a_month
14966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
15066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    f_month = property(__get_f_month, __set_nothing,
15166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman                       doc="Full month names (dummy value at index 0)")
15266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    a_month = property(__get_a_month, __set_nothing,
15366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman                       doc="Abbreviated month names (dummy value at index 0)")
15466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
15566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    def __get_am_pm(self):
15666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # Fetch self.am_pm.
15766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if not self.__am_pm:
15866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__calc_am_pm()
15966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        return self.__am_pm
16066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
16166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    am_pm = property(__get_am_pm, __set_nothing, doc="AM/PM representation")
16266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
16366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    def __get_timezone(self):
16466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # Fetch self.timezone.
16566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if not self.__timezone:
16666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__calc_timezone()
16766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        return self.__timezone
16866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
16966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    timezone = property(__get_timezone, __set_nothing,
17066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman                        doc="Timezone representation (dummy value at index 2)")
17166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
17266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    def __get_LC_date_time(self):
17366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # Fetch self.LC_date_time.
17466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if not self.__LC_date_time:
17566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__calc_date_time()
17666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        return self.__LC_date_time
17766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
17866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    def __get_LC_date(self):
17966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # Fetch self.LC_date.
18066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if not self.__LC_date:
18166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__calc_date_time()
18266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        return self.__LC_date
18366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
18466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    def __get_LC_time(self):
18566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # Fetch self.LC_time.
18666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if not self.__LC_time:
18766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__calc_date_time()
18866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        return self.__LC_time
18966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
19066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    LC_date_time = property(
19166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        __get_LC_date_time, __set_nothing,
19266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        doc=
19366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        "Format string for locale's date/time representation ('%c' format)")
19466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    LC_date = property(__get_LC_date, __set_nothing,
19566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        doc="Format string for locale's date representation ('%x' format)")
19666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    LC_time = property(__get_LC_time, __set_nothing,
19766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        doc="Format string for locale's time representation ('%X' format)")
19866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
19966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    def __get_lang(self):
20066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # Fetch self.lang.
20166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if not self.__lang:
20266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__calc_lang()
20366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        return self.__lang
20466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
20566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    lang = property(__get_lang, __set_nothing,
20666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman                    doc="Language used for instance")
20766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
20866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    def __calc_weekday(self):
20966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # Set self.__a_weekday and self.__f_weekday using the calendar
21066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # module.
21166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        a_weekday = [calendar.day_abbr[i] for i in range(7)]
21266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        f_weekday = [calendar.day_name[i] for i in range(7)]
21366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if not self.__a_weekday:
21466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__a_weekday = a_weekday
21566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if not self.__f_weekday:
21666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__f_weekday = f_weekday
21766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
21866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    def __calc_month(self):
21966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # Set self.__f_month and self.__a_month using the calendar module.
22066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        a_month = [calendar.month_abbr[i] for i in range(13)]
22166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        f_month = [calendar.month_name[i] for i in range(13)]
22266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if not self.__a_month:
22366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__a_month = a_month
22466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        if not self.__f_month:
22566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            self.__f_month = f_month
22666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
22766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    def __calc_am_pm(self):
22866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # Set self.__am_pm by using time.strftime().
22966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
23066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # The magic date (1999,3,17,hour,44,55,2,76,0) is not really that
23166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # magical; just happened to have used it everywhere else where a
23266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # static date was needed.
23366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        am_pm = []
23466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        for hour in (01,22):
23566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            time_tuple = time.struct_time((1999,3,17,hour,44,55,2,76,0))
23666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman            am_pm.append(time.strftime("%p", time_tuple))
23766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        self.__am_pm = am_pm
23866b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
23966b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman    def __calc_date_time(self):
24066b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # Set self.__date_time, self.__date, & self.__time by using
24166b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # time.strftime().
24266b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman
24366b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # Use (1999,3,17,22,44,55,2,76,0) for magic date because the amount of
24466b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # overloaded numbers is minimized.  The order in which searches for
24566b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # values within the format string is very important; it eliminates
24666b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        # possible ambiguity for what something represents.
24766b8ab22586debccb1f787d4d52b7f042d4ddeb8John Bauman        time_tuple = time.struct_time((1999,3,17,22,44,55,2,76,0))
248        date_time = [None, None, None]
249        date_time[0] = time.strftime("%c", time_tuple)
250        date_time[1] = time.strftime("%x", time_tuple)
251        date_time[2] = time.strftime("%X", time_tuple)
252        for offset,directive in ((0,'%c'), (1,'%x'), (2,'%X')):
253            current_format = date_time[offset]
254            for old, new in (
255                    ('%', '%%'), (self.f_weekday[2], '%A'),
256                    (self.f_month[3], '%B'), (self.a_weekday[2], '%a'),
257                    (self.a_month[3], '%b'), (self.am_pm[1], '%p'),
258                    (self.timezone[0], '%Z'), (self.timezone[1], '%Z'),
259                    ('1999', '%Y'), ('99', '%y'), ('22', '%H'),
260                    ('44', '%M'), ('55', '%S'), ('76', '%j'),
261                    ('17', '%d'), ('03', '%m'), ('3', '%m'),
262                    # '3' needed for when no leading zero.
263                    ('2', '%w'), ('10', '%I')):
264                # Must deal with possible lack of locale info
265                # manifesting itself as the empty string (e.g., Swedish's
266                # lack of AM/PM info) or a platform returning a tuple of empty
267                # strings (e.g., MacOS 9 having timezone as ('','')).
268                if old:
269                    current_format = current_format.replace(old, new)
270            time_tuple = time.struct_time((1999,1,3,1,1,1,6,3,0))
271            if time.strftime(directive, time_tuple).find('00'):
272                U_W = '%U'
273            else:
274                U_W = '%W'
275            date_time[offset] = current_format.replace('11', U_W)
276        if not self.__LC_date_time:
277            self.__LC_date_time = date_time[0]
278        if not self.__LC_date:
279            self.__LC_date = date_time[1]
280        if not self.__LC_time:
281            self.__LC_time = date_time[2]
282
283    def __calc_timezone(self):
284        # Set self.__timezone by using time.tzname.
285        #
286        # Empty string used for matching when timezone is not used/needed such
287        # as with UTC.
288        self.__timezone = self.__pad(time.tzname, 0)
289
290    def __calc_lang(self):
291        # Set self.__lang by using __getlang().
292        self.__lang = _getlang()
293
294
295
296class TimeRE(dict):
297    """Handle conversion from format directives to regexes."""
298
299    def __init__(self, locale_time=LocaleTime()):
300        """Init inst with non-locale regexes and store LocaleTime object."""
301        #XXX: Does 'Y' need to worry about having less or more than 4 digits?
302        base = super(TimeRE, self)
303        base.__init__({
304            # The " \d" option is to make %c from ANSI C work
305            'd': r"(?P<d>3[0-1]|[1-2]\d|0[1-9]|[1-9]| [1-9])",
306            'H': r"(?P<H>2[0-3]|[0-1]\d|\d)",
307            'I': r"(?P<I>1[0-2]|0[1-9]|[1-9])",
308            '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])",
309            'm': r"(?P<m>1[0-2]|0[1-9]|[1-9])",
310            'M': r"(?P<M>[0-5]\d|\d)",
311            'S': r"(?P<S>6[0-1]|[0-5]\d|\d)",
312            'U': r"(?P<U>5[0-3]|[0-4]\d|\d)",
313            'w': r"(?P<w>[0-6])",
314            # W is set below by using 'U'
315            'y': r"(?P<y>\d\d)",
316            'Y': r"(?P<Y>\d\d\d\d)"})
317        base.__setitem__('W', base.__getitem__('U'))
318        self.locale_time = locale_time
319
320    def __getitem__(self, fetch):
321        """Try to fetch regex; if it does not exist, construct it."""
322        try:
323            return super(TimeRE, self).__getitem__(fetch)
324        except KeyError:
325            constructors = {
326                'A': lambda: self.__seqToRE(self.locale_time.f_weekday, fetch),
327                'a': lambda: self.__seqToRE(self.locale_time.a_weekday, fetch),
328                'B': lambda: self.__seqToRE(self.locale_time.f_month[1:],
329                                            fetch),
330                'b': lambda: self.__seqToRE(self.locale_time.a_month[1:],
331                                            fetch),
332                'c': lambda: self.pattern(self.locale_time.LC_date_time),
333                'p': lambda: self.__seqToRE(self.locale_time.am_pm, fetch),
334                'x': lambda: self.pattern(self.locale_time.LC_date),
335                'X': lambda: self.pattern(self.locale_time.LC_time),
336                'Z': lambda: self.__seqToRE(self.locale_time.timezone, fetch),
337                '%': lambda: '%',
338                }
339            if fetch in constructors:
340                self[fetch] = constructors[fetch]()
341                return self[fetch]
342            else:
343                raise
344
345    def __seqToRE(self, to_convert, directive):
346        """Convert a list to a regex string for matching a directive."""
347        def sorter(a, b):
348            """Sort based on length.
349
350            Done in case for some strange reason that names in the locale only
351            differ by a suffix and thus want the name with the suffix to match
352            first.
353            """
354            try:
355                a_length = len(a)
356            except TypeError:
357                a_length = 0
358            try:
359                b_length = len(b)
360            except TypeError:
361                b_length = 0
362            return cmp(b_length, a_length)
363
364        to_convert = to_convert[:]  # Don't want to change value in-place.
365        for value in to_convert:
366            if value != '':
367                break
368        else:
369            return ''
370        to_convert.sort(sorter)
371        regex = '|'.join(to_convert)
372        regex = '(?P<%s>%s' % (directive, regex)
373        return '%s)' % regex
374
375    def pattern(self, format):
376        """Return re pattern for the format string.
377
378        Need to make sure that any characters that might be interpreted as
379        regex syntax is escaped.
380
381        """
382        processed_format = ''
383        # The sub() call escapes all characters that might be misconstrued
384        # as regex syntax.
385        regex_chars = re_compile(r"([\\.^$*+?{}\[\]|])")
386        format = regex_chars.sub(r"\\\1", format)
387        whitespace_replacement = re_compile('\s+')
388        format = whitespace_replacement.sub('\s*', format)
389        while format.find('%') != -1:
390            directive_index = format.index('%')+1
391            processed_format = "%s%s%s" % (processed_format,
392                                           format[:directive_index-1],
393                                           self[format[directive_index]])
394            format = format[directive_index+1:]
395        return "%s%s" % (processed_format, format)
396
397    def compile(self, format):
398        """Return a compiled re object for the format string."""
399        return re_compile(self.pattern(format), IGNORECASE)
400
401# Cached TimeRE; probably only need one instance ever so cache it for performance
402_locale_cache = TimeRE()
403# Cached regex objects; same reason as for TimeRE cache
404_regex_cache = dict()
405
406def strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
407    """Return a time struct based on the input data and the format string."""
408    global _locale_cache
409    global _regex_cache
410    locale_time = _locale_cache.locale_time
411    # If the language changes, caches are invalidated, so clear them
412    if locale_time.lang != _getlang():
413        _locale_cache = TimeRE()
414        _regex_cache.clear()
415    format_regex = _regex_cache.get(format)
416    if not format_regex:
417        # Limit regex cache size to prevent major bloating of the module;
418        # The value 5 is arbitrary
419        if len(_regex_cache) > 5:
420            _regex_cache.clear()
421        format_regex = _locale_cache.compile(format)
422        _regex_cache[format] = format_regex
423    found = format_regex.match(data_string)
424    if not found:
425        raise ValueError("time data did not match format")
426    year = 1900
427    month = day = 1
428    hour = minute = second = 0
429    tz = -1
430    # weekday and julian defaulted to -1 so as to signal need to calculate values
431    weekday = julian = -1
432    found_dict = found.groupdict()
433    for group_key in found_dict.iterkeys():
434        if group_key == 'y':
435            year = int(found_dict['y'])
436            # Open Group specification for strptime() states that a %y
437            #value in the range of [00, 68] is in the century 2000, while
438            #[69,99] is in the century 1900
439            if year <= 68:
440                year += 2000
441            else:
442                year += 1900
443        elif group_key == 'Y':
444            year = int(found_dict['Y'])
445        elif group_key == 'm':
446            month = int(found_dict['m'])
447        elif group_key == 'B':
448            month = _insensitiveindex(locale_time.f_month, found_dict['B'])
449        elif group_key == 'b':
450            month = _insensitiveindex(locale_time.a_month, found_dict['b'])
451        elif group_key == 'd':
452            day = int(found_dict['d'])
453        elif group_key is 'H':
454            hour = int(found_dict['H'])
455        elif group_key == 'I':
456            hour = int(found_dict['I'])
457            ampm = found_dict.get('p', '').lower()
458            # If there was no AM/PM indicator, we'll treat this like AM
459            if ampm in ('', locale_time.am_pm[0].lower()):
460                # We're in AM so the hour is correct unless we're
461                # looking at 12 midnight.
462                # 12 midnight == 12 AM == hour 0
463                if hour == 12:
464                    hour = 0
465            elif ampm == locale_time.am_pm[1].lower():
466                # We're in PM so we need to add 12 to the hour unless
467                # we're looking at 12 noon.
468                # 12 noon == 12 PM == hour 12
469                if hour != 12:
470                    hour += 12
471        elif group_key == 'M':
472            minute = int(found_dict['M'])
473        elif group_key == 'S':
474            second = int(found_dict['S'])
475        elif group_key == 'A':
476            weekday = _insensitiveindex(locale_time.f_weekday,
477                                        found_dict['A'])
478        elif group_key == 'a':
479            weekday = _insensitiveindex(locale_time.a_weekday,
480                                        found_dict['a'])
481        elif group_key == 'w':
482            weekday = int(found_dict['w'])
483            if weekday == 0:
484                weekday = 6
485            else:
486                weekday -= 1
487        elif group_key == 'j':
488            julian = int(found_dict['j'])
489        elif group_key == 'Z':
490            found_zone = found_dict['Z'].lower()
491            if locale_time.timezone[0] == locale_time.timezone[1]:
492                pass #Deals with bad locale setup where timezone info is
493                     # the same; first found on FreeBSD 4.4.
494            elif locale_time.timezone[0].lower() == found_zone:
495                tz = 0
496            elif locale_time.timezone[1].lower() == found_zone:
497                tz = 1
498            elif locale_time.timezone[2].lower() == found_zone:
499                tz = -1
500    # Cannot pre-calculate datetime_date() since can change in Julian
501    #calculation and thus could have different value for the day of the week
502    #calculation
503    if julian == -1:
504        # Need to add 1 to result since first day of the year is 1, not 0.
505        julian = datetime_date(year, month, day).toordinal() - \
506                  datetime_date(year, 1, 1).toordinal() + 1
507    else:  # Assume that if they bothered to include Julian day it will
508           #be accurate
509        datetime_result = datetime_date.fromordinal((julian - 1) + datetime_date(year, 1, 1).toordinal())
510        year = datetime_result.year
511        month = datetime_result.month
512        day = datetime_result.day
513    if weekday == -1:
514        weekday = datetime_date(year, month, day).weekday()
515    return time.struct_time((year, month, day,
516                             hour, minute, second,
517                             weekday, julian, tz))
518
519def _insensitiveindex(lst, findme):
520    # Perform a case-insensitive index search.
521
522    #XXX <bc>: If LocaleTime is not exposed, then consider removing this and
523    #          just lowercase when LocaleTime sets its vars and lowercasing
524    #          search values.
525    findme = findme.lower()
526    for key,item in enumerate(lst):
527        if item.lower() == findme:
528            return key
529    else:
530        raise ValueError("value not in list")
531