mirror of
https://github.com/SickGear/SickGear.git
synced 2024-11-30 16:33:37 +00:00
Update dateutil library 2.4.2 (d4baf97) to 2.6.1 (2f3a160).
This commit is contained in:
parent
18d8bac4a1
commit
ad3f8c9584
16 changed files with 6287 additions and 5013 deletions
|
@ -1,2 +1,2 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
__version__ = "2.4.2"
|
from ._version import VERSION as __version__
|
||||||
|
|
43
lib/dateutil/_common.py
Normal file
43
lib/dateutil/_common.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
"""
|
||||||
|
Common code used in multiple modules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class weekday(object):
|
||||||
|
__slots__ = ["weekday", "n"]
|
||||||
|
|
||||||
|
def __init__(self, weekday, n=None):
|
||||||
|
self.weekday = weekday
|
||||||
|
self.n = n
|
||||||
|
|
||||||
|
def __call__(self, n):
|
||||||
|
if n == self.n:
|
||||||
|
return self
|
||||||
|
else:
|
||||||
|
return self.__class__(self.weekday, n)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
try:
|
||||||
|
if self.weekday != other.weekday or self.n != other.n:
|
||||||
|
return False
|
||||||
|
except AttributeError:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash((
|
||||||
|
self.weekday,
|
||||||
|
self.n,
|
||||||
|
))
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not (self == other)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
s = ("MO", "TU", "WE", "TH", "FR", "SA", "SU")[self.weekday]
|
||||||
|
if not self.n:
|
||||||
|
return s
|
||||||
|
else:
|
||||||
|
return "%s(%+d)" % (s, self.n)
|
||||||
|
|
||||||
|
# vim:ts=4:sw=4:et
|
10
lib/dateutil/_version.py
Normal file
10
lib/dateutil/_version.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
"""
|
||||||
|
Contains information about the dateutil version.
|
||||||
|
"""
|
||||||
|
|
||||||
|
VERSION_MAJOR = 2
|
||||||
|
VERSION_MINOR = 6
|
||||||
|
VERSION_PATCH = 1
|
||||||
|
|
||||||
|
VERSION_TUPLE = (VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH)
|
||||||
|
VERSION = '.'.join(map(str, VERSION_TUPLE))
|
|
@ -1,89 +1,89 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
This module offers a generic easter computing method for any given year, using
|
This module offers a generic easter computing method for any given year, using
|
||||||
Western, Orthodox or Julian algorithms.
|
Western, Orthodox or Julian algorithms.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
__all__ = ["easter", "EASTER_JULIAN", "EASTER_ORTHODOX", "EASTER_WESTERN"]
|
__all__ = ["easter", "EASTER_JULIAN", "EASTER_ORTHODOX", "EASTER_WESTERN"]
|
||||||
|
|
||||||
EASTER_JULIAN = 1
|
EASTER_JULIAN = 1
|
||||||
EASTER_ORTHODOX = 2
|
EASTER_ORTHODOX = 2
|
||||||
EASTER_WESTERN = 3
|
EASTER_WESTERN = 3
|
||||||
|
|
||||||
|
|
||||||
def easter(year, method=EASTER_WESTERN):
|
def easter(year, method=EASTER_WESTERN):
|
||||||
"""
|
"""
|
||||||
This method was ported from the work done by GM Arts,
|
This method was ported from the work done by GM Arts,
|
||||||
on top of the algorithm by Claus Tondering, which was
|
on top of the algorithm by Claus Tondering, which was
|
||||||
based in part on the algorithm of Ouding (1940), as
|
based in part on the algorithm of Ouding (1940), as
|
||||||
quoted in "Explanatory Supplement to the Astronomical
|
quoted in "Explanatory Supplement to the Astronomical
|
||||||
Almanac", P. Kenneth Seidelmann, editor.
|
Almanac", P. Kenneth Seidelmann, editor.
|
||||||
|
|
||||||
This algorithm implements three different easter
|
This algorithm implements three different easter
|
||||||
calculation methods:
|
calculation methods:
|
||||||
|
|
||||||
1 - Original calculation in Julian calendar, valid in
|
1 - Original calculation in Julian calendar, valid in
|
||||||
dates after 326 AD
|
dates after 326 AD
|
||||||
2 - Original method, with date converted to Gregorian
|
2 - Original method, with date converted to Gregorian
|
||||||
calendar, valid in years 1583 to 4099
|
calendar, valid in years 1583 to 4099
|
||||||
3 - Revised method, in Gregorian calendar, valid in
|
3 - Revised method, in Gregorian calendar, valid in
|
||||||
years 1583 to 4099 as well
|
years 1583 to 4099 as well
|
||||||
|
|
||||||
These methods are represented by the constants:
|
These methods are represented by the constants:
|
||||||
|
|
||||||
EASTER_JULIAN = 1
|
* ``EASTER_JULIAN = 1``
|
||||||
EASTER_ORTHODOX = 2
|
* ``EASTER_ORTHODOX = 2``
|
||||||
EASTER_WESTERN = 3
|
* ``EASTER_WESTERN = 3``
|
||||||
|
|
||||||
The default method is method 3.
|
The default method is method 3.
|
||||||
|
|
||||||
More about the algorithm may be found at:
|
More about the algorithm may be found at:
|
||||||
|
|
||||||
http://users.chariot.net.au/~gmarts/eastalg.htm
|
http://users.chariot.net.au/~gmarts/eastalg.htm
|
||||||
|
|
||||||
and
|
and
|
||||||
|
|
||||||
http://www.tondering.dk/claus/calendar.html
|
http://www.tondering.dk/claus/calendar.html
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not (1 <= method <= 3):
|
if not (1 <= method <= 3):
|
||||||
raise ValueError("invalid method")
|
raise ValueError("invalid method")
|
||||||
|
|
||||||
# g - Golden year - 1
|
# g - Golden year - 1
|
||||||
# c - Century
|
# c - Century
|
||||||
# h - (23 - Epact) mod 30
|
# h - (23 - Epact) mod 30
|
||||||
# i - Number of days from March 21 to Paschal Full Moon
|
# i - Number of days from March 21 to Paschal Full Moon
|
||||||
# j - Weekday for PFM (0=Sunday, etc)
|
# j - Weekday for PFM (0=Sunday, etc)
|
||||||
# p - Number of days from March 21 to Sunday on or before PFM
|
# p - Number of days from March 21 to Sunday on or before PFM
|
||||||
# (-6 to 28 methods 1 & 3, to 56 for method 2)
|
# (-6 to 28 methods 1 & 3, to 56 for method 2)
|
||||||
# e - Extra days to add for method 2 (converting Julian
|
# e - Extra days to add for method 2 (converting Julian
|
||||||
# date to Gregorian date)
|
# date to Gregorian date)
|
||||||
|
|
||||||
y = year
|
y = year
|
||||||
g = y % 19
|
g = y % 19
|
||||||
e = 0
|
e = 0
|
||||||
if method < 3:
|
if method < 3:
|
||||||
# Old method
|
# Old method
|
||||||
i = (19*g + 15) % 30
|
i = (19*g + 15) % 30
|
||||||
j = (y + y//4 + i) % 7
|
j = (y + y//4 + i) % 7
|
||||||
if method == 2:
|
if method == 2:
|
||||||
# Extra dates to convert Julian to Gregorian date
|
# Extra dates to convert Julian to Gregorian date
|
||||||
e = 10
|
e = 10
|
||||||
if y > 1600:
|
if y > 1600:
|
||||||
e = e + y//100 - 16 - (y//100 - 16)//4
|
e = e + y//100 - 16 - (y//100 - 16)//4
|
||||||
else:
|
else:
|
||||||
# New method
|
# New method
|
||||||
c = y//100
|
c = y//100
|
||||||
h = (c - c//4 - (8*c + 13)//25 + 19*g + 15) % 30
|
h = (c - c//4 - (8*c + 13)//25 + 19*g + 15) % 30
|
||||||
i = h - (h//28)*(1 - (h//28)*(29//(h + 1))*((21 - g)//11))
|
i = h - (h//28)*(1 - (h//28)*(29//(h + 1))*((21 - g)//11))
|
||||||
j = (y + y//4 + i + 2 - c + c//4) % 7
|
j = (y + y//4 + i + 2 - c + c//4) % 7
|
||||||
|
|
||||||
# p can be from -6 to 56 corresponding to dates 22 March to 23 May
|
# p can be from -6 to 56 corresponding to dates 22 March to 23 May
|
||||||
# (later dates apply to method 2, although 23 May never actually occurs)
|
# (later dates apply to method 2, although 23 May never actually occurs)
|
||||||
p = i - j + e
|
p = i - j + e
|
||||||
d = 1 + (p + 27 + (p + 6)//40) % 31
|
d = 1 + (p + 27 + (p + 6)//40) % 31
|
||||||
m = 3 + (p + 26)//30
|
m = 3 + (p + 26)//30
|
||||||
return datetime.date(int(y), int(m), int(d))
|
return datetime.date(int(y), int(m), int(d))
|
||||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,20 +1,5 @@
|
||||||
from .tz import *
|
from .tz import *
|
||||||
from six import PY3
|
|
||||||
|
__all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange",
|
||||||
__all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange",
|
"tzstr", "tzical", "tzwin", "tzwinlocal", "gettz",
|
||||||
"tzstr", "tzical", "tzwin", "tzwinlocal", "gettz"]
|
"enfold", "datetime_ambiguous", "datetime_exists"]
|
||||||
|
|
||||||
def tzname_in_python2(namefunc):
|
|
||||||
"""Change unicode output into bytestrings in Python 2
|
|
||||||
|
|
||||||
tzname() API changed in Python 3. It used to return bytes, but was changed
|
|
||||||
to unicode strings
|
|
||||||
"""
|
|
||||||
def adjust_encoding(*args, **kwargs):
|
|
||||||
name = namefunc(*args, **kwargs)
|
|
||||||
if name is not None and not PY3:
|
|
||||||
name = name.encode()
|
|
||||||
|
|
||||||
return name
|
|
||||||
|
|
||||||
return adjust_encoding
|
|
||||||
|
|
415
lib/dateutil/tz/_common.py
Normal file
415
lib/dateutil/tz/_common.py
Normal file
|
@ -0,0 +1,415 @@
|
||||||
|
from six import PY3
|
||||||
|
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta, tzinfo
|
||||||
|
|
||||||
|
|
||||||
|
ZERO = timedelta(0)
|
||||||
|
|
||||||
|
__all__ = ['tzname_in_python2', 'enfold']
|
||||||
|
|
||||||
|
|
||||||
|
def tzname_in_python2(namefunc):
|
||||||
|
"""Change unicode output into bytestrings in Python 2
|
||||||
|
|
||||||
|
tzname() API changed in Python 3. It used to return bytes, but was changed
|
||||||
|
to unicode strings
|
||||||
|
"""
|
||||||
|
def adjust_encoding(*args, **kwargs):
|
||||||
|
name = namefunc(*args, **kwargs)
|
||||||
|
if name is not None and not PY3:
|
||||||
|
name = name.encode()
|
||||||
|
|
||||||
|
return name
|
||||||
|
|
||||||
|
return adjust_encoding
|
||||||
|
|
||||||
|
|
||||||
|
# The following is adapted from Alexander Belopolsky's tz library
|
||||||
|
# https://github.com/abalkin/tz
|
||||||
|
if hasattr(datetime, 'fold'):
|
||||||
|
# This is the pre-python 3.6 fold situation
|
||||||
|
def enfold(dt, fold=1):
|
||||||
|
"""
|
||||||
|
Provides a unified interface for assigning the ``fold`` attribute to
|
||||||
|
datetimes both before and after the implementation of PEP-495.
|
||||||
|
|
||||||
|
:param fold:
|
||||||
|
The value for the ``fold`` attribute in the returned datetime. This
|
||||||
|
should be either 0 or 1.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
Returns an object for which ``getattr(dt, 'fold', 0)`` returns
|
||||||
|
``fold`` for all versions of Python. In versions prior to
|
||||||
|
Python 3.6, this is a ``_DatetimeWithFold`` object, which is a
|
||||||
|
subclass of :py:class:`datetime.datetime` with the ``fold``
|
||||||
|
attribute added, if ``fold`` is 1.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6.0
|
||||||
|
"""
|
||||||
|
return dt.replace(fold=fold)
|
||||||
|
|
||||||
|
else:
|
||||||
|
class _DatetimeWithFold(datetime):
|
||||||
|
"""
|
||||||
|
This is a class designed to provide a PEP 495-compliant interface for
|
||||||
|
Python versions before 3.6. It is used only for dates in a fold, so
|
||||||
|
the ``fold`` attribute is fixed at ``1``.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6.0
|
||||||
|
"""
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
def replace(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Return a datetime with the same attributes, except for those
|
||||||
|
attributes given new values by whichever keyword arguments are
|
||||||
|
specified. Note that tzinfo=None can be specified to create a naive
|
||||||
|
datetime from an aware datetime with no conversion of date and time
|
||||||
|
data.
|
||||||
|
|
||||||
|
This is reimplemented in ``_DatetimeWithFold`` because pypy3 will
|
||||||
|
return a ``datetime.datetime`` even if ``fold`` is unchanged.
|
||||||
|
"""
|
||||||
|
argnames = (
|
||||||
|
'year', 'month', 'day', 'hour', 'minute', 'second',
|
||||||
|
'microsecond', 'tzinfo'
|
||||||
|
)
|
||||||
|
|
||||||
|
for arg, argname in zip(args, argnames):
|
||||||
|
if argname in kwargs:
|
||||||
|
raise TypeError('Duplicate argument: {}'.format(argname))
|
||||||
|
|
||||||
|
kwargs[argname] = arg
|
||||||
|
|
||||||
|
for argname in argnames:
|
||||||
|
if argname not in kwargs:
|
||||||
|
kwargs[argname] = getattr(self, argname)
|
||||||
|
|
||||||
|
dt_class = self.__class__ if kwargs.get('fold', 1) else datetime
|
||||||
|
|
||||||
|
return dt_class(**kwargs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fold(self):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def enfold(dt, fold=1):
|
||||||
|
"""
|
||||||
|
Provides a unified interface for assigning the ``fold`` attribute to
|
||||||
|
datetimes both before and after the implementation of PEP-495.
|
||||||
|
|
||||||
|
:param fold:
|
||||||
|
The value for the ``fold`` attribute in the returned datetime. This
|
||||||
|
should be either 0 or 1.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
Returns an object for which ``getattr(dt, 'fold', 0)`` returns
|
||||||
|
``fold`` for all versions of Python. In versions prior to
|
||||||
|
Python 3.6, this is a ``_DatetimeWithFold`` object, which is a
|
||||||
|
subclass of :py:class:`datetime.datetime` with the ``fold``
|
||||||
|
attribute added, if ``fold`` is 1.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6.0
|
||||||
|
"""
|
||||||
|
if getattr(dt, 'fold', 0) == fold:
|
||||||
|
return dt
|
||||||
|
|
||||||
|
args = dt.timetuple()[:6]
|
||||||
|
args += (dt.microsecond, dt.tzinfo)
|
||||||
|
|
||||||
|
if fold:
|
||||||
|
return _DatetimeWithFold(*args)
|
||||||
|
else:
|
||||||
|
return datetime(*args)
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_fromutc_inputs(f):
|
||||||
|
"""
|
||||||
|
The CPython version of ``fromutc`` checks that the input is a ``datetime``
|
||||||
|
object and that ``self`` is attached as its ``tzinfo``.
|
||||||
|
"""
|
||||||
|
@wraps(f)
|
||||||
|
def fromutc(self, dt):
|
||||||
|
if not isinstance(dt, datetime):
|
||||||
|
raise TypeError("fromutc() requires a datetime argument")
|
||||||
|
if dt.tzinfo is not self:
|
||||||
|
raise ValueError("dt.tzinfo is not self")
|
||||||
|
|
||||||
|
return f(self, dt)
|
||||||
|
|
||||||
|
return fromutc
|
||||||
|
|
||||||
|
|
||||||
|
class _tzinfo(tzinfo):
|
||||||
|
"""
|
||||||
|
Base class for all ``dateutil`` ``tzinfo`` objects.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def is_ambiguous(self, dt):
|
||||||
|
"""
|
||||||
|
Whether or not the "wall time" of a given datetime is ambiguous in this
|
||||||
|
zone.
|
||||||
|
|
||||||
|
:param dt:
|
||||||
|
A :py:class:`datetime.datetime`, naive or time zone aware.
|
||||||
|
|
||||||
|
|
||||||
|
:return:
|
||||||
|
Returns ``True`` if ambiguous, ``False`` otherwise.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6.0
|
||||||
|
"""
|
||||||
|
|
||||||
|
dt = dt.replace(tzinfo=self)
|
||||||
|
|
||||||
|
wall_0 = enfold(dt, fold=0)
|
||||||
|
wall_1 = enfold(dt, fold=1)
|
||||||
|
|
||||||
|
same_offset = wall_0.utcoffset() == wall_1.utcoffset()
|
||||||
|
same_dt = wall_0.replace(tzinfo=None) == wall_1.replace(tzinfo=None)
|
||||||
|
|
||||||
|
return same_dt and not same_offset
|
||||||
|
|
||||||
|
def _fold_status(self, dt_utc, dt_wall):
|
||||||
|
"""
|
||||||
|
Determine the fold status of a "wall" datetime, given a representation
|
||||||
|
of the same datetime as a (naive) UTC datetime. This is calculated based
|
||||||
|
on the assumption that ``dt.utcoffset() - dt.dst()`` is constant for all
|
||||||
|
datetimes, and that this offset is the actual number of hours separating
|
||||||
|
``dt_utc`` and ``dt_wall``.
|
||||||
|
|
||||||
|
:param dt_utc:
|
||||||
|
Representation of the datetime as UTC
|
||||||
|
|
||||||
|
:param dt_wall:
|
||||||
|
Representation of the datetime as "wall time". This parameter must
|
||||||
|
either have a `fold` attribute or have a fold-naive
|
||||||
|
:class:`datetime.tzinfo` attached, otherwise the calculation may
|
||||||
|
fail.
|
||||||
|
"""
|
||||||
|
if self.is_ambiguous(dt_wall):
|
||||||
|
delta_wall = dt_wall - dt_utc
|
||||||
|
_fold = int(delta_wall == (dt_utc.utcoffset() - dt_utc.dst()))
|
||||||
|
else:
|
||||||
|
_fold = 0
|
||||||
|
|
||||||
|
return _fold
|
||||||
|
|
||||||
|
def _fold(self, dt):
|
||||||
|
return getattr(dt, 'fold', 0)
|
||||||
|
|
||||||
|
def _fromutc(self, dt):
|
||||||
|
"""
|
||||||
|
Given a timezone-aware datetime in a given timezone, calculates a
|
||||||
|
timezone-aware datetime in a new timezone.
|
||||||
|
|
||||||
|
Since this is the one time that we *know* we have an unambiguous
|
||||||
|
datetime object, we take this opportunity to determine whether the
|
||||||
|
datetime is ambiguous and in a "fold" state (e.g. if it's the first
|
||||||
|
occurence, chronologically, of the ambiguous datetime).
|
||||||
|
|
||||||
|
:param dt:
|
||||||
|
A timezone-aware :class:`datetime.datetime` object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Re-implement the algorithm from Python's datetime.py
|
||||||
|
dtoff = dt.utcoffset()
|
||||||
|
if dtoff is None:
|
||||||
|
raise ValueError("fromutc() requires a non-None utcoffset() "
|
||||||
|
"result")
|
||||||
|
|
||||||
|
# The original datetime.py code assumes that `dst()` defaults to
|
||||||
|
# zero during ambiguous times. PEP 495 inverts this presumption, so
|
||||||
|
# for pre-PEP 495 versions of python, we need to tweak the algorithm.
|
||||||
|
dtdst = dt.dst()
|
||||||
|
if dtdst is None:
|
||||||
|
raise ValueError("fromutc() requires a non-None dst() result")
|
||||||
|
delta = dtoff - dtdst
|
||||||
|
|
||||||
|
dt += delta
|
||||||
|
# Set fold=1 so we can default to being in the fold for
|
||||||
|
# ambiguous dates.
|
||||||
|
dtdst = enfold(dt, fold=1).dst()
|
||||||
|
if dtdst is None:
|
||||||
|
raise ValueError("fromutc(): dt.dst gave inconsistent "
|
||||||
|
"results; cannot convert")
|
||||||
|
return dt + dtdst
|
||||||
|
|
||||||
|
@_validate_fromutc_inputs
|
||||||
|
def fromutc(self, dt):
|
||||||
|
"""
|
||||||
|
Given a timezone-aware datetime in a given timezone, calculates a
|
||||||
|
timezone-aware datetime in a new timezone.
|
||||||
|
|
||||||
|
Since this is the one time that we *know* we have an unambiguous
|
||||||
|
datetime object, we take this opportunity to determine whether the
|
||||||
|
datetime is ambiguous and in a "fold" state (e.g. if it's the first
|
||||||
|
occurance, chronologically, of the ambiguous datetime).
|
||||||
|
|
||||||
|
:param dt:
|
||||||
|
A timezone-aware :class:`datetime.datetime` object.
|
||||||
|
"""
|
||||||
|
dt_wall = self._fromutc(dt)
|
||||||
|
|
||||||
|
# Calculate the fold status given the two datetimes.
|
||||||
|
_fold = self._fold_status(dt, dt_wall)
|
||||||
|
|
||||||
|
# Set the default fold value for ambiguous dates
|
||||||
|
return enfold(dt_wall, fold=_fold)
|
||||||
|
|
||||||
|
|
||||||
|
class tzrangebase(_tzinfo):
|
||||||
|
"""
|
||||||
|
This is an abstract base class for time zones represented by an annual
|
||||||
|
transition into and out of DST. Child classes should implement the following
|
||||||
|
methods:
|
||||||
|
|
||||||
|
* ``__init__(self, *args, **kwargs)``
|
||||||
|
* ``transitions(self, year)`` - this is expected to return a tuple of
|
||||||
|
datetimes representing the DST on and off transitions in standard
|
||||||
|
time.
|
||||||
|
|
||||||
|
A fully initialized ``tzrangebase`` subclass should also provide the
|
||||||
|
following attributes:
|
||||||
|
* ``hasdst``: Boolean whether or not the zone uses DST.
|
||||||
|
* ``_dst_offset`` / ``_std_offset``: :class:`datetime.timedelta` objects
|
||||||
|
representing the respective UTC offsets.
|
||||||
|
* ``_dst_abbr`` / ``_std_abbr``: Strings representing the timezone short
|
||||||
|
abbreviations in DST and STD, respectively.
|
||||||
|
* ``_hasdst``: Whether or not the zone has DST.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6.0
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
raise NotImplementedError('tzrangebase is an abstract base class')
|
||||||
|
|
||||||
|
def utcoffset(self, dt):
|
||||||
|
isdst = self._isdst(dt)
|
||||||
|
|
||||||
|
if isdst is None:
|
||||||
|
return None
|
||||||
|
elif isdst:
|
||||||
|
return self._dst_offset
|
||||||
|
else:
|
||||||
|
return self._std_offset
|
||||||
|
|
||||||
|
def dst(self, dt):
|
||||||
|
isdst = self._isdst(dt)
|
||||||
|
|
||||||
|
if isdst is None:
|
||||||
|
return None
|
||||||
|
elif isdst:
|
||||||
|
return self._dst_base_offset
|
||||||
|
else:
|
||||||
|
return ZERO
|
||||||
|
|
||||||
|
@tzname_in_python2
|
||||||
|
def tzname(self, dt):
|
||||||
|
if self._isdst(dt):
|
||||||
|
return self._dst_abbr
|
||||||
|
else:
|
||||||
|
return self._std_abbr
|
||||||
|
|
||||||
|
def fromutc(self, dt):
|
||||||
|
""" Given a datetime in UTC, return local time """
|
||||||
|
if not isinstance(dt, datetime):
|
||||||
|
raise TypeError("fromutc() requires a datetime argument")
|
||||||
|
|
||||||
|
if dt.tzinfo is not self:
|
||||||
|
raise ValueError("dt.tzinfo is not self")
|
||||||
|
|
||||||
|
# Get transitions - if there are none, fixed offset
|
||||||
|
transitions = self.transitions(dt.year)
|
||||||
|
if transitions is None:
|
||||||
|
return dt + self.utcoffset(dt)
|
||||||
|
|
||||||
|
# Get the transition times in UTC
|
||||||
|
dston, dstoff = transitions
|
||||||
|
|
||||||
|
dston -= self._std_offset
|
||||||
|
dstoff -= self._std_offset
|
||||||
|
|
||||||
|
utc_transitions = (dston, dstoff)
|
||||||
|
dt_utc = dt.replace(tzinfo=None)
|
||||||
|
|
||||||
|
isdst = self._naive_isdst(dt_utc, utc_transitions)
|
||||||
|
|
||||||
|
if isdst:
|
||||||
|
dt_wall = dt + self._dst_offset
|
||||||
|
else:
|
||||||
|
dt_wall = dt + self._std_offset
|
||||||
|
|
||||||
|
_fold = int(not isdst and self.is_ambiguous(dt_wall))
|
||||||
|
|
||||||
|
return enfold(dt_wall, fold=_fold)
|
||||||
|
|
||||||
|
def is_ambiguous(self, dt):
|
||||||
|
"""
|
||||||
|
Whether or not the "wall time" of a given datetime is ambiguous in this
|
||||||
|
zone.
|
||||||
|
|
||||||
|
:param dt:
|
||||||
|
A :py:class:`datetime.datetime`, naive or time zone aware.
|
||||||
|
|
||||||
|
|
||||||
|
:return:
|
||||||
|
Returns ``True`` if ambiguous, ``False`` otherwise.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6.0
|
||||||
|
"""
|
||||||
|
if not self.hasdst:
|
||||||
|
return False
|
||||||
|
|
||||||
|
start, end = self.transitions(dt.year)
|
||||||
|
|
||||||
|
dt = dt.replace(tzinfo=None)
|
||||||
|
return (end <= dt < end + self._dst_base_offset)
|
||||||
|
|
||||||
|
def _isdst(self, dt):
|
||||||
|
if not self.hasdst:
|
||||||
|
return False
|
||||||
|
elif dt is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
transitions = self.transitions(dt.year)
|
||||||
|
|
||||||
|
if transitions is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
dt = dt.replace(tzinfo=None)
|
||||||
|
|
||||||
|
isdst = self._naive_isdst(dt, transitions)
|
||||||
|
|
||||||
|
# Handle ambiguous dates
|
||||||
|
if not isdst and self.is_ambiguous(dt):
|
||||||
|
return not self._fold(dt)
|
||||||
|
else:
|
||||||
|
return isdst
|
||||||
|
|
||||||
|
def _naive_isdst(self, dt, transitions):
|
||||||
|
dston, dstoff = transitions
|
||||||
|
|
||||||
|
dt = dt.replace(tzinfo=None)
|
||||||
|
|
||||||
|
if dston < dstoff:
|
||||||
|
isdst = dston <= dt < dstoff
|
||||||
|
else:
|
||||||
|
isdst = not dstoff <= dt < dston
|
||||||
|
|
||||||
|
return isdst
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _dst_base_offset(self):
|
||||||
|
return self._dst_offset - self._std_offset
|
||||||
|
|
||||||
|
__hash__ = None
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not (self == other)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "%s(...)" % self.__class__.__name__
|
||||||
|
|
||||||
|
__reduce__ = object.__reduce__
|
File diff suppressed because it is too large
Load diff
|
@ -1,187 +0,0 @@
|
||||||
# This code was originally contributed by Jeffrey Harris.
|
|
||||||
import datetime
|
|
||||||
import struct
|
|
||||||
|
|
||||||
from six.moves import winreg
|
|
||||||
|
|
||||||
from .__init__ import tzname_in_python2
|
|
||||||
|
|
||||||
__all__ = ["tzwin", "tzwinlocal"]
|
|
||||||
|
|
||||||
ONEWEEK = datetime.timedelta(7)
|
|
||||||
|
|
||||||
TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones"
|
|
||||||
TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones"
|
|
||||||
TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"
|
|
||||||
|
|
||||||
|
|
||||||
def _settzkeyname():
|
|
||||||
handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
|
|
||||||
try:
|
|
||||||
winreg.OpenKey(handle, TZKEYNAMENT).Close()
|
|
||||||
TZKEYNAME = TZKEYNAMENT
|
|
||||||
except WindowsError:
|
|
||||||
TZKEYNAME = TZKEYNAME9X
|
|
||||||
handle.Close()
|
|
||||||
return TZKEYNAME
|
|
||||||
|
|
||||||
TZKEYNAME = _settzkeyname()
|
|
||||||
|
|
||||||
|
|
||||||
class tzwinbase(datetime.tzinfo):
|
|
||||||
"""tzinfo class based on win32's timezones available in the registry."""
|
|
||||||
|
|
||||||
def utcoffset(self, dt):
|
|
||||||
if self._isdst(dt):
|
|
||||||
return datetime.timedelta(minutes=self._dstoffset)
|
|
||||||
else:
|
|
||||||
return datetime.timedelta(minutes=self._stdoffset)
|
|
||||||
|
|
||||||
def dst(self, dt):
|
|
||||||
if self._isdst(dt):
|
|
||||||
minutes = self._dstoffset - self._stdoffset
|
|
||||||
return datetime.timedelta(minutes=minutes)
|
|
||||||
else:
|
|
||||||
return datetime.timedelta(0)
|
|
||||||
|
|
||||||
@tzname_in_python2
|
|
||||||
def tzname(self, dt):
|
|
||||||
if self._isdst(dt):
|
|
||||||
return self._dstname
|
|
||||||
else:
|
|
||||||
return self._stdname
|
|
||||||
|
|
||||||
def list():
|
|
||||||
"""Return a list of all time zones known to the system."""
|
|
||||||
handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
|
|
||||||
tzkey = winreg.OpenKey(handle, TZKEYNAME)
|
|
||||||
result = [winreg.EnumKey(tzkey, i)
|
|
||||||
for i in range(winreg.QueryInfoKey(tzkey)[0])]
|
|
||||||
tzkey.Close()
|
|
||||||
handle.Close()
|
|
||||||
return result
|
|
||||||
list = staticmethod(list)
|
|
||||||
|
|
||||||
def display(self):
|
|
||||||
return self._display
|
|
||||||
|
|
||||||
def _isdst(self, dt):
|
|
||||||
if not self._dstmonth:
|
|
||||||
# dstmonth == 0 signals the zone has no daylight saving time
|
|
||||||
return False
|
|
||||||
dston = picknthweekday(dt.year, self._dstmonth, self._dstdayofweek,
|
|
||||||
self._dsthour, self._dstminute,
|
|
||||||
self._dstweeknumber)
|
|
||||||
dstoff = picknthweekday(dt.year, self._stdmonth, self._stddayofweek,
|
|
||||||
self._stdhour, self._stdminute,
|
|
||||||
self._stdweeknumber)
|
|
||||||
if dston < dstoff:
|
|
||||||
return dston <= dt.replace(tzinfo=None) < dstoff
|
|
||||||
else:
|
|
||||||
return not dstoff <= dt.replace(tzinfo=None) < dston
|
|
||||||
|
|
||||||
|
|
||||||
class tzwin(tzwinbase):
|
|
||||||
|
|
||||||
def __init__(self, name):
|
|
||||||
self._name = name
|
|
||||||
|
|
||||||
# multiple contexts only possible in 2.7 and 3.1, we still support 2.6
|
|
||||||
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
|
|
||||||
with winreg.OpenKey(handle,
|
|
||||||
"%s\%s" % (TZKEYNAME, name)) as tzkey:
|
|
||||||
keydict = valuestodict(tzkey)
|
|
||||||
|
|
||||||
self._stdname = keydict["Std"]
|
|
||||||
self._dstname = keydict["Dlt"]
|
|
||||||
|
|
||||||
self._display = keydict["Display"]
|
|
||||||
|
|
||||||
# See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm
|
|
||||||
tup = struct.unpack("=3l16h", keydict["TZI"])
|
|
||||||
self._stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1
|
|
||||||
self._dstoffset = self._stdoffset-tup[2] # + DaylightBias * -1
|
|
||||||
|
|
||||||
# for the meaning see the win32 TIME_ZONE_INFORMATION structure docs
|
|
||||||
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx
|
|
||||||
(self._stdmonth,
|
|
||||||
self._stddayofweek, # Sunday = 0
|
|
||||||
self._stdweeknumber, # Last = 5
|
|
||||||
self._stdhour,
|
|
||||||
self._stdminute) = tup[4:9]
|
|
||||||
|
|
||||||
(self._dstmonth,
|
|
||||||
self._dstdayofweek, # Sunday = 0
|
|
||||||
self._dstweeknumber, # Last = 5
|
|
||||||
self._dsthour,
|
|
||||||
self._dstminute) = tup[12:17]
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "tzwin(%s)" % repr(self._name)
|
|
||||||
|
|
||||||
def __reduce__(self):
|
|
||||||
return (self.__class__, (self._name,))
|
|
||||||
|
|
||||||
|
|
||||||
class tzwinlocal(tzwinbase):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
|
|
||||||
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
|
|
||||||
|
|
||||||
with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey:
|
|
||||||
keydict = valuestodict(tzlocalkey)
|
|
||||||
|
|
||||||
self._stdname = keydict["StandardName"]
|
|
||||||
self._dstname = keydict["DaylightName"]
|
|
||||||
|
|
||||||
try:
|
|
||||||
with winreg.OpenKey(
|
|
||||||
handle, "%s\%s" % (TZKEYNAME, self._stdname)) as tzkey:
|
|
||||||
_keydict = valuestodict(tzkey)
|
|
||||||
self._display = _keydict["Display"]
|
|
||||||
except OSError:
|
|
||||||
self._display = None
|
|
||||||
|
|
||||||
self._stdoffset = -keydict["Bias"]-keydict["StandardBias"]
|
|
||||||
self._dstoffset = self._stdoffset-keydict["DaylightBias"]
|
|
||||||
|
|
||||||
# See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm
|
|
||||||
tup = struct.unpack("=8h", keydict["StandardStart"])
|
|
||||||
|
|
||||||
(self._stdmonth,
|
|
||||||
self._stddayofweek, # Sunday = 0
|
|
||||||
self._stdweeknumber, # Last = 5
|
|
||||||
self._stdhour,
|
|
||||||
self._stdminute) = tup[1:6]
|
|
||||||
|
|
||||||
tup = struct.unpack("=8h", keydict["DaylightStart"])
|
|
||||||
|
|
||||||
(self._dstmonth,
|
|
||||||
self._dstdayofweek, # Sunday = 0
|
|
||||||
self._dstweeknumber, # Last = 5
|
|
||||||
self._dsthour,
|
|
||||||
self._dstminute) = tup[1:6]
|
|
||||||
|
|
||||||
def __reduce__(self):
|
|
||||||
return (self.__class__, ())
|
|
||||||
|
|
||||||
|
|
||||||
def picknthweekday(year, month, dayofweek, hour, minute, whichweek):
|
|
||||||
"""dayofweek == 0 means Sunday, whichweek 5 means last instance"""
|
|
||||||
first = datetime.datetime(year, month, 1, hour, minute)
|
|
||||||
weekdayone = first.replace(day=((dayofweek-first.isoweekday()) % 7+1))
|
|
||||||
for n in range(whichweek):
|
|
||||||
dt = weekdayone+(whichweek-n)*ONEWEEK
|
|
||||||
if dt.month == month:
|
|
||||||
return dt
|
|
||||||
|
|
||||||
|
|
||||||
def valuestodict(key):
|
|
||||||
"""Convert a registry key's values to a dictionary."""
|
|
||||||
dict = {}
|
|
||||||
size = winreg.QueryInfoKey(key)[1]
|
|
||||||
for i in range(size):
|
|
||||||
data = winreg.EnumValue(key, i)
|
|
||||||
dict[data[0]] = data[1]
|
|
||||||
return dict
|
|
|
@ -1,187 +1,331 @@
|
||||||
# This code was originally contributed by Jeffrey Harris.
|
# This code was originally contributed by Jeffrey Harris.
|
||||||
import datetime
|
import datetime
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
from six.moves import winreg
|
from six.moves import winreg
|
||||||
|
from six import text_type
|
||||||
from .__init__ import tzname_in_python2
|
|
||||||
|
try:
|
||||||
__all__ = ["tzwin", "tzwinlocal"]
|
import ctypes
|
||||||
|
from ctypes import wintypes
|
||||||
ONEWEEK = datetime.timedelta(7)
|
except ValueError:
|
||||||
|
# ValueError is raised on non-Windows systems for some horrible reason.
|
||||||
TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones"
|
raise ImportError("Running tzwin on non-Windows system")
|
||||||
TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones"
|
|
||||||
TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"
|
from ._common import tzrangebase
|
||||||
|
|
||||||
|
__all__ = ["tzwin", "tzwinlocal", "tzres"]
|
||||||
def _settzkeyname():
|
|
||||||
handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
|
ONEWEEK = datetime.timedelta(7)
|
||||||
try:
|
|
||||||
winreg.OpenKey(handle, TZKEYNAMENT).Close()
|
TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones"
|
||||||
TZKEYNAME = TZKEYNAMENT
|
TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones"
|
||||||
except WindowsError:
|
TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"
|
||||||
TZKEYNAME = TZKEYNAME9X
|
|
||||||
handle.Close()
|
|
||||||
return TZKEYNAME
|
def _settzkeyname():
|
||||||
|
handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
|
||||||
TZKEYNAME = _settzkeyname()
|
try:
|
||||||
|
winreg.OpenKey(handle, TZKEYNAMENT).Close()
|
||||||
|
TZKEYNAME = TZKEYNAMENT
|
||||||
class tzwinbase(datetime.tzinfo):
|
except WindowsError:
|
||||||
"""tzinfo class based on win32's timezones available in the registry."""
|
TZKEYNAME = TZKEYNAME9X
|
||||||
|
handle.Close()
|
||||||
def utcoffset(self, dt):
|
return TZKEYNAME
|
||||||
if self._isdst(dt):
|
|
||||||
return datetime.timedelta(minutes=self._dstoffset)
|
|
||||||
else:
|
TZKEYNAME = _settzkeyname()
|
||||||
return datetime.timedelta(minutes=self._stdoffset)
|
|
||||||
|
|
||||||
def dst(self, dt):
|
class tzres(object):
|
||||||
if self._isdst(dt):
|
"""
|
||||||
minutes = self._dstoffset - self._stdoffset
|
Class for accessing `tzres.dll`, which contains timezone name related
|
||||||
return datetime.timedelta(minutes=minutes)
|
resources.
|
||||||
else:
|
|
||||||
return datetime.timedelta(0)
|
.. versionadded:: 2.5.0
|
||||||
|
"""
|
||||||
@tzname_in_python2
|
p_wchar = ctypes.POINTER(wintypes.WCHAR) # Pointer to a wide char
|
||||||
def tzname(self, dt):
|
|
||||||
if self._isdst(dt):
|
def __init__(self, tzres_loc='tzres.dll'):
|
||||||
return self._dstname
|
# Load the user32 DLL so we can load strings from tzres
|
||||||
else:
|
user32 = ctypes.WinDLL('user32')
|
||||||
return self._stdname
|
|
||||||
|
# Specify the LoadStringW function
|
||||||
def list():
|
user32.LoadStringW.argtypes = (wintypes.HINSTANCE,
|
||||||
"""Return a list of all time zones known to the system."""
|
wintypes.UINT,
|
||||||
handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
|
wintypes.LPWSTR,
|
||||||
tzkey = winreg.OpenKey(handle, TZKEYNAME)
|
ctypes.c_int)
|
||||||
result = [winreg.EnumKey(tzkey, i)
|
|
||||||
for i in range(winreg.QueryInfoKey(tzkey)[0])]
|
self.LoadStringW = user32.LoadStringW
|
||||||
tzkey.Close()
|
self._tzres = ctypes.WinDLL(tzres_loc)
|
||||||
handle.Close()
|
self.tzres_loc = tzres_loc
|
||||||
return result
|
|
||||||
list = staticmethod(list)
|
def load_name(self, offset):
|
||||||
|
"""
|
||||||
def display(self):
|
Load a timezone name from a DLL offset (integer).
|
||||||
return self._display
|
|
||||||
|
>>> from dateutil.tzwin import tzres
|
||||||
def _isdst(self, dt):
|
>>> tzr = tzres()
|
||||||
if not self._dstmonth:
|
>>> print(tzr.load_name(112))
|
||||||
# dstmonth == 0 signals the zone has no daylight saving time
|
'Eastern Standard Time'
|
||||||
return False
|
|
||||||
dston = picknthweekday(dt.year, self._dstmonth, self._dstdayofweek,
|
:param offset:
|
||||||
self._dsthour, self._dstminute,
|
A positive integer value referring to a string from the tzres dll.
|
||||||
self._dstweeknumber)
|
|
||||||
dstoff = picknthweekday(dt.year, self._stdmonth, self._stddayofweek,
|
..note:
|
||||||
self._stdhour, self._stdminute,
|
Offsets found in the registry are generally of the form
|
||||||
self._stdweeknumber)
|
`@tzres.dll,-114`. The offset in this case if 114, not -114.
|
||||||
if dston < dstoff:
|
|
||||||
return dston <= dt.replace(tzinfo=None) < dstoff
|
"""
|
||||||
else:
|
resource = self.p_wchar()
|
||||||
return not dstoff <= dt.replace(tzinfo=None) < dston
|
lpBuffer = ctypes.cast(ctypes.byref(resource), wintypes.LPWSTR)
|
||||||
|
nchar = self.LoadStringW(self._tzres._handle, offset, lpBuffer, 0)
|
||||||
|
return resource[:nchar]
|
||||||
class tzwin(tzwinbase):
|
|
||||||
|
def name_from_string(self, tzname_str):
|
||||||
def __init__(self, name):
|
"""
|
||||||
self._name = name
|
Parse strings as returned from the Windows registry into the time zone
|
||||||
|
name as defined in the registry.
|
||||||
# multiple contexts only possible in 2.7 and 3.1, we still support 2.6
|
|
||||||
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
|
>>> from dateutil.tzwin import tzres
|
||||||
with winreg.OpenKey(handle,
|
>>> tzr = tzres()
|
||||||
"%s\%s" % (TZKEYNAME, name)) as tzkey:
|
>>> print(tzr.name_from_string('@tzres.dll,-251'))
|
||||||
keydict = valuestodict(tzkey)
|
'Dateline Daylight Time'
|
||||||
|
>>> print(tzr.name_from_string('Eastern Standard Time'))
|
||||||
self._stdname = keydict["Std"]
|
'Eastern Standard Time'
|
||||||
self._dstname = keydict["Dlt"]
|
|
||||||
|
:param tzname_str:
|
||||||
self._display = keydict["Display"]
|
A timezone name string as returned from a Windows registry key.
|
||||||
|
|
||||||
# See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm
|
:return:
|
||||||
tup = struct.unpack("=3l16h", keydict["TZI"])
|
Returns the localized timezone string from tzres.dll if the string
|
||||||
self._stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1
|
is of the form `@tzres.dll,-offset`, else returns the input string.
|
||||||
self._dstoffset = self._stdoffset-tup[2] # + DaylightBias * -1
|
"""
|
||||||
|
if not tzname_str.startswith('@'):
|
||||||
# for the meaning see the win32 TIME_ZONE_INFORMATION structure docs
|
return tzname_str
|
||||||
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx
|
|
||||||
(self._stdmonth,
|
name_splt = tzname_str.split(',-')
|
||||||
self._stddayofweek, # Sunday = 0
|
try:
|
||||||
self._stdweeknumber, # Last = 5
|
offset = int(name_splt[1])
|
||||||
self._stdhour,
|
except:
|
||||||
self._stdminute) = tup[4:9]
|
raise ValueError("Malformed timezone string.")
|
||||||
|
|
||||||
(self._dstmonth,
|
return self.load_name(offset)
|
||||||
self._dstdayofweek, # Sunday = 0
|
|
||||||
self._dstweeknumber, # Last = 5
|
|
||||||
self._dsthour,
|
class tzwinbase(tzrangebase):
|
||||||
self._dstminute) = tup[12:17]
|
"""tzinfo class based on win32's timezones available in the registry."""
|
||||||
|
def __init__(self):
|
||||||
def __repr__(self):
|
raise NotImplementedError('tzwinbase is an abstract base class')
|
||||||
return "tzwin(%s)" % repr(self._name)
|
|
||||||
|
def __eq__(self, other):
|
||||||
def __reduce__(self):
|
# Compare on all relevant dimensions, including name.
|
||||||
return (self.__class__, (self._name,))
|
if not isinstance(other, tzwinbase):
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
class tzwinlocal(tzwinbase):
|
return (self._std_offset == other._std_offset and
|
||||||
|
self._dst_offset == other._dst_offset and
|
||||||
def __init__(self):
|
self._stddayofweek == other._stddayofweek and
|
||||||
|
self._dstdayofweek == other._dstdayofweek and
|
||||||
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
|
self._stdweeknumber == other._stdweeknumber and
|
||||||
|
self._dstweeknumber == other._dstweeknumber and
|
||||||
with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey:
|
self._stdhour == other._stdhour and
|
||||||
keydict = valuestodict(tzlocalkey)
|
self._dsthour == other._dsthour and
|
||||||
|
self._stdminute == other._stdminute and
|
||||||
self._stdname = keydict["StandardName"]
|
self._dstminute == other._dstminute and
|
||||||
self._dstname = keydict["DaylightName"]
|
self._std_abbr == other._std_abbr and
|
||||||
|
self._dst_abbr == other._dst_abbr)
|
||||||
try:
|
|
||||||
with winreg.OpenKey(
|
@staticmethod
|
||||||
handle, "%s\%s" % (TZKEYNAME, self._stdname)) as tzkey:
|
def list():
|
||||||
_keydict = valuestodict(tzkey)
|
"""Return a list of all time zones known to the system."""
|
||||||
self._display = _keydict["Display"]
|
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
|
||||||
except OSError:
|
with winreg.OpenKey(handle, TZKEYNAME) as tzkey:
|
||||||
self._display = None
|
result = [winreg.EnumKey(tzkey, i)
|
||||||
|
for i in range(winreg.QueryInfoKey(tzkey)[0])]
|
||||||
self._stdoffset = -keydict["Bias"]-keydict["StandardBias"]
|
return result
|
||||||
self._dstoffset = self._stdoffset-keydict["DaylightBias"]
|
|
||||||
|
def display(self):
|
||||||
# See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm
|
return self._display
|
||||||
tup = struct.unpack("=8h", keydict["StandardStart"])
|
|
||||||
|
def transitions(self, year):
|
||||||
(self._stdmonth,
|
"""
|
||||||
self._stddayofweek, # Sunday = 0
|
For a given year, get the DST on and off transition times, expressed
|
||||||
self._stdweeknumber, # Last = 5
|
always on the standard time side. For zones with no transitions, this
|
||||||
self._stdhour,
|
function returns ``None``.
|
||||||
self._stdminute) = tup[1:6]
|
|
||||||
|
:param year:
|
||||||
tup = struct.unpack("=8h", keydict["DaylightStart"])
|
The year whose transitions you would like to query.
|
||||||
|
|
||||||
(self._dstmonth,
|
:return:
|
||||||
self._dstdayofweek, # Sunday = 0
|
Returns a :class:`tuple` of :class:`datetime.datetime` objects,
|
||||||
self._dstweeknumber, # Last = 5
|
``(dston, dstoff)`` for zones with an annual DST transition, or
|
||||||
self._dsthour,
|
``None`` for fixed offset zones.
|
||||||
self._dstminute) = tup[1:6]
|
"""
|
||||||
|
|
||||||
def __reduce__(self):
|
if not self.hasdst:
|
||||||
return (self.__class__, ())
|
return None
|
||||||
|
|
||||||
|
dston = picknthweekday(year, self._dstmonth, self._dstdayofweek,
|
||||||
def picknthweekday(year, month, dayofweek, hour, minute, whichweek):
|
self._dsthour, self._dstminute,
|
||||||
"""dayofweek == 0 means Sunday, whichweek 5 means last instance"""
|
self._dstweeknumber)
|
||||||
first = datetime.datetime(year, month, 1, hour, minute)
|
|
||||||
weekdayone = first.replace(day=((dayofweek-first.isoweekday()) % 7+1))
|
dstoff = picknthweekday(year, self._stdmonth, self._stddayofweek,
|
||||||
for n in range(whichweek):
|
self._stdhour, self._stdminute,
|
||||||
dt = weekdayone+(whichweek-n)*ONEWEEK
|
self._stdweeknumber)
|
||||||
if dt.month == month:
|
|
||||||
return dt
|
# Ambiguous dates default to the STD side
|
||||||
|
dstoff -= self._dst_base_offset
|
||||||
|
|
||||||
def valuestodict(key):
|
return dston, dstoff
|
||||||
"""Convert a registry key's values to a dictionary."""
|
|
||||||
dict = {}
|
def _get_hasdst(self):
|
||||||
size = winreg.QueryInfoKey(key)[1]
|
return self._dstmonth != 0
|
||||||
for i in range(size):
|
|
||||||
data = winreg.EnumValue(key, i)
|
@property
|
||||||
dict[data[0]] = data[1]
|
def _dst_base_offset(self):
|
||||||
return dict
|
return self._dst_base_offset_
|
||||||
|
|
||||||
|
|
||||||
|
class tzwin(tzwinbase):
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
self._name = name
|
||||||
|
|
||||||
|
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
|
||||||
|
tzkeyname = text_type("{kn}\\{name}").format(kn=TZKEYNAME, name=name)
|
||||||
|
with winreg.OpenKey(handle, tzkeyname) as tzkey:
|
||||||
|
keydict = valuestodict(tzkey)
|
||||||
|
|
||||||
|
self._std_abbr = keydict["Std"]
|
||||||
|
self._dst_abbr = keydict["Dlt"]
|
||||||
|
|
||||||
|
self._display = keydict["Display"]
|
||||||
|
|
||||||
|
# See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm
|
||||||
|
tup = struct.unpack("=3l16h", keydict["TZI"])
|
||||||
|
stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1
|
||||||
|
dstoffset = stdoffset-tup[2] # + DaylightBias * -1
|
||||||
|
self._std_offset = datetime.timedelta(minutes=stdoffset)
|
||||||
|
self._dst_offset = datetime.timedelta(minutes=dstoffset)
|
||||||
|
|
||||||
|
# for the meaning see the win32 TIME_ZONE_INFORMATION structure docs
|
||||||
|
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx
|
||||||
|
(self._stdmonth,
|
||||||
|
self._stddayofweek, # Sunday = 0
|
||||||
|
self._stdweeknumber, # Last = 5
|
||||||
|
self._stdhour,
|
||||||
|
self._stdminute) = tup[4:9]
|
||||||
|
|
||||||
|
(self._dstmonth,
|
||||||
|
self._dstdayofweek, # Sunday = 0
|
||||||
|
self._dstweeknumber, # Last = 5
|
||||||
|
self._dsthour,
|
||||||
|
self._dstminute) = tup[12:17]
|
||||||
|
|
||||||
|
self._dst_base_offset_ = self._dst_offset - self._std_offset
|
||||||
|
self.hasdst = self._get_hasdst()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "tzwin(%s)" % repr(self._name)
|
||||||
|
|
||||||
|
def __reduce__(self):
|
||||||
|
return (self.__class__, (self._name,))
|
||||||
|
|
||||||
|
|
||||||
|
class tzwinlocal(tzwinbase):
|
||||||
|
def __init__(self):
|
||||||
|
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
|
||||||
|
with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey:
|
||||||
|
keydict = valuestodict(tzlocalkey)
|
||||||
|
|
||||||
|
self._std_abbr = keydict["StandardName"]
|
||||||
|
self._dst_abbr = keydict["DaylightName"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
tzkeyname = text_type('{kn}\\{sn}').format(kn=TZKEYNAME,
|
||||||
|
sn=self._std_abbr)
|
||||||
|
with winreg.OpenKey(handle, tzkeyname) as tzkey:
|
||||||
|
_keydict = valuestodict(tzkey)
|
||||||
|
self._display = _keydict["Display"]
|
||||||
|
except OSError:
|
||||||
|
self._display = None
|
||||||
|
|
||||||
|
stdoffset = -keydict["Bias"]-keydict["StandardBias"]
|
||||||
|
dstoffset = stdoffset-keydict["DaylightBias"]
|
||||||
|
|
||||||
|
self._std_offset = datetime.timedelta(minutes=stdoffset)
|
||||||
|
self._dst_offset = datetime.timedelta(minutes=dstoffset)
|
||||||
|
|
||||||
|
# For reasons unclear, in this particular key, the day of week has been
|
||||||
|
# moved to the END of the SYSTEMTIME structure.
|
||||||
|
tup = struct.unpack("=8h", keydict["StandardStart"])
|
||||||
|
|
||||||
|
(self._stdmonth,
|
||||||
|
self._stdweeknumber, # Last = 5
|
||||||
|
self._stdhour,
|
||||||
|
self._stdminute) = tup[1:5]
|
||||||
|
|
||||||
|
self._stddayofweek = tup[7]
|
||||||
|
|
||||||
|
tup = struct.unpack("=8h", keydict["DaylightStart"])
|
||||||
|
|
||||||
|
(self._dstmonth,
|
||||||
|
self._dstweeknumber, # Last = 5
|
||||||
|
self._dsthour,
|
||||||
|
self._dstminute) = tup[1:5]
|
||||||
|
|
||||||
|
self._dstdayofweek = tup[7]
|
||||||
|
|
||||||
|
self._dst_base_offset_ = self._dst_offset - self._std_offset
|
||||||
|
self.hasdst = self._get_hasdst()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "tzwinlocal()"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
# str will return the standard name, not the daylight name.
|
||||||
|
return "tzwinlocal(%s)" % repr(self._std_abbr)
|
||||||
|
|
||||||
|
def __reduce__(self):
|
||||||
|
return (self.__class__, ())
|
||||||
|
|
||||||
|
|
||||||
|
def picknthweekday(year, month, dayofweek, hour, minute, whichweek):
|
||||||
|
""" dayofweek == 0 means Sunday, whichweek 5 means last instance """
|
||||||
|
first = datetime.datetime(year, month, 1, hour, minute)
|
||||||
|
|
||||||
|
# This will work if dayofweek is ISO weekday (1-7) or Microsoft-style (0-6),
|
||||||
|
# Because 7 % 7 = 0
|
||||||
|
weekdayone = first.replace(day=((dayofweek - first.isoweekday()) % 7) + 1)
|
||||||
|
wd = weekdayone + ((whichweek - 1) * ONEWEEK)
|
||||||
|
if (wd.month != month):
|
||||||
|
wd -= ONEWEEK
|
||||||
|
|
||||||
|
return wd
|
||||||
|
|
||||||
|
|
||||||
|
def valuestodict(key):
|
||||||
|
"""Convert a registry key's values to a dictionary."""
|
||||||
|
dout = {}
|
||||||
|
size = winreg.QueryInfoKey(key)[1]
|
||||||
|
tz_res = None
|
||||||
|
|
||||||
|
for i in range(size):
|
||||||
|
key_name, value, dtype = winreg.EnumValue(key, i)
|
||||||
|
if dtype == winreg.REG_DWORD or dtype == winreg.REG_DWORD_LITTLE_ENDIAN:
|
||||||
|
# If it's a DWORD (32-bit integer), it's stored as unsigned - convert
|
||||||
|
# that to a proper signed integer
|
||||||
|
if value & (1 << 31):
|
||||||
|
value = value - (1 << 32)
|
||||||
|
elif dtype == winreg.REG_SZ:
|
||||||
|
# If it's a reference to the tzres DLL, load the actual string
|
||||||
|
if value.startswith('@tzres'):
|
||||||
|
tz_res = tz_res or tzres()
|
||||||
|
value = tz_res.name_from_string(value)
|
||||||
|
|
||||||
|
value = value.rstrip('\x00') # Remove trailing nulls
|
||||||
|
|
||||||
|
dout[key_name] = value
|
||||||
|
|
||||||
|
return dout
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
# tzwin has moved to dateutil.tz.win
|
# tzwin has moved to dateutil.tz.win
|
||||||
from .tz.win import *
|
from .tz.win import *
|
||||||
|
|
12
lib/dateutil/utils.py
Normal file
12
lib/dateutil/utils.py
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|
||||||
|
def within_delta(dt1, dt2, delta):
|
||||||
|
"""
|
||||||
|
Useful for comparing two datetimes that may a negilible difference
|
||||||
|
to be considered equal.
|
||||||
|
"""
|
||||||
|
delta = abs(delta)
|
||||||
|
difference = dt1 - dt2
|
||||||
|
return -delta <= difference <= delta
|
|
@ -1,107 +1,168 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import logging
|
import warnings
|
||||||
import os
|
import json
|
||||||
import warnings
|
|
||||||
import tempfile
|
from tarfile import TarFile
|
||||||
import shutil
|
from pkgutil import get_data
|
||||||
import json
|
from io import BytesIO
|
||||||
|
from contextlib import closing
|
||||||
from subprocess import check_call
|
|
||||||
from tarfile import TarFile
|
from dateutil.tz import tzfile
|
||||||
from pkgutil import get_data
|
|
||||||
from io import BytesIO
|
__all__ = ["get_zonefile_instance", "gettz", "gettz_db_metadata", "rebuild"]
|
||||||
from contextlib import closing
|
|
||||||
|
ZONEFILENAME = "dateutil-zoneinfo.tar.gz"
|
||||||
from dateutil.tz import tzfile
|
METADATA_FN = 'METADATA'
|
||||||
|
|
||||||
from sickbeard import encodingKludge as ek
|
|
||||||
import sickbeard
|
class tzfile(tzfile):
|
||||||
|
def __reduce__(self):
|
||||||
__all__ = ["gettz", "gettz_db_metadata", "rebuild"]
|
return (gettz, (self._filename,))
|
||||||
|
|
||||||
ZONEFILENAME = "dateutil-zoneinfo.tar.gz"
|
|
||||||
METADATA_FN = 'METADATA'
|
def getzoneinfofile_stream():
|
||||||
|
try:
|
||||||
# python2.6 compatability. Note that TarFile.__exit__ != TarFile.close, but
|
return BytesIO(get_data(__name__, ZONEFILENAME))
|
||||||
# it's close enough for python2.6
|
except IOError as e: # TODO switch to FileNotFoundError?
|
||||||
tar_open = TarFile.open
|
warnings.warn("I/O error({0}): {1}".format(e.errno, e.strerror))
|
||||||
if not hasattr(TarFile, '__exit__'):
|
return None
|
||||||
def tar_open(*args, **kwargs):
|
|
||||||
return closing(TarFile.open(*args, **kwargs))
|
|
||||||
|
class ZoneInfoFile(object):
|
||||||
|
def __init__(self, zonefile_stream=None):
|
||||||
class tzfile(tzfile):
|
if zonefile_stream is not None:
|
||||||
def __reduce__(self):
|
with TarFile.open(fileobj=zonefile_stream) as tf:
|
||||||
return (gettz, (self._filename,))
|
self.zones = {zf.name: tzfile(tf.extractfile(zf), filename=zf.name)
|
||||||
|
for zf in tf.getmembers()
|
||||||
|
if zf.isfile() and zf.name != METADATA_FN}
|
||||||
def getzoneinfofile_stream():
|
# deal with links: They'll point to their parent object. Less
|
||||||
try:
|
# waste of memory
|
||||||
# return BytesIO(get_data(__name__, ZONEFILENAME))
|
links = {zl.name: self.zones[zl.linkname]
|
||||||
with open(ek.ek(os.path.join, sickbeard.ZONEINFO_DIR, ZONEFILENAME), 'rb') as f:
|
for zl in tf.getmembers() if
|
||||||
return BytesIO(f.read())
|
zl.islnk() or zl.issym()}
|
||||||
except IOError as e: # TODO switch to FileNotFoundError?
|
self.zones.update(links)
|
||||||
warnings.warn("I/O error({0}): {1}".format(e.errno, e.strerror))
|
try:
|
||||||
return None
|
metadata_json = tf.extractfile(tf.getmember(METADATA_FN))
|
||||||
|
metadata_str = metadata_json.read().decode('UTF-8')
|
||||||
|
self.metadata = json.loads(metadata_str)
|
||||||
class ZoneInfoFile(object):
|
except KeyError:
|
||||||
def __init__(self, zonefile_stream=None):
|
# no metadata in tar file
|
||||||
if zonefile_stream is not None:
|
self.metadata = None
|
||||||
with tar_open(fileobj=zonefile_stream, mode='r') as tf:
|
else:
|
||||||
# dict comprehension does not work on python2.6
|
self.zones = {}
|
||||||
# TODO: get back to the nicer syntax when we ditch python2.6
|
self.metadata = None
|
||||||
# self.zones = {zf.name: tzfile(tf.extractfile(zf),
|
|
||||||
# filename = zf.name)
|
def get(self, name, default=None):
|
||||||
# for zf in tf.getmembers() if zf.isfile()}
|
"""
|
||||||
self.zones = dict((zf.name, tzfile(tf.extractfile(zf),
|
Wrapper for :func:`ZoneInfoFile.zones.get`. This is a convenience method
|
||||||
filename=zf.name))
|
for retrieving zones from the zone dictionary.
|
||||||
for zf in tf.getmembers()
|
|
||||||
if zf.isfile() and zf.name != METADATA_FN)
|
:param name:
|
||||||
# deal with links: They'll point to their parent object. Less
|
The name of the zone to retrieve. (Generally IANA zone names)
|
||||||
# waste of memory
|
|
||||||
# links = {zl.name: self.zones[zl.linkname]
|
:param default:
|
||||||
# for zl in tf.getmembers() if zl.islnk() or zl.issym()}
|
The value to return in the event of a missing key.
|
||||||
links = dict((zl.name, self.zones[zl.linkname])
|
|
||||||
for zl in tf.getmembers() if
|
.. versionadded:: 2.6.0
|
||||||
zl.islnk() or zl.issym())
|
|
||||||
self.zones.update(links)
|
"""
|
||||||
try:
|
return self.zones.get(name, default)
|
||||||
metadata_json = tf.extractfile(tf.getmember(METADATA_FN))
|
|
||||||
metadata_str = metadata_json.read().decode('UTF-8')
|
|
||||||
self.metadata = json.loads(metadata_str)
|
# The current API has gettz as a module function, although in fact it taps into
|
||||||
except KeyError:
|
# a stateful class. So as a workaround for now, without changing the API, we
|
||||||
# no metadata in tar file
|
# will create a new "global" class instance the first time a user requests a
|
||||||
self.metadata = None
|
# timezone. Ugly, but adheres to the api.
|
||||||
else:
|
#
|
||||||
self.zones = dict()
|
# TODO: Remove after deprecation period.
|
||||||
self.metadata = None
|
_CLASS_ZONE_INSTANCE = []
|
||||||
|
|
||||||
|
|
||||||
# The current API has gettz as a module function, although in fact it taps into
|
def get_zonefile_instance(new_instance=False):
|
||||||
# a stateful class. So as a workaround for now, without changing the API, we
|
"""
|
||||||
# will create a new "global" class instance the first time a user requests a
|
This is a convenience function which provides a :class:`ZoneInfoFile`
|
||||||
# timezone. Ugly, but adheres to the api.
|
instance using the data provided by the ``dateutil`` package. By default, it
|
||||||
#
|
caches a single instance of the ZoneInfoFile object and returns that.
|
||||||
# TODO: deprecate this.
|
|
||||||
_CLASS_ZONE_INSTANCE = list()
|
:param new_instance:
|
||||||
|
If ``True``, a new instance of :class:`ZoneInfoFile` is instantiated and
|
||||||
|
used as the cached instance for the next call. Otherwise, new instances
|
||||||
def gettz(name):
|
are created only as necessary.
|
||||||
if len(_CLASS_ZONE_INSTANCE) == 0:
|
|
||||||
_CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream()))
|
:return:
|
||||||
return _CLASS_ZONE_INSTANCE[0].zones.get(name)
|
Returns a :class:`ZoneInfoFile` object.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6
|
||||||
def gettz_db_metadata():
|
"""
|
||||||
""" Get the zonefile metadata
|
if new_instance:
|
||||||
|
zif = None
|
||||||
See `zonefile_metadata`_
|
else:
|
||||||
|
zif = getattr(get_zonefile_instance, '_cached_instance', None)
|
||||||
:returns: A dictionary with the database metadata
|
|
||||||
"""
|
if zif is None:
|
||||||
if len(_CLASS_ZONE_INSTANCE) == 0:
|
zif = ZoneInfoFile(getzoneinfofile_stream())
|
||||||
_CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream()))
|
|
||||||
return _CLASS_ZONE_INSTANCE[0].metadata
|
get_zonefile_instance._cached_instance = zif
|
||||||
|
|
||||||
|
return zif
|
||||||
|
|
||||||
|
|
||||||
|
def gettz(name):
|
||||||
|
"""
|
||||||
|
This retrieves a time zone from the local zoneinfo tarball that is packaged
|
||||||
|
with dateutil.
|
||||||
|
|
||||||
|
:param name:
|
||||||
|
An IANA-style time zone name, as found in the zoneinfo file.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
Returns a :class:`dateutil.tz.tzfile` time zone object.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
It is generally inadvisable to use this function, and it is only
|
||||||
|
provided for API compatibility with earlier versions. This is *not*
|
||||||
|
equivalent to ``dateutil.tz.gettz()``, which selects an appropriate
|
||||||
|
time zone based on the inputs, favoring system zoneinfo. This is ONLY
|
||||||
|
for accessing the dateutil-specific zoneinfo (which may be out of
|
||||||
|
date compared to the system zoneinfo).
|
||||||
|
|
||||||
|
.. deprecated:: 2.6
|
||||||
|
If you need to use a specific zoneinfofile over the system zoneinfo,
|
||||||
|
instantiate a :class:`dateutil.zoneinfo.ZoneInfoFile` object and call
|
||||||
|
:func:`dateutil.zoneinfo.ZoneInfoFile.get(name)` instead.
|
||||||
|
|
||||||
|
Use :func:`get_zonefile_instance` to retrieve an instance of the
|
||||||
|
dateutil-provided zoneinfo.
|
||||||
|
"""
|
||||||
|
warnings.warn("zoneinfo.gettz() will be removed in future versions, "
|
||||||
|
"to use the dateutil-provided zoneinfo files, instantiate a "
|
||||||
|
"ZoneInfoFile object and use ZoneInfoFile.zones.get() "
|
||||||
|
"instead. See the documentation for details.",
|
||||||
|
DeprecationWarning)
|
||||||
|
|
||||||
|
if len(_CLASS_ZONE_INSTANCE) == 0:
|
||||||
|
_CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream()))
|
||||||
|
return _CLASS_ZONE_INSTANCE[0].zones.get(name)
|
||||||
|
|
||||||
|
|
||||||
|
def gettz_db_metadata():
|
||||||
|
""" Get the zonefile metadata
|
||||||
|
|
||||||
|
See `zonefile_metadata`_
|
||||||
|
|
||||||
|
:returns:
|
||||||
|
A dictionary with the database metadata
|
||||||
|
|
||||||
|
.. deprecated:: 2.6
|
||||||
|
See deprecation warning in :func:`zoneinfo.gettz`. To get metadata,
|
||||||
|
query the attribute ``zoneinfo.ZoneInfoFile.metadata``.
|
||||||
|
"""
|
||||||
|
warnings.warn("zoneinfo.gettz_db_metadata() will be removed in future "
|
||||||
|
"versions, to use the dateutil-provided zoneinfo files, "
|
||||||
|
"ZoneInfoFile object and query the 'metadata' attribute "
|
||||||
|
"instead. See the documentation for details.",
|
||||||
|
DeprecationWarning)
|
||||||
|
|
||||||
|
if len(_CLASS_ZONE_INSTANCE) == 0:
|
||||||
|
_CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream()))
|
||||||
|
return _CLASS_ZONE_INSTANCE[0].metadata
|
||||||
|
|
|
@ -1,51 +1,53 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
import shutil
|
import shutil
|
||||||
import json
|
import json
|
||||||
from subprocess import check_call
|
from subprocess import check_call
|
||||||
|
from tarfile import TarFile
|
||||||
from dateutil.zoneinfo import tar_open, METADATA_FN, ZONEFILENAME
|
|
||||||
|
from dateutil.zoneinfo import METADATA_FN, ZONEFILENAME
|
||||||
|
|
||||||
def rebuild(filename, tag=None, format="gz", zonegroups=[], metadata=None):
|
|
||||||
"""Rebuild the internal timezone info in dateutil/zoneinfo/zoneinfo*tar*
|
def rebuild(filename, tag=None, format="gz", zonegroups=[], metadata=None):
|
||||||
|
"""Rebuild the internal timezone info in dateutil/zoneinfo/zoneinfo*tar*
|
||||||
filename is the timezone tarball from ftp.iana.org/tz.
|
|
||||||
|
filename is the timezone tarball from ftp.iana.org/tz.
|
||||||
"""
|
|
||||||
tmpdir = tempfile.mkdtemp()
|
"""
|
||||||
zonedir = os.path.join(tmpdir, "zoneinfo")
|
tmpdir = tempfile.mkdtemp()
|
||||||
moduledir = os.path.dirname(__file__)
|
zonedir = os.path.join(tmpdir, "zoneinfo")
|
||||||
try:
|
moduledir = os.path.dirname(__file__)
|
||||||
with tar_open(filename) as tf:
|
try:
|
||||||
for name in zonegroups:
|
with TarFile.open(filename) as tf:
|
||||||
tf.extract(name, tmpdir)
|
for name in zonegroups:
|
||||||
filepaths = [os.path.join(tmpdir, n) for n in zonegroups]
|
tf.extract(name, tmpdir)
|
||||||
try:
|
filepaths = [os.path.join(tmpdir, n) for n in zonegroups]
|
||||||
check_call(["zic", "-d", zonedir] + filepaths)
|
try:
|
||||||
except OSError as e:
|
check_call(["zic", "-d", zonedir] + filepaths)
|
||||||
_print_on_nosuchfile(e)
|
except OSError as e:
|
||||||
raise
|
_print_on_nosuchfile(e)
|
||||||
# write metadata file
|
raise
|
||||||
with open(os.path.join(zonedir, METADATA_FN), 'w') as f:
|
# write metadata file
|
||||||
json.dump(metadata, f, indent=4, sort_keys=True)
|
with open(os.path.join(zonedir, METADATA_FN), 'w') as f:
|
||||||
target = os.path.join(moduledir, ZONEFILENAME)
|
json.dump(metadata, f, indent=4, sort_keys=True)
|
||||||
with tar_open(target, "w:%s" % format) as tf:
|
target = os.path.join(moduledir, ZONEFILENAME)
|
||||||
for entry in os.listdir(zonedir):
|
with TarFile.open(target, "w:%s" % format) as tf:
|
||||||
entrypath = os.path.join(zonedir, entry)
|
for entry in os.listdir(zonedir):
|
||||||
tf.add(entrypath, entry)
|
entrypath = os.path.join(zonedir, entry)
|
||||||
finally:
|
tf.add(entrypath, entry)
|
||||||
shutil.rmtree(tmpdir)
|
finally:
|
||||||
|
shutil.rmtree(tmpdir)
|
||||||
def _print_on_nosuchfile(e):
|
|
||||||
"""Print helpful troubleshooting message
|
|
||||||
|
def _print_on_nosuchfile(e):
|
||||||
e is an exception raised by subprocess.check_call()
|
"""Print helpful troubleshooting message
|
||||||
|
|
||||||
"""
|
e is an exception raised by subprocess.check_call()
|
||||||
if e.errno == 2:
|
|
||||||
logging.error(
|
"""
|
||||||
"Could not find zic. Perhaps you need to install "
|
if e.errno == 2:
|
||||||
"libc-bin or some other package that provides it, "
|
logging.error(
|
||||||
"or it's not in your PATH?")
|
"Could not find zic. Perhaps you need to install "
|
||||||
|
"libc-bin or some other package that provides it, "
|
||||||
|
"or it's not in your PATH?")
|
||||||
|
|
Loading…
Reference in a new issue