Index: third_party/recipe_engine/third_party/dateutil/parser.py |
diff --git a/third_party/recipe_engine/third_party/dateutil/parser.py b/third_party/recipe_engine/third_party/dateutil/parser.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..5d824e411f32949c95de0a512bfa061a12c68bc1 |
--- /dev/null |
+++ b/third_party/recipe_engine/third_party/dateutil/parser.py |
@@ -0,0 +1,886 @@ |
+# -*- coding:iso-8859-1 -*- |
+""" |
+Copyright (c) 2003-2007 Gustavo Niemeyer <gustavo@niemeyer.net> |
+ |
+This module offers extensions to the standard python 2.3+ |
+datetime module. |
+""" |
+__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>" |
+__license__ = "PSF License" |
+ |
+import datetime |
+import string |
+import time |
+import sys |
+import os |
+ |
+try: |
+ from cStringIO import StringIO |
+except ImportError: |
+ from StringIO import StringIO |
+ |
+import relativedelta |
+import tz |
+ |
+ |
+__all__ = ["parse", "parserinfo"] |
+ |
+ |
+# Some pointers: |
+# |
+# http://www.cl.cam.ac.uk/~mgk25/iso-time.html |
+# http://www.iso.ch/iso/en/prods-services/popstds/datesandtime.html |
+# http://www.w3.org/TR/NOTE-datetime |
+# http://ringmaster.arc.nasa.gov/tools/time_formats.html |
+# http://search.cpan.org/author/MUIR/Time-modules-2003.0211/lib/Time/ParseDate.pm |
+# http://stein.cshl.org/jade/distrib/docs/java.text.SimpleDateFormat.html |
+ |
+ |
+class _timelex(object): |
+ |
+ def __init__(self, instream): |
+ if isinstance(instream, basestring): |
+ instream = StringIO(instream) |
+ self.instream = instream |
+ self.wordchars = ('abcdfeghijklmnopqrstuvwxyz' |
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_' |
+ 'ßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ' |
+ 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ') |
+ self.numchars = '0123456789' |
+ self.whitespace = ' \t\r\n' |
+ self.charstack = [] |
+ self.tokenstack = [] |
+ self.eof = False |
+ |
+ def get_token(self): |
+ if self.tokenstack: |
+ return self.tokenstack.pop(0) |
+ seenletters = False |
+ token = None |
+ state = None |
+ wordchars = self.wordchars |
+ numchars = self.numchars |
+ whitespace = self.whitespace |
+ while not self.eof: |
+ if self.charstack: |
+ nextchar = self.charstack.pop(0) |
+ else: |
+ nextchar = self.instream.read(1) |
+ while nextchar == '\x00': |
+ nextchar = self.instream.read(1) |
+ if not nextchar: |
+ self.eof = True |
+ break |
+ elif not state: |
+ token = nextchar |
+ if nextchar in wordchars: |
+ state = 'a' |
+ elif nextchar in numchars: |
+ state = '0' |
+ elif nextchar in whitespace: |
+ token = ' ' |
+ break # emit token |
+ else: |
+ break # emit token |
+ elif state == 'a': |
+ seenletters = True |
+ if nextchar in wordchars: |
+ token += nextchar |
+ elif nextchar == '.': |
+ token += nextchar |
+ state = 'a.' |
+ else: |
+ self.charstack.append(nextchar) |
+ break # emit token |
+ elif state == '0': |
+ if nextchar in numchars: |
+ token += nextchar |
+ elif nextchar == '.': |
+ token += nextchar |
+ state = '0.' |
+ else: |
+ self.charstack.append(nextchar) |
+ break # emit token |
+ elif state == 'a.': |
+ seenletters = True |
+ if nextchar == '.' or nextchar in wordchars: |
+ token += nextchar |
+ elif nextchar in numchars and token[-1] == '.': |
+ token += nextchar |
+ state = '0.' |
+ else: |
+ self.charstack.append(nextchar) |
+ break # emit token |
+ elif state == '0.': |
+ if nextchar == '.' or nextchar in numchars: |
+ token += nextchar |
+ elif nextchar in wordchars and token[-1] == '.': |
+ token += nextchar |
+ state = 'a.' |
+ else: |
+ self.charstack.append(nextchar) |
+ break # emit token |
+ if (state in ('a.', '0.') and |
+ (seenletters or token.count('.') > 1 or token[-1] == '.')): |
+ l = token.split('.') |
+ token = l[0] |
+ for tok in l[1:]: |
+ self.tokenstack.append('.') |
+ if tok: |
+ self.tokenstack.append(tok) |
+ return token |
+ |
+ def __iter__(self): |
+ return self |
+ |
+ def next(self): |
+ token = self.get_token() |
+ if token is None: |
+ raise StopIteration |
+ return token |
+ |
+ def split(cls, s): |
+ return list(cls(s)) |
+ split = classmethod(split) |
+ |
+ |
+class _resultbase(object): |
+ |
+ def __init__(self): |
+ for attr in self.__slots__: |
+ setattr(self, attr, None) |
+ |
+ def _repr(self, classname): |
+ l = [] |
+ for attr in self.__slots__: |
+ value = getattr(self, attr) |
+ if value is not None: |
+ l.append("%s=%s" % (attr, `value`)) |
+ return "%s(%s)" % (classname, ", ".join(l)) |
+ |
+ def __repr__(self): |
+ return self._repr(self.__class__.__name__) |
+ |
+ |
+class parserinfo(object): |
+ |
+ # m from a.m/p.m, t from ISO T separator |
+ JUMP = [" ", ".", ",", ";", "-", "/", "'", |
+ "at", "on", "and", "ad", "m", "t", "of", |
+ "st", "nd", "rd", "th"] |
+ |
+ WEEKDAYS = [("Mon", "Monday"), |
+ ("Tue", "Tuesday"), |
+ ("Wed", "Wednesday"), |
+ ("Thu", "Thursday"), |
+ ("Fri", "Friday"), |
+ ("Sat", "Saturday"), |
+ ("Sun", "Sunday")] |
+ MONTHS = [("Jan", "January"), |
+ ("Feb", "February"), |
+ ("Mar", "March"), |
+ ("Apr", "April"), |
+ ("May", "May"), |
+ ("Jun", "June"), |
+ ("Jul", "July"), |
+ ("Aug", "August"), |
+ ("Sep", "September"), |
+ ("Oct", "October"), |
+ ("Nov", "November"), |
+ ("Dec", "December")] |
+ HMS = [("h", "hour", "hours"), |
+ ("m", "minute", "minutes"), |
+ ("s", "second", "seconds")] |
+ AMPM = [("am", "a"), |
+ ("pm", "p")] |
+ UTCZONE = ["UTC", "GMT", "Z"] |
+ PERTAIN = ["of"] |
+ TZOFFSET = {} |
+ |
+ def __init__(self, dayfirst=False, yearfirst=False): |
+ self._jump = self._convert(self.JUMP) |
+ self._weekdays = self._convert(self.WEEKDAYS) |
+ self._months = self._convert(self.MONTHS) |
+ self._hms = self._convert(self.HMS) |
+ self._ampm = self._convert(self.AMPM) |
+ self._utczone = self._convert(self.UTCZONE) |
+ self._pertain = self._convert(self.PERTAIN) |
+ |
+ self.dayfirst = dayfirst |
+ self.yearfirst = yearfirst |
+ |
+ self._year = time.localtime().tm_year |
+ self._century = self._year//100*100 |
+ |
+ def _convert(self, lst): |
+ dct = {} |
+ for i in range(len(lst)): |
+ v = lst[i] |
+ if isinstance(v, tuple): |
+ for v in v: |
+ dct[v.lower()] = i |
+ else: |
+ dct[v.lower()] = i |
+ return dct |
+ |
+ def jump(self, name): |
+ return name.lower() in self._jump |
+ |
+ def weekday(self, name): |
+ if len(name) >= 3: |
+ try: |
+ return self._weekdays[name.lower()] |
+ except KeyError: |
+ pass |
+ return None |
+ |
+ def month(self, name): |
+ if len(name) >= 3: |
+ try: |
+ return self._months[name.lower()]+1 |
+ except KeyError: |
+ pass |
+ return None |
+ |
+ def hms(self, name): |
+ try: |
+ return self._hms[name.lower()] |
+ except KeyError: |
+ return None |
+ |
+ def ampm(self, name): |
+ try: |
+ return self._ampm[name.lower()] |
+ except KeyError: |
+ return None |
+ |
+ def pertain(self, name): |
+ return name.lower() in self._pertain |
+ |
+ def utczone(self, name): |
+ return name.lower() in self._utczone |
+ |
+ def tzoffset(self, name): |
+ if name in self._utczone: |
+ return 0 |
+ return self.TZOFFSET.get(name) |
+ |
+ def convertyear(self, year): |
+ if year < 100: |
+ year += self._century |
+ if abs(year-self._year) >= 50: |
+ if year < self._year: |
+ year += 100 |
+ else: |
+ year -= 100 |
+ return year |
+ |
+ def validate(self, res): |
+ # move to info |
+ if res.year is not None: |
+ res.year = self.convertyear(res.year) |
+ if res.tzoffset == 0 and not res.tzname or res.tzname == 'Z': |
+ res.tzname = "UTC" |
+ res.tzoffset = 0 |
+ elif res.tzoffset != 0 and res.tzname and self.utczone(res.tzname): |
+ res.tzoffset = 0 |
+ return True |
+ |
+ |
+class parser(object): |
+ |
+ def __init__(self, info=None): |
+ self.info = info or parserinfo() |
+ |
+ def parse(self, timestr, default=None, |
+ ignoretz=False, tzinfos=None, |
+ **kwargs): |
+ if not default: |
+ default = datetime.datetime.now().replace(hour=0, minute=0, |
+ second=0, microsecond=0) |
+ res = self._parse(timestr, **kwargs) |
+ if res is None: |
+ raise ValueError, "unknown string format" |
+ repl = {} |
+ for attr in ["year", "month", "day", "hour", |
+ "minute", "second", "microsecond"]: |
+ value = getattr(res, attr) |
+ if value is not None: |
+ repl[attr] = value |
+ ret = default.replace(**repl) |
+ if res.weekday is not None and not res.day: |
+ ret = ret+relativedelta.relativedelta(weekday=res.weekday) |
+ if not ignoretz: |
+ if callable(tzinfos) or tzinfos and res.tzname in tzinfos: |
+ if callable(tzinfos): |
+ tzdata = tzinfos(res.tzname, res.tzoffset) |
+ else: |
+ tzdata = tzinfos.get(res.tzname) |
+ if isinstance(tzdata, datetime.tzinfo): |
+ tzinfo = tzdata |
+ elif isinstance(tzdata, basestring): |
+ tzinfo = tz.tzstr(tzdata) |
+ elif isinstance(tzdata, int): |
+ tzinfo = tz.tzoffset(res.tzname, tzdata) |
+ else: |
+ raise ValueError, "offset must be tzinfo subclass, " \ |
+ "tz string, or int offset" |
+ ret = ret.replace(tzinfo=tzinfo) |
+ elif res.tzname and res.tzname in time.tzname: |
+ ret = ret.replace(tzinfo=tz.tzlocal()) |
+ elif res.tzoffset == 0: |
+ ret = ret.replace(tzinfo=tz.tzutc()) |
+ elif res.tzoffset: |
+ ret = ret.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset)) |
+ return ret |
+ |
+ class _result(_resultbase): |
+ __slots__ = ["year", "month", "day", "weekday", |
+ "hour", "minute", "second", "microsecond", |
+ "tzname", "tzoffset"] |
+ |
+ def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False): |
+ info = self.info |
+ if dayfirst is None: |
+ dayfirst = info.dayfirst |
+ if yearfirst is None: |
+ yearfirst = info.yearfirst |
+ res = self._result() |
+ l = _timelex.split(timestr) |
+ try: |
+ |
+ # year/month/day list |
+ ymd = [] |
+ |
+ # Index of the month string in ymd |
+ mstridx = -1 |
+ |
+ len_l = len(l) |
+ i = 0 |
+ while i < len_l: |
+ |
+ # Check if it's a number |
+ try: |
+ value_repr = l[i] |
+ value = float(value_repr) |
+ except ValueError: |
+ value = None |
+ |
+ if value is not None: |
+ # Token is a number |
+ len_li = len(l[i]) |
+ i += 1 |
+ if (len(ymd) == 3 and len_li in (2, 4) |
+ and (i >= len_l or (l[i] != ':' and |
+ info.hms(l[i]) is None))): |
+ # 19990101T23[59] |
+ s = l[i-1] |
+ res.hour = int(s[:2]) |
+ if len_li == 4: |
+ res.minute = int(s[2:]) |
+ elif len_li == 6 or (len_li > 6 and l[i-1].find('.') == 6): |
+ # YYMMDD or HHMMSS[.ss] |
+ s = l[i-1] |
+ if not ymd and l[i-1].find('.') == -1: |
+ ymd.append(info.convertyear(int(s[:2]))) |
+ ymd.append(int(s[2:4])) |
+ ymd.append(int(s[4:])) |
+ else: |
+ # 19990101T235959[.59] |
+ res.hour = int(s[:2]) |
+ res.minute = int(s[2:4]) |
+ res.second, res.microsecond = _parsems(s[4:]) |
+ elif len_li == 8: |
+ # YYYYMMDD |
+ s = l[i-1] |
+ ymd.append(int(s[:4])) |
+ ymd.append(int(s[4:6])) |
+ ymd.append(int(s[6:])) |
+ elif len_li in (12, 14): |
+ # YYYYMMDDhhmm[ss] |
+ s = l[i-1] |
+ ymd.append(int(s[:4])) |
+ ymd.append(int(s[4:6])) |
+ ymd.append(int(s[6:8])) |
+ res.hour = int(s[8:10]) |
+ res.minute = int(s[10:12]) |
+ if len_li == 14: |
+ res.second = int(s[12:]) |
+ elif ((i < len_l and info.hms(l[i]) is not None) or |
+ (i+1 < len_l and l[i] == ' ' and |
+ info.hms(l[i+1]) is not None)): |
+ # HH[ ]h or MM[ ]m or SS[.ss][ ]s |
+ if l[i] == ' ': |
+ i += 1 |
+ idx = info.hms(l[i]) |
+ while True: |
+ if idx == 0: |
+ res.hour = int(value) |
+ if value%1: |
+ res.minute = int(60*(value%1)) |
+ elif idx == 1: |
+ res.minute = int(value) |
+ if value%1: |
+ res.second = int(60*(value%1)) |
+ elif idx == 2: |
+ res.second, res.microsecond = \ |
+ _parsems(value_repr) |
+ i += 1 |
+ if i >= len_l or idx == 2: |
+ break |
+ # 12h00 |
+ try: |
+ value_repr = l[i] |
+ value = float(value_repr) |
+ except ValueError: |
+ break |
+ else: |
+ i += 1 |
+ idx += 1 |
+ if i < len_l: |
+ newidx = info.hms(l[i]) |
+ if newidx is not None: |
+ idx = newidx |
+ elif i+1 < len_l and l[i] == ':': |
+ # HH:MM[:SS[.ss]] |
+ res.hour = int(value) |
+ i += 1 |
+ value = float(l[i]) |
+ res.minute = int(value) |
+ if value%1: |
+ res.second = int(60*(value%1)) |
+ i += 1 |
+ if i < len_l and l[i] == ':': |
+ res.second, res.microsecond = _parsems(l[i+1]) |
+ i += 2 |
+ elif i < len_l and l[i] in ('-', '/', '.'): |
+ sep = l[i] |
+ ymd.append(int(value)) |
+ i += 1 |
+ if i < len_l and not info.jump(l[i]): |
+ try: |
+ # 01-01[-01] |
+ ymd.append(int(l[i])) |
+ except ValueError: |
+ # 01-Jan[-01] |
+ value = info.month(l[i]) |
+ if value is not None: |
+ ymd.append(value) |
+ assert mstridx == -1 |
+ mstridx = len(ymd)-1 |
+ else: |
+ return None |
+ i += 1 |
+ if i < len_l and l[i] == sep: |
+ # We have three members |
+ i += 1 |
+ value = info.month(l[i]) |
+ if value is not None: |
+ ymd.append(value) |
+ mstridx = len(ymd)-1 |
+ assert mstridx == -1 |
+ else: |
+ ymd.append(int(l[i])) |
+ i += 1 |
+ elif i >= len_l or info.jump(l[i]): |
+ if i+1 < len_l and info.ampm(l[i+1]) is not None: |
+ # 12 am |
+ res.hour = int(value) |
+ if res.hour < 12 and info.ampm(l[i+1]) == 1: |
+ res.hour += 12 |
+ elif res.hour == 12 and info.ampm(l[i+1]) == 0: |
+ res.hour = 0 |
+ i += 1 |
+ else: |
+ # Year, month or day |
+ ymd.append(int(value)) |
+ i += 1 |
+ elif info.ampm(l[i]) is not None: |
+ # 12am |
+ res.hour = int(value) |
+ if res.hour < 12 and info.ampm(l[i]) == 1: |
+ res.hour += 12 |
+ elif res.hour == 12 and info.ampm(l[i]) == 0: |
+ res.hour = 0 |
+ i += 1 |
+ elif not fuzzy: |
+ return None |
+ else: |
+ i += 1 |
+ continue |
+ |
+ # Check weekday |
+ value = info.weekday(l[i]) |
+ if value is not None: |
+ res.weekday = value |
+ i += 1 |
+ continue |
+ |
+ # Check month name |
+ value = info.month(l[i]) |
+ if value is not None: |
+ ymd.append(value) |
+ assert mstridx == -1 |
+ mstridx = len(ymd)-1 |
+ i += 1 |
+ if i < len_l: |
+ if l[i] in ('-', '/'): |
+ # Jan-01[-99] |
+ sep = l[i] |
+ i += 1 |
+ ymd.append(int(l[i])) |
+ i += 1 |
+ if i < len_l and l[i] == sep: |
+ # Jan-01-99 |
+ i += 1 |
+ ymd.append(int(l[i])) |
+ i += 1 |
+ elif (i+3 < len_l and l[i] == l[i+2] == ' ' |
+ and info.pertain(l[i+1])): |
+ # Jan of 01 |
+ # In this case, 01 is clearly year |
+ try: |
+ value = int(l[i+3]) |
+ except ValueError: |
+ # Wrong guess |
+ pass |
+ else: |
+ # Convert it here to become unambiguous |
+ ymd.append(info.convertyear(value)) |
+ i += 4 |
+ continue |
+ |
+ # Check am/pm |
+ value = info.ampm(l[i]) |
+ if value is not None: |
+ if value == 1 and res.hour < 12: |
+ res.hour += 12 |
+ elif value == 0 and res.hour == 12: |
+ res.hour = 0 |
+ i += 1 |
+ continue |
+ |
+ # Check for a timezone name |
+ if (res.hour is not None and len(l[i]) <= 5 and |
+ res.tzname is None and res.tzoffset is None and |
+ not [x for x in l[i] if x not in string.ascii_uppercase]): |
+ res.tzname = l[i] |
+ res.tzoffset = info.tzoffset(res.tzname) |
+ i += 1 |
+ |
+ # Check for something like GMT+3, or BRST+3. Notice |
+ # that it doesn't mean "I am 3 hours after GMT", but |
+ # "my time +3 is GMT". If found, we reverse the |
+ # logic so that timezone parsing code will get it |
+ # right. |
+ if i < len_l and l[i] in ('+', '-'): |
+ l[i] = ('+', '-')[l[i] == '+'] |
+ res.tzoffset = None |
+ if info.utczone(res.tzname): |
+ # With something like GMT+3, the timezone |
+ # is *not* GMT. |
+ res.tzname = None |
+ |
+ continue |
+ |
+ # Check for a numbered timezone |
+ if res.hour is not None and l[i] in ('+', '-'): |
+ signal = (-1,1)[l[i] == '+'] |
+ i += 1 |
+ len_li = len(l[i]) |
+ if len_li == 4: |
+ # -0300 |
+ res.tzoffset = int(l[i][:2])*3600+int(l[i][2:])*60 |
+ elif i+1 < len_l and l[i+1] == ':': |
+ # -03:00 |
+ res.tzoffset = int(l[i])*3600+int(l[i+2])*60 |
+ i += 2 |
+ elif len_li <= 2: |
+ # -[0]3 |
+ res.tzoffset = int(l[i][:2])*3600 |
+ else: |
+ return None |
+ i += 1 |
+ res.tzoffset *= signal |
+ |
+ # Look for a timezone name between parenthesis |
+ if (i+3 < len_l and |
+ info.jump(l[i]) and l[i+1] == '(' and l[i+3] == ')' and |
+ 3 <= len(l[i+2]) <= 5 and |
+ not [x for x in l[i+2] |
+ if x not in string.ascii_uppercase]): |
+ # -0300 (BRST) |
+ res.tzname = l[i+2] |
+ i += 4 |
+ continue |
+ |
+ # Check jumps |
+ if not (info.jump(l[i]) or fuzzy): |
+ return None |
+ |
+ i += 1 |
+ |
+ # Process year/month/day |
+ len_ymd = len(ymd) |
+ if len_ymd > 3: |
+ # More than three members!? |
+ return None |
+ elif len_ymd == 1 or (mstridx != -1 and len_ymd == 2): |
+ # One member, or two members with a month string |
+ if mstridx != -1: |
+ res.month = ymd[mstridx] |
+ del ymd[mstridx] |
+ if len_ymd > 1 or mstridx == -1: |
+ if ymd[0] > 31: |
+ res.year = ymd[0] |
+ else: |
+ res.day = ymd[0] |
+ elif len_ymd == 2: |
+ # Two members with numbers |
+ if ymd[0] > 31: |
+ # 99-01 |
+ res.year, res.month = ymd |
+ elif ymd[1] > 31: |
+ # 01-99 |
+ res.month, res.year = ymd |
+ elif dayfirst and ymd[1] <= 12: |
+ # 13-01 |
+ res.day, res.month = ymd |
+ else: |
+ # 01-13 |
+ res.month, res.day = ymd |
+ if len_ymd == 3: |
+ # Three members |
+ if mstridx == 0: |
+ res.month, res.day, res.year = ymd |
+ elif mstridx == 1: |
+ if ymd[0] > 31 or (yearfirst and ymd[2] <= 31): |
+ # 99-Jan-01 |
+ res.year, res.month, res.day = ymd |
+ else: |
+ # 01-Jan-01 |
+ # Give precendence to day-first, since |
+ # two-digit years is usually hand-written. |
+ res.day, res.month, res.year = ymd |
+ elif mstridx == 2: |
+ # WTF!? |
+ if ymd[1] > 31: |
+ # 01-99-Jan |
+ res.day, res.year, res.month = ymd |
+ else: |
+ # 99-01-Jan |
+ res.year, res.day, res.month = ymd |
+ else: |
+ if ymd[0] > 31 or \ |
+ (yearfirst and ymd[1] <= 12 and ymd[2] <= 31): |
+ # 99-01-01 |
+ res.year, res.month, res.day = ymd |
+ elif ymd[0] > 12 or (dayfirst and ymd[1] <= 12): |
+ # 13-01-01 |
+ res.day, res.month, res.year = ymd |
+ else: |
+ # 01-13-01 |
+ res.month, res.day, res.year = ymd |
+ |
+ except (IndexError, ValueError, AssertionError): |
+ return None |
+ |
+ if not info.validate(res): |
+ return None |
+ return res |
+ |
+DEFAULTPARSER = parser() |
+def parse(timestr, parserinfo=None, **kwargs): |
+ if parserinfo: |
+ return parser(parserinfo).parse(timestr, **kwargs) |
+ else: |
+ return DEFAULTPARSER.parse(timestr, **kwargs) |
+ |
+ |
+class _tzparser(object): |
+ |
+ class _result(_resultbase): |
+ |
+ __slots__ = ["stdabbr", "stdoffset", "dstabbr", "dstoffset", |
+ "start", "end"] |
+ |
+ class _attr(_resultbase): |
+ __slots__ = ["month", "week", "weekday", |
+ "yday", "jyday", "day", "time"] |
+ |
+ def __repr__(self): |
+ return self._repr("") |
+ |
+ def __init__(self): |
+ _resultbase.__init__(self) |
+ self.start = self._attr() |
+ self.end = self._attr() |
+ |
+ def parse(self, tzstr): |
+ res = self._result() |
+ l = _timelex.split(tzstr) |
+ try: |
+ |
+ len_l = len(l) |
+ |
+ i = 0 |
+ while i < len_l: |
+ # BRST+3[BRDT[+2]] |
+ j = i |
+ while j < len_l and not [x for x in l[j] |
+ if x in "0123456789:,-+"]: |
+ j += 1 |
+ if j != i: |
+ if not res.stdabbr: |
+ offattr = "stdoffset" |
+ res.stdabbr = "".join(l[i:j]) |
+ else: |
+ offattr = "dstoffset" |
+ res.dstabbr = "".join(l[i:j]) |
+ i = j |
+ if (i < len_l and |
+ (l[i] in ('+', '-') or l[i][0] in "0123456789")): |
+ if l[i] in ('+', '-'): |
+ # Yes, that's right. See the TZ variable |
+ # documentation. |
+ signal = (1,-1)[l[i] == '+'] |
+ i += 1 |
+ else: |
+ signal = -1 |
+ len_li = len(l[i]) |
+ if len_li == 4: |
+ # -0300 |
+ setattr(res, offattr, |
+ (int(l[i][:2])*3600+int(l[i][2:])*60)*signal) |
+ elif i+1 < len_l and l[i+1] == ':': |
+ # -03:00 |
+ setattr(res, offattr, |
+ (int(l[i])*3600+int(l[i+2])*60)*signal) |
+ i += 2 |
+ elif len_li <= 2: |
+ # -[0]3 |
+ setattr(res, offattr, |
+ int(l[i][:2])*3600*signal) |
+ else: |
+ return None |
+ i += 1 |
+ if res.dstabbr: |
+ break |
+ else: |
+ break |
+ |
+ if i < len_l: |
+ for j in range(i, len_l): |
+ if l[j] == ';': l[j] = ',' |
+ |
+ assert l[i] == ',' |
+ |
+ i += 1 |
+ |
+ if i >= len_l: |
+ pass |
+ elif (8 <= l.count(',') <= 9 and |
+ not [y for x in l[i:] if x != ',' |
+ for y in x if y not in "0123456789"]): |
+ # GMT0BST,3,0,30,3600,10,0,26,7200[,3600] |
+ for x in (res.start, res.end): |
+ x.month = int(l[i]) |
+ i += 2 |
+ if l[i] == '-': |
+ value = int(l[i+1])*-1 |
+ i += 1 |
+ else: |
+ value = int(l[i]) |
+ i += 2 |
+ if value: |
+ x.week = value |
+ x.weekday = (int(l[i])-1)%7 |
+ else: |
+ x.day = int(l[i]) |
+ i += 2 |
+ x.time = int(l[i]) |
+ i += 2 |
+ if i < len_l: |
+ if l[i] in ('-','+'): |
+ signal = (-1,1)[l[i] == "+"] |
+ i += 1 |
+ else: |
+ signal = 1 |
+ res.dstoffset = (res.stdoffset+int(l[i]))*signal |
+ elif (l.count(',') == 2 and l[i:].count('/') <= 2 and |
+ not [y for x in l[i:] if x not in (',','/','J','M', |
+ '.','-',':') |
+ for y in x if y not in "0123456789"]): |
+ for x in (res.start, res.end): |
+ if l[i] == 'J': |
+ # non-leap year day (1 based) |
+ i += 1 |
+ x.jyday = int(l[i]) |
+ elif l[i] == 'M': |
+ # month[-.]week[-.]weekday |
+ i += 1 |
+ x.month = int(l[i]) |
+ i += 1 |
+ assert l[i] in ('-', '.') |
+ i += 1 |
+ x.week = int(l[i]) |
+ if x.week == 5: |
+ x.week = -1 |
+ i += 1 |
+ assert l[i] in ('-', '.') |
+ i += 1 |
+ x.weekday = (int(l[i])-1)%7 |
+ else: |
+ # year day (zero based) |
+ x.yday = int(l[i])+1 |
+ |
+ i += 1 |
+ |
+ if i < len_l and l[i] == '/': |
+ i += 1 |
+ # start time |
+ len_li = len(l[i]) |
+ if len_li == 4: |
+ # -0300 |
+ x.time = (int(l[i][:2])*3600+int(l[i][2:])*60) |
+ elif i+1 < len_l and l[i+1] == ':': |
+ # -03:00 |
+ x.time = int(l[i])*3600+int(l[i+2])*60 |
+ i += 2 |
+ if i+1 < len_l and l[i+1] == ':': |
+ i += 2 |
+ x.time += int(l[i]) |
+ elif len_li <= 2: |
+ # -[0]3 |
+ x.time = (int(l[i][:2])*3600) |
+ else: |
+ return None |
+ i += 1 |
+ |
+ assert i == len_l or l[i] == ',' |
+ |
+ i += 1 |
+ |
+ assert i >= len_l |
+ |
+ except (IndexError, ValueError, AssertionError): |
+ return None |
+ |
+ return res |
+ |
+ |
+DEFAULTTZPARSER = _tzparser() |
+def _parsetz(tzstr): |
+ return DEFAULTTZPARSER.parse(tzstr) |
+ |
+ |
+def _parsems(value): |
+ """Parse a I[.F] seconds value into (seconds, microseconds).""" |
+ if "." not in value: |
+ return int(value), 0 |
+ else: |
+ i, f = value.split(".") |
+ return int(i), int(f.ljust(6, "0")[:6]) |
+ |
+ |
+# vim:ts=4:sw=4:et |