NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

tz.py (29123B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\dateutil\tz\tz.py
      2 import datetime
      3 import struct
      4 import time
      5 import sys
      6 import os
      7 import bisect
      8 import copy
      9 from operator import itemgetter
     10 from contextlib import contextmanager
     11 from six import string_types, PY3
     12 from ._common import tzname_in_python2, _tzinfo, _total_seconds
     13 from ._common import tzrangebase, enfold
     14 try:
     15     from .win import tzwin, tzwinlocal
     16 except ImportError:
     17     tzwin = tzwinlocal = None
     18 
     19 ZERO = datetime.timedelta(0)
     20 EPOCH = datetime.datetime.utcfromtimestamp(0)
     21 EPOCHORDINAL = EPOCH.toordinal()
     22 
     23 class tzutc(datetime.tzinfo):
     24 
     25     def utcoffset(self, dt):
     26         return ZERO
     27 
     28     def dst(self, dt):
     29         return ZERO
     30 
     31     @tzname_in_python2
     32     def tzname(self, dt):
     33         return 'UTC'
     34 
     35     def is_ambiguous(self, dt):
     36         return False
     37 
     38     def __eq__(self, other):
     39         if not isinstance(other, (tzutc, tzoffset)):
     40             return NotImplemented
     41         return isinstance(other, tzutc) or isinstance(other, tzoffset) and other._offset == ZERO
     42 
     43     __hash__ = None
     44 
     45     def __ne__(self, other):
     46         return not self == other
     47 
     48     def __repr__(self):
     49         return '%s()' % self.__class__.__name__
     50 
     51     __reduce__ = object.__reduce__
     52 
     53 
     54 class tzoffset(datetime.tzinfo):
     55 
     56     def __init__(self, name, offset):
     57         self._name = name
     58         try:
     59             offset = _total_seconds(offset)
     60         except (TypeError, AttributeError):
     61             pass
     62 
     63         self._offset = datetime.timedelta(seconds=offset)
     64 
     65     def utcoffset(self, dt):
     66         return self._offset
     67 
     68     def dst(self, dt):
     69         return ZERO
     70 
     71     def is_ambiguous(self, dt):
     72         return False
     73 
     74     @tzname_in_python2
     75     def tzname(self, dt):
     76         return self._name
     77 
     78     def __eq__(self, other):
     79         if not isinstance(other, tzoffset):
     80             return NotImplemented
     81         return self._offset == other._offset
     82 
     83     __hash__ = None
     84 
     85     def __ne__(self, other):
     86         return not self == other
     87 
     88     def __repr__(self):
     89         return '%s(%s, %s)' % (self.__class__.__name__, repr(self._name), int(_total_seconds(self._offset)))
     90 
     91     __reduce__ = object.__reduce__
     92 
     93 
     94 class tzlocal(_tzinfo):
     95 
     96     def __init__(self):
     97         super(tzlocal, self).__init__()
     98         self._std_offset = datetime.timedelta(seconds=-time.timezone)
     99         if time.daylight:
    100             self._dst_offset = datetime.timedelta(seconds=-time.altzone)
    101         else:
    102             self._dst_offset = self._std_offset
    103         self._dst_saved = self._dst_offset - self._std_offset
    104         self._hasdst = bool(self._dst_saved)
    105 
    106     def utcoffset(self, dt):
    107         if dt is None and self._hasdst:
    108             return
    109         elif self._isdst(dt):
    110             return self._dst_offset
    111         else:
    112             return self._std_offset
    113 
    114     def dst(self, dt):
    115         if dt is None and self._hasdst:
    116             return
    117         elif self._isdst(dt):
    118             return self._dst_offset - self._std_offset
    119         else:
    120             return ZERO
    121 
    122     @tzname_in_python2
    123     def tzname(self, dt):
    124         return time.tzname[self._isdst(dt)]
    125 
    126     def is_ambiguous(self, dt):
    127         naive_dst = self._naive_is_dst(dt)
    128         return not naive_dst and naive_dst != self._naive_is_dst(dt - self._dst_saved)
    129 
    130     def _naive_is_dst(self, dt):
    131         timestamp = _datetime_to_timestamp(dt)
    132         return time.localtime(timestamp + time.timezone).tm_isdst
    133 
    134     def _isdst(self, dt, fold_naive = True):
    135         if not self._hasdst:
    136             return False
    137         dstval = self._naive_is_dst(dt)
    138         fold = getattr(dt, 'fold', None)
    139         if self.is_ambiguous(dt):
    140             if fold is not None:
    141                 return not self._fold(dt)
    142             else:
    143                 return True
    144         return dstval
    145 
    146     def __eq__(self, other):
    147         if not isinstance(other, tzlocal):
    148             return NotImplemented
    149         return self._std_offset == other._std_offset and self._dst_offset == other._dst_offset
    150 
    151     __hash__ = None
    152 
    153     def __ne__(self, other):
    154         return not self == other
    155 
    156     def __repr__(self):
    157         return '%s()' % self.__class__.__name__
    158 
    159     __reduce__ = object.__reduce__
    160 
    161 
    162 class _ttinfo(object):
    163     __slots__ = ['offset',
    164      'delta',
    165      'isdst',
    166      'abbr',
    167      'isstd',
    168      'isgmt',
    169      'dstoffset']
    170 
    171     def __init__(self):
    172         for attr in self.__slots__:
    173             setattr(self, attr, None)
    174 
    175     def __repr__(self):
    176         l = []
    177         for attr in self.__slots__:
    178             value = getattr(self, attr)
    179             if value is not None:
    180                 l.append('%s=%s' % (attr, repr(value)))
    181 
    182         return '%s(%s)' % (self.__class__.__name__, ', '.join(l))
    183 
    184     def __eq__(self, other):
    185         if not isinstance(other, _ttinfo):
    186             return NotImplemented
    187         return self.offset == other.offset and self.delta == other.delta and self.isdst == other.isdst and self.abbr == other.abbr and self.isstd == other.isstd and self.isgmt == other.isgmt and self.dstoffset == other.dstoffset
    188 
    189     __hash__ = None
    190 
    191     def __ne__(self, other):
    192         return not self == other
    193 
    194     def __getstate__(self):
    195         state = {}
    196         for name in self.__slots__:
    197             state[name] = getattr(self, name, None)
    198 
    199         return state
    200 
    201     def __setstate__(self, state):
    202         for name in self.__slots__:
    203             if name in state:
    204                 setattr(self, name, state[name])
    205 
    206 
    207 class _tzfile(object):
    208     attrs = ['trans_list',
    209      'trans_idx',
    210      'ttinfo_list',
    211      'ttinfo_std',
    212      'ttinfo_dst',
    213      'ttinfo_before',
    214      'ttinfo_first']
    215 
    216     def __init__(self, **kwargs):
    217         for attr in self.attrs:
    218             setattr(self, attr, kwargs.get(attr, None))
    219 
    220 
    221 class tzfile(_tzinfo):
    222 
    223     def __init__(self, fileobj, filename = None):
    224         super(tzfile, self).__init__()
    225         file_opened_here = False
    226         if isinstance(fileobj, string_types):
    227             self._filename = fileobj
    228             fileobj = open(fileobj, 'rb')
    229             file_opened_here = True
    230         elif filename is not None:
    231             self._filename = filename
    232         elif hasattr(fileobj, 'name'):
    233             self._filename = fileobj.name
    234         else:
    235             self._filename = repr(fileobj)
    236         if fileobj is not None:
    237             if not file_opened_here:
    238                 fileobj = _ContextWrapper(fileobj)
    239             with fileobj as file_stream:
    240                 tzobj = self._read_tzfile(file_stream)
    241             self._set_tzdata(tzobj)
    242 
    243     def _set_tzdata(self, tzobj):
    244         for attr in _tzfile.attrs:
    245             setattr(self, '_' + attr, getattr(tzobj, attr))
    246 
    247     def _read_tzfile(self, fileobj):
    248         out = _tzfile()
    249         if fileobj.read(4).decode() != 'TZif':
    250             raise ValueError('magic not found')
    251         fileobj.read(16)
    252         ttisgmtcnt, ttisstdcnt, leapcnt, timecnt, typecnt, charcnt = struct.unpack('>6l', fileobj.read(24))
    253         if timecnt:
    254             out.trans_list = list(struct.unpack('>%dl' % timecnt, fileobj.read(timecnt * 4)))
    255         else:
    256             out.trans_list = []
    257         if timecnt:
    258             out.trans_idx = struct.unpack('>%dB' % timecnt, fileobj.read(timecnt))
    259         else:
    260             out.trans_idx = []
    261         ttinfo = []
    262         for i in range(typecnt):
    263             ttinfo.append(struct.unpack('>lbb', fileobj.read(6)))
    264 
    265         abbr = fileobj.read(charcnt).decode()
    266         if leapcnt:
    267             leap = struct.unpack('>%dl' % (leapcnt * 2), fileobj.read(leapcnt * 8))
    268         if ttisstdcnt:
    269             isstd = struct.unpack('>%db' % ttisstdcnt, fileobj.read(ttisstdcnt))
    270         if ttisgmtcnt:
    271             isgmt = struct.unpack('>%db' % ttisgmtcnt, fileobj.read(ttisgmtcnt))
    272         out.ttinfo_list = []
    273         for i in range(typecnt):
    274             gmtoff, isdst, abbrind = ttinfo[i]
    275             gmtoff = 60 * ((gmtoff + 30) // 60)
    276             tti = _ttinfo()
    277             tti.offset = gmtoff
    278             tti.dstoffset = datetime.timedelta(0)
    279             tti.delta = datetime.timedelta(seconds=gmtoff)
    280             tti.isdst = isdst
    281             tti.abbr = abbr[abbrind:abbr.find('\x00', abbrind)]
    282             tti.isstd = ttisstdcnt > i and isstd[i] != 0
    283             tti.isgmt = ttisgmtcnt > i and isgmt[i] != 0
    284             out.ttinfo_list.append(tti)
    285 
    286         out.trans_idx = [ out.ttinfo_list[idx] for idx in out.trans_idx ]
    287         out.ttinfo_std = None
    288         out.ttinfo_dst = None
    289         out.ttinfo_before = None
    290         if out.ttinfo_list:
    291             if not out.trans_list:
    292                 out.ttinfo_std = out.ttinfo_first = out.ttinfo_list[0]
    293             else:
    294                 for i in range(timecnt - 1, -1, -1):
    295                     tti = out.trans_idx[i]
    296                     if not out.ttinfo_std and not tti.isdst:
    297                         out.ttinfo_std = tti
    298                     elif not out.ttinfo_dst and tti.isdst:
    299                         out.ttinfo_dst = tti
    300                     if out.ttinfo_std and out.ttinfo_dst:
    301                         break
    302                 else:
    303                     if out.ttinfo_dst and not out.ttinfo_std:
    304                         out.ttinfo_std = out.ttinfo_dst
    305 
    306                 for tti in out.ttinfo_list:
    307                     if not tti.isdst:
    308                         out.ttinfo_before = tti
    309                         break
    310                 else:
    311                     out.ttinfo_before = out.ttinfo_list[0]
    312 
    313         laststdoffset = None
    314         for i, tti in enumerate(out.trans_idx):
    315             if not tti.isdst:
    316                 offset = tti.offset
    317                 laststdoffset = offset
    318             else:
    319                 if laststdoffset is not None:
    320                     tti.dstoffset = tti.offset - laststdoffset
    321                     out.trans_idx[i] = tti
    322                 offset = laststdoffset or 0
    323             out.trans_list[i] += offset
    324 
    325         laststdoffset = None
    326         for i in reversed(range(len(out.trans_idx))):
    327             tti = out.trans_idx[i]
    328             if tti.isdst:
    329                 if not (tti.dstoffset or laststdoffset is None):
    330                     tti.dstoffset = tti.offset - laststdoffset
    331             else:
    332                 laststdoffset = tti.offset
    333             if not isinstance(tti.dstoffset, datetime.timedelta):
    334                 tti.dstoffset = datetime.timedelta(seconds=tti.dstoffset)
    335             out.trans_idx[i] = tti
    336 
    337         out.trans_idx = tuple(out.trans_idx)
    338         out.trans_list = tuple(out.trans_list)
    339         return out
    340 
    341     def _find_last_transition(self, dt):
    342         if not self._trans_list:
    343             return None
    344         timestamp = _datetime_to_timestamp(dt)
    345         idx = bisect.bisect_right(self._trans_list, timestamp)
    346         return idx - 1
    347 
    348     def _get_ttinfo(self, idx):
    349         if idx is None or idx + 1 == len(self._trans_list):
    350             return self._ttinfo_std
    351         if idx < 0:
    352             return self._ttinfo_before
    353         return self._trans_idx[idx]
    354 
    355     def _find_ttinfo(self, dt):
    356         idx = self._resolve_ambiguous_time(dt)
    357         return self._get_ttinfo(idx)
    358 
    359     def is_ambiguous(self, dt, idx = None):
    360         if idx is None:
    361             idx = self._find_last_transition(dt)
    362         timestamp = _datetime_to_timestamp(dt)
    363         tti = self._get_ttinfo(idx)
    364         if idx is None or idx <= 0:
    365             return False
    366         od = self._get_ttinfo(idx - 1).offset - tti.offset
    367         tt = self._trans_list[idx]
    368         return timestamp < tt + od
    369 
    370     def _resolve_ambiguous_time(self, dt):
    371         idx = self._find_last_transition(dt)
    372         _fold = self._fold(dt)
    373         if idx is None or idx == 0:
    374             return idx
    375         idx_offset = int(not _fold and self.is_ambiguous(dt, idx))
    376         return idx - idx_offset
    377 
    378     def utcoffset(self, dt):
    379         if dt is None:
    380             return
    381         if not self._ttinfo_std:
    382             return ZERO
    383         return self._find_ttinfo(dt).delta
    384 
    385     def dst(self, dt):
    386         if dt is None:
    387             return
    388         if not self._ttinfo_dst:
    389             return ZERO
    390         tti = self._find_ttinfo(dt)
    391         if not tti.isdst:
    392             return ZERO
    393         return tti.dstoffset
    394 
    395     @tzname_in_python2
    396     def tzname(self, dt):
    397         if not self._ttinfo_std or dt is None:
    398             return
    399         return self._find_ttinfo(dt).abbr
    400 
    401     def __eq__(self, other):
    402         if not isinstance(other, tzfile):
    403             return NotImplemented
    404         return self._trans_list == other._trans_list and self._trans_idx == other._trans_idx and self._ttinfo_list == other._ttinfo_list
    405 
    406     __hash__ = None
    407 
    408     def __ne__(self, other):
    409         return not self == other
    410 
    411     def __repr__(self):
    412         return '%s(%s)' % (self.__class__.__name__, repr(self._filename))
    413 
    414     def __reduce__(self):
    415         return self.__reduce_ex__(None)
    416 
    417     def __reduce_ex__(self, protocol):
    418         return (self.__class__, (None, self._filename), self.__dict__)
    419 
    420 
    421 class tzrange(tzrangebase):
    422 
    423     def __init__(self, stdabbr, stdoffset = None, dstabbr = None, dstoffset = None, start = None, end = None):
    424         global relativedelta
    425         from dateutil import relativedelta
    426         self._std_abbr = stdabbr
    427         self._dst_abbr = dstabbr
    428         try:
    429             stdoffset = _total_seconds(stdoffset)
    430         except (TypeError, AttributeError):
    431             pass
    432 
    433         try:
    434             dstoffset = _total_seconds(dstoffset)
    435         except (TypeError, AttributeError):
    436             pass
    437 
    438         if stdoffset is not None:
    439             self._std_offset = datetime.timedelta(seconds=stdoffset)
    440         else:
    441             self._std_offset = ZERO
    442         if dstoffset is not None:
    443             self._dst_offset = datetime.timedelta(seconds=dstoffset)
    444         elif dstabbr and stdoffset is not None:
    445             self._dst_offset = self._std_offset + datetime.timedelta(hours=+1)
    446         else:
    447             self._dst_offset = ZERO
    448         if dstabbr and start is None:
    449             self._start_delta = relativedelta.relativedelta(hours=+2, month=4, day=1, weekday=relativedelta.SU(+1))
    450         else:
    451             self._start_delta = start
    452         if dstabbr and end is None:
    453             self._end_delta = relativedelta.relativedelta(hours=+1, month=10, day=31, weekday=relativedelta.SU(-1))
    454         else:
    455             self._end_delta = end
    456         self._dst_base_offset_ = self._dst_offset - self._std_offset
    457         self.hasdst = bool(self._start_delta)
    458 
    459     def transitions(self, year):
    460         if not self.hasdst:
    461             return None
    462         base_year = datetime.datetime(year, 1, 1)
    463         start = base_year + self._start_delta
    464         end = base_year + self._end_delta
    465         return (start, end)
    466 
    467     def __eq__(self, other):
    468         if not isinstance(other, tzrange):
    469             return NotImplemented
    470         return self._std_abbr == other._std_abbr and self._dst_abbr == other._dst_abbr and self._std_offset == other._std_offset and self._dst_offset == other._dst_offset and self._start_delta == other._start_delta and self._end_delta == other._end_delta
    471 
    472     @property
    473     def _dst_base_offset(self):
    474         return self._dst_base_offset_
    475 
    476 
    477 class tzstr(tzrange):
    478 
    479     def __init__(self, s, posix_offset = False):
    480         global parser
    481         from dateutil import parser
    482         self._s = s
    483         res = parser._parsetz(s)
    484         if res is None:
    485             raise ValueError('unknown string format')
    486         if res.stdabbr in ('GMT', 'UTC') and not posix_offset:
    487             res.stdoffset *= -1
    488         tzrange.__init__(self, res.stdabbr, res.stdoffset, res.dstabbr, res.dstoffset, start=False, end=False)
    489         if not res.dstabbr:
    490             self._start_delta = None
    491             self._end_delta = None
    492         else:
    493             self._start_delta = self._delta(res.start)
    494             if self._start_delta:
    495                 self._end_delta = self._delta(res.end, isend=1)
    496         self.hasdst = bool(self._start_delta)
    497 
    498     def _delta(self, x, isend = 0):
    499         from dateutil import relativedelta
    500         kwargs = {}
    501         if x.month is not None:
    502             kwargs['month'] = x.month
    503             if x.weekday is not None:
    504                 kwargs['weekday'] = relativedelta.weekday(x.weekday, x.week)
    505                 if x.week > 0:
    506                     kwargs['day'] = 1
    507                 else:
    508                     kwargs['day'] = 31
    509             elif x.day:
    510                 kwargs['day'] = x.day
    511         elif x.yday is not None:
    512             kwargs['yearday'] = x.yday
    513         elif x.jyday is not None:
    514             kwargs['nlyearday'] = x.jyday
    515         if not kwargs:
    516             if not isend:
    517                 kwargs['month'] = 4
    518                 kwargs['day'] = 1
    519                 kwargs['weekday'] = relativedelta.SU(+1)
    520             else:
    521                 kwargs['month'] = 10
    522                 kwargs['day'] = 31
    523                 kwargs['weekday'] = relativedelta.SU(-1)
    524         if x.time is not None:
    525             kwargs['seconds'] = x.time
    526         else:
    527             kwargs['seconds'] = 7200
    528         if isend:
    529             delta = self._dst_offset - self._std_offset
    530             kwargs['seconds'] -= delta.seconds + delta.days * 86400
    531         return relativedelta.relativedelta(**kwargs)
    532 
    533     def __repr__(self):
    534         return '%s(%s)' % (self.__class__.__name__, repr(self._s))
    535 
    536 
    537 class _tzicalvtzcomp(object):
    538 
    539     def __init__(self, tzoffsetfrom, tzoffsetto, isdst, tzname = None, rrule = None):
    540         self.tzoffsetfrom = datetime.timedelta(seconds=tzoffsetfrom)
    541         self.tzoffsetto = datetime.timedelta(seconds=tzoffsetto)
    542         self.tzoffsetdiff = self.tzoffsetto - self.tzoffsetfrom
    543         self.isdst = isdst
    544         self.tzname = tzname
    545         self.rrule = rrule
    546 
    547 
    548 class _tzicalvtz(_tzinfo):
    549 
    550     def __init__(self, tzid, comps = []):
    551         super(_tzicalvtz, self).__init__()
    552         self._tzid = tzid
    553         self._comps = comps
    554         self._cachedate = []
    555         self._cachecomp = []
    556 
    557     def _find_comp(self, dt):
    558         if len(self._comps) == 1:
    559             return self._comps[0]
    560         dt = dt.replace(tzinfo=None)
    561         try:
    562             return self._cachecomp[self._cachedate.index((dt, self._fold(dt)))]
    563         except ValueError:
    564             pass
    565 
    566         lastcompdt = None
    567         lastcomp = None
    568         for comp in self._comps:
    569             compdt = self._find_compdt(comp, dt)
    570             if compdt and (not lastcompdt or lastcompdt < compdt):
    571                 lastcompdt = compdt
    572                 lastcomp = comp
    573 
    574         if not lastcomp:
    575             for comp in self._comps:
    576                 if not comp.isdst:
    577                     lastcomp = comp
    578                     break
    579             else:
    580                 lastcomp = comp[0]
    581 
    582         self._cachedate.insert(0, (dt, self._fold(dt)))
    583         self._cachecomp.insert(0, lastcomp)
    584         if len(self._cachedate) > 10:
    585             self._cachedate.pop()
    586             self._cachecomp.pop()
    587         return lastcomp
    588 
    589     def _find_compdt(self, comp, dt):
    590         if comp.tzoffsetdiff < ZERO and self._fold(dt):
    591             dt -= comp.tzoffsetdiff
    592         compdt = comp.rrule.before(dt, inc=True)
    593         return compdt
    594 
    595     def utcoffset(self, dt):
    596         if dt is None:
    597             return
    598         return self._find_comp(dt).tzoffsetto
    599 
    600     def dst(self, dt):
    601         comp = self._find_comp(dt)
    602         if comp.isdst:
    603             return comp.tzoffsetdiff
    604         else:
    605             return ZERO
    606 
    607     @tzname_in_python2
    608     def tzname(self, dt):
    609         return self._find_comp(dt).tzname
    610 
    611     def __repr__(self):
    612         return '<tzicalvtz %s>' % repr(self._tzid)
    613 
    614     __reduce__ = object.__reduce__
    615 
    616 
    617 class tzical(object):
    618 
    619     def __init__(self, fileobj):
    620         global rrule
    621         from dateutil import rrule
    622         if isinstance(fileobj, string_types):
    623             self._s = fileobj
    624             fileobj = open(fileobj, 'r')
    625             file_opened_here = True
    626         else:
    627             self._s = getattr(fileobj, 'name', repr(fileobj))
    628             fileobj = _ContextWrapper(fileobj)
    629         self._vtz = {}
    630         with fileobj as fobj:
    631             self._parse_rfc(fobj.read())
    632 
    633     def keys(self):
    634         return list(self._vtz.keys())
    635 
    636     def get(self, tzid = None):
    637         if tzid is None:
    638             if len(self._vtz) == 0:
    639                 raise ValueError('no timezones defined')
    640             elif len(self._vtz) > 1:
    641                 raise ValueError('more than one timezone available')
    642             tzid = next(iter(self._vtz))
    643         return self._vtz.get(tzid)
    644 
    645     def _parse_offset(self, s):
    646         s = s.strip()
    647         if not s:
    648             raise ValueError('empty offset')
    649         if s[0] in ('+', '-'):
    650             signal = (-1, +1)[s[0] == '+']
    651             s = s[1:]
    652         else:
    653             signal = +1
    654         if len(s) == 4:
    655             return (int(s[:2]) * 3600 + int(s[2:]) * 60) * signal
    656         if len(s) == 6:
    657             return (int(s[:2]) * 3600 + int(s[2:4]) * 60 + int(s[4:])) * signal
    658         raise ValueError('invalid offset: ' + s)
    659 
    660     def _parse_rfc(self, s):
    661         lines = s.splitlines()
    662         if not lines:
    663             raise ValueError('empty string')
    664         i = 0
    665         while i < len(lines):
    666             line = lines[i].rstrip()
    667             if not line:
    668                 del lines[i]
    669             elif i > 0 and line[0] == ' ':
    670                 lines[i - 1] += line[1:]
    671                 del lines[i]
    672             else:
    673                 i += 1
    674 
    675         tzid = None
    676         comps = []
    677         invtz = False
    678         comptype = None
    679         for line in lines:
    680             if not line:
    681                 continue
    682             name, value = line.split(':', 1)
    683             parms = name.split(';')
    684             if not parms:
    685                 raise ValueError('empty property name')
    686             name = parms[0].upper()
    687             parms = parms[1:]
    688             if invtz:
    689                 if name == 'BEGIN':
    690                     if value in ('STANDARD', 'DAYLIGHT'):
    691                         pass
    692                     else:
    693                         raise ValueError('unknown component: ' + value)
    694                     comptype = value
    695                     founddtstart = False
    696                     tzoffsetfrom = None
    697                     tzoffsetto = None
    698                     rrulelines = []
    699                     tzname = None
    700                 elif name == 'END':
    701                     if value == 'VTIMEZONE':
    702                         if comptype:
    703                             raise ValueError('component not closed: ' + comptype)
    704                         if not tzid:
    705                             raise ValueError('mandatory TZID not found')
    706                         if not comps:
    707                             raise ValueError('at least one component is needed')
    708                         self._vtz[tzid] = _tzicalvtz(tzid, comps)
    709                         invtz = False
    710                     elif value == comptype:
    711                         if not founddtstart:
    712                             raise ValueError('mandatory DTSTART not found')
    713                         if tzoffsetfrom is None:
    714                             raise ValueError('mandatory TZOFFSETFROM not found')
    715                         if tzoffsetto is None:
    716                             raise ValueError('mandatory TZOFFSETFROM not found')
    717                         rr = None
    718                         if rrulelines:
    719                             rr = rrule.rrulestr('\n'.join(rrulelines), compatible=True, ignoretz=True, cache=True)
    720                         comp = _tzicalvtzcomp(tzoffsetfrom, tzoffsetto, comptype == 'DAYLIGHT', tzname, rr)
    721                         comps.append(comp)
    722                         comptype = None
    723                     else:
    724                         raise ValueError('invalid component end: ' + value)
    725                 elif comptype:
    726                     if name == 'DTSTART':
    727                         rrulelines.append(line)
    728                         founddtstart = True
    729                     elif name in ('RRULE', 'RDATE', 'EXRULE', 'EXDATE'):
    730                         rrulelines.append(line)
    731                     elif name == 'TZOFFSETFROM':
    732                         if parms:
    733                             raise ValueError('unsupported %s parm: %s ' % (name, parms[0]))
    734                         tzoffsetfrom = self._parse_offset(value)
    735                     elif name == 'TZOFFSETTO':
    736                         if parms:
    737                             raise ValueError('unsupported TZOFFSETTO parm: ' + parms[0])
    738                         tzoffsetto = self._parse_offset(value)
    739                     elif name == 'TZNAME':
    740                         if parms:
    741                             raise ValueError('unsupported TZNAME parm: ' + parms[0])
    742                         tzname = value
    743                     elif name == 'COMMENT':
    744                         pass
    745                     else:
    746                         raise ValueError('unsupported property: ' + name)
    747                 elif name == 'TZID':
    748                     if parms:
    749                         raise ValueError('unsupported TZID parm: ' + parms[0])
    750                     tzid = value
    751                 elif name in ('TZURL', 'LAST-MODIFIED', 'COMMENT'):
    752                     pass
    753                 else:
    754                     raise ValueError('unsupported property: ' + name)
    755             elif name == 'BEGIN' and value == 'VTIMEZONE':
    756                 tzid = None
    757                 comps = []
    758                 invtz = True
    759 
    760     def __repr__(self):
    761         return '%s(%s)' % (self.__class__.__name__, repr(self._s))
    762 
    763 
    764 if sys.platform != 'win32':
    765     TZFILES = ['/etc/localtime', 'localtime']
    766     TZPATHS = ['/usr/share/zoneinfo',
    767      '/usr/lib/zoneinfo',
    768      '/usr/share/lib/zoneinfo',
    769      '/etc/zoneinfo']
    770 else:
    771     TZFILES = []
    772     TZPATHS = []
    773 
    774 def gettz(name = None):
    775     tz = None
    776     if not name:
    777         try:
    778             name = os.environ['TZ']
    779         except KeyError:
    780             pass
    781 
    782     if name is None or name == ':':
    783         for filepath in TZFILES:
    784             if not os.path.isabs(filepath):
    785                 filename = filepath
    786                 for path in TZPATHS:
    787                     filepath = os.path.join(path, filename)
    788                     if os.path.isfile(filepath):
    789                         break
    790                 else:
    791                     continue
    792 
    793             if os.path.isfile(filepath):
    794                 try:
    795                     tz = tzfile(filepath)
    796                     break
    797                 except (IOError, OSError, ValueError):
    798                     pass
    799 
    800         else:
    801             tz = tzlocal()
    802 
    803     else:
    804         if name.startswith(':'):
    805             name = name[:-1]
    806         if os.path.isabs(name):
    807             if os.path.isfile(name):
    808                 tz = tzfile(name)
    809             else:
    810                 tz = None
    811         else:
    812             for path in TZPATHS:
    813                 filepath = os.path.join(path, name)
    814                 if not os.path.isfile(filepath):
    815                     filepath = filepath.replace(' ', '_')
    816                     if not os.path.isfile(filepath):
    817                         continue
    818                 try:
    819                     tz = tzfile(filepath)
    820                     break
    821                 except (IOError, OSError, ValueError):
    822                     pass
    823 
    824             else:
    825                 tz = None
    826                 if tzwin is not None:
    827                     try:
    828                         tz = tzwin(name)
    829                     except WindowsError:
    830                         tz = None
    831 
    832                 if not tz:
    833                     from dateutil.zoneinfo import get_zonefile_instance
    834                     tz = get_zonefile_instance().get(name)
    835                 if not tz:
    836                     for c in name:
    837                         if c in '0123456789':
    838                             try:
    839                                 tz = tzstr(name)
    840                             except ValueError:
    841                                 pass
    842 
    843                             break
    844                     else:
    845                         if name in ('GMT', 'UTC'):
    846                             tz = tzutc()
    847                         elif name in time.tzname:
    848                             tz = tzlocal()
    849 
    850     return tz
    851 
    852 
    853 def datetime_exists(dt, tz = None):
    854     if tz is None:
    855         if dt.tzinfo is None:
    856             raise ValueError('Datetime is naive and no time zone provided.')
    857         tz = dt.tzinfo
    858     dt = dt.replace(tzinfo=None)
    859     dt_rt = dt.replace(tzinfo=tz).astimezone(tzutc()).astimezone(tz)
    860     dt_rt = dt_rt.replace(tzinfo=None)
    861     return dt == dt_rt
    862 
    863 
    864 def datetime_ambiguous(dt, tz = None):
    865     if tz is None:
    866         if dt.tzinfo is None:
    867             raise ValueError('Datetime is naive and no time zone provided.')
    868         tz = dt.tzinfo
    869     is_ambiguous_fn = getattr(tz, 'is_ambiguous', None)
    870     if is_ambiguous_fn is not None:
    871         try:
    872             return tz.is_ambiguous(dt)
    873         except:
    874             pass
    875 
    876     dt = dt.replace(tzinfo=tz)
    877     wall_0 = enfold(dt, fold=0)
    878     wall_1 = enfold(dt, fold=1)
    879     same_offset = wall_0.utcoffset() == wall_1.utcoffset()
    880     same_dst = wall_0.dst() == wall_1.dst()
    881     return not (same_offset and same_dst)
    882 
    883 
    884 def _datetime_to_timestamp(dt):
    885     return _total_seconds(dt.replace(tzinfo=None) - EPOCH)
    886 
    887 
    888 class _ContextWrapper(object):
    889 
    890     def __init__(self, context):
    891         self.context = context
    892 
    893     def __enter__(self):
    894         return self.context
    895 
    896     def __exit__(*args, **kwargs):
    897         pass