| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886 |
- # -*- 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
|