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