mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-07 21:51:14 -07:00
parent
045bfb9502
commit
c0fb1dbbdc
20 changed files with 333 additions and 1122 deletions
|
@ -1,34 +0,0 @@
|
|||
__all__ = [
|
||||
"AmbiguousTimeError",
|
||||
"NonExistentTimeError",
|
||||
"InvalidTimeError",
|
||||
"UnknownTimeZoneError",
|
||||
"PytzUsageWarning",
|
||||
"FixedOffset",
|
||||
"UTC",
|
||||
"utc",
|
||||
"build_tzinfo",
|
||||
"timezone",
|
||||
"fixed_offset_timezone",
|
||||
"wrap_zone",
|
||||
]
|
||||
|
||||
from . import helpers
|
||||
from ._exceptions import (
|
||||
AmbiguousTimeError,
|
||||
InvalidTimeError,
|
||||
NonExistentTimeError,
|
||||
PytzUsageWarning,
|
||||
UnknownTimeZoneError,
|
||||
)
|
||||
from ._impl import (
|
||||
UTC,
|
||||
build_tzinfo,
|
||||
fixed_offset_timezone,
|
||||
timezone,
|
||||
wrap_zone,
|
||||
)
|
||||
|
||||
# Compatibility aliases
|
||||
utc = UTC
|
||||
FixedOffset = fixed_offset_timezone
|
|
@ -1,13 +0,0 @@
|
|||
import sys
|
||||
|
||||
_PYTZ_IMPORTED = False
|
||||
|
||||
|
||||
def pytz_imported():
|
||||
"""Detects whether or not pytz has been imported without importing pytz."""
|
||||
global _PYTZ_IMPORTED
|
||||
|
||||
if not _PYTZ_IMPORTED and "pytz" in sys.modules:
|
||||
_PYTZ_IMPORTED = True
|
||||
|
||||
return _PYTZ_IMPORTED
|
|
@ -1,15 +0,0 @@
|
|||
import sys
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
from . import _compat_py2 as _compat_impl
|
||||
else:
|
||||
from . import _compat_py3 as _compat_impl
|
||||
|
||||
UTC = _compat_impl.UTC
|
||||
get_timezone = _compat_impl.get_timezone
|
||||
get_timezone_file = _compat_impl.get_timezone_file
|
||||
get_fixed_offset_zone = _compat_impl.get_fixed_offset_zone
|
||||
is_ambiguous = _compat_impl.is_ambiguous
|
||||
is_imaginary = _compat_impl.is_imaginary
|
||||
enfold = _compat_impl.enfold
|
||||
get_fold = _compat_impl.get_fold
|
|
@ -1,43 +0,0 @@
|
|||
from datetime import timedelta
|
||||
|
||||
from dateutil import tz
|
||||
|
||||
UTC = tz.UTC
|
||||
|
||||
|
||||
def get_timezone(key):
|
||||
if not key:
|
||||
raise KeyError("Unknown time zone: %s" % key)
|
||||
|
||||
try:
|
||||
rv = tz.gettz(key)
|
||||
except Exception:
|
||||
rv = None
|
||||
|
||||
if rv is None or not isinstance(rv, (tz.tzutc, tz.tzfile)):
|
||||
raise KeyError("Unknown time zone: %s" % key)
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
def get_timezone_file(f, key=None):
|
||||
return tz.tzfile(f)
|
||||
|
||||
|
||||
def get_fixed_offset_zone(offset):
|
||||
return tz.tzoffset(None, timedelta(minutes=offset))
|
||||
|
||||
|
||||
def is_ambiguous(dt):
|
||||
return tz.datetime_ambiguous(dt)
|
||||
|
||||
|
||||
def is_imaginary(dt):
|
||||
return not tz.datetime_exists(dt)
|
||||
|
||||
|
||||
enfold = tz.enfold
|
||||
|
||||
|
||||
def get_fold(dt):
|
||||
return getattr(dt, "fold", 0)
|
|
@ -1,58 +0,0 @@
|
|||
# Note: This file could use Python 3-only syntax, but at the moment this breaks
|
||||
# the coverage job on Python 2. Until we make it so that coverage can ignore
|
||||
# this file only on Python 2, we'll have to stick to 2/3-compatible syntax.
|
||||
try:
|
||||
import zoneinfo
|
||||
except ImportError:
|
||||
from backports import zoneinfo
|
||||
|
||||
import datetime
|
||||
|
||||
UTC = datetime.timezone.utc
|
||||
|
||||
|
||||
def get_timezone(key):
|
||||
try:
|
||||
return zoneinfo.ZoneInfo(key)
|
||||
except (ValueError, OSError):
|
||||
# TODO: Use `from e` when this file can use Python 3 syntax
|
||||
raise KeyError(key)
|
||||
|
||||
|
||||
def get_timezone_file(f, key=None):
|
||||
return zoneinfo.ZoneInfo.from_file(f, key=key)
|
||||
|
||||
|
||||
def get_fixed_offset_zone(offset):
|
||||
return datetime.timezone(datetime.timedelta(minutes=offset))
|
||||
|
||||
|
||||
def is_imaginary(dt):
|
||||
dt_rt = dt.astimezone(UTC).astimezone(dt.tzinfo)
|
||||
|
||||
return not (dt == dt_rt)
|
||||
|
||||
|
||||
def is_ambiguous(dt):
|
||||
if is_imaginary(dt):
|
||||
return False
|
||||
|
||||
wall_0 = dt
|
||||
wall_1 = dt.replace(fold=not dt.fold)
|
||||
|
||||
# Ambiguous datetimes can only exist if the offset changes, so we don't
|
||||
# actually have to check whether dst() or tzname() are different.
|
||||
same_offset = wall_0.utcoffset() == wall_1.utcoffset()
|
||||
|
||||
return not same_offset
|
||||
|
||||
|
||||
def enfold(dt, fold=1):
|
||||
if dt.fold != fold:
|
||||
return dt.replace(fold=fold)
|
||||
else:
|
||||
return dt
|
||||
|
||||
|
||||
def get_fold(dt):
|
||||
return dt.fold
|
|
@ -1,75 +0,0 @@
|
|||
from ._common import pytz_imported
|
||||
|
||||
|
||||
class PytzUsageWarning(RuntimeWarning):
|
||||
"""Warning raised when accessing features specific to ``pytz``'s interface.
|
||||
|
||||
This warning is used to direct users of ``pytz``-specific features like the
|
||||
``localize`` and ``normalize`` methods towards using the standard
|
||||
``tzinfo`` interface, so that these shims can be replaced with one of the
|
||||
underlying libraries they are wrapping.
|
||||
"""
|
||||
|
||||
|
||||
class UnknownTimeZoneError(KeyError):
|
||||
"""Raised when no time zone is found for a specified key."""
|
||||
|
||||
|
||||
class InvalidTimeError(Exception):
|
||||
"""The base class for exceptions related to folds and gaps."""
|
||||
|
||||
|
||||
class AmbiguousTimeError(InvalidTimeError):
|
||||
"""Exception raised when ``is_dst=None`` for an ambiguous time (fold)."""
|
||||
|
||||
|
||||
class NonExistentTimeError(InvalidTimeError):
|
||||
"""Exception raised when ``is_dst=None`` for a non-existent time (gap)."""
|
||||
|
||||
|
||||
PYTZ_BASE_ERROR_MAPPING = {}
|
||||
|
||||
|
||||
def _make_pytz_derived_errors(
|
||||
InvalidTimeError_=InvalidTimeError,
|
||||
AmbiguousTimeError_=AmbiguousTimeError,
|
||||
NonExistentTimeError_=NonExistentTimeError,
|
||||
UnknownTimeZoneError_=UnknownTimeZoneError,
|
||||
):
|
||||
if PYTZ_BASE_ERROR_MAPPING or not pytz_imported():
|
||||
return
|
||||
|
||||
import pytz
|
||||
|
||||
class InvalidTimeError(InvalidTimeError_, pytz.InvalidTimeError):
|
||||
pass
|
||||
|
||||
class AmbiguousTimeError(AmbiguousTimeError_, pytz.AmbiguousTimeError):
|
||||
pass
|
||||
|
||||
class NonExistentTimeError(
|
||||
NonExistentTimeError_, pytz.NonExistentTimeError
|
||||
):
|
||||
pass
|
||||
|
||||
class UnknownTimeZoneError(
|
||||
UnknownTimeZoneError_, pytz.UnknownTimeZoneError
|
||||
):
|
||||
pass
|
||||
|
||||
PYTZ_BASE_ERROR_MAPPING.update(
|
||||
{
|
||||
InvalidTimeError_: InvalidTimeError,
|
||||
AmbiguousTimeError_: AmbiguousTimeError,
|
||||
NonExistentTimeError_: NonExistentTimeError,
|
||||
UnknownTimeZoneError_: UnknownTimeZoneError,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def get_exception(exc_type, msg):
|
||||
_make_pytz_derived_errors()
|
||||
|
||||
out_exc_type = PYTZ_BASE_ERROR_MAPPING.get(exc_type, exc_type)
|
||||
|
||||
return out_exc_type(msg)
|
|
@ -1,296 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import warnings
|
||||
from datetime import tzinfo
|
||||
|
||||
from . import _compat
|
||||
from ._exceptions import (
|
||||
AmbiguousTimeError,
|
||||
NonExistentTimeError,
|
||||
PytzUsageWarning,
|
||||
UnknownTimeZoneError,
|
||||
get_exception,
|
||||
)
|
||||
|
||||
IS_DST_SENTINEL = object()
|
||||
KEY_SENTINEL = object()
|
||||
|
||||
|
||||
def timezone(key, _cache={}):
|
||||
"""Builds an IANA database time zone shim.
|
||||
|
||||
This is the equivalent of ``pytz.timezone``.
|
||||
|
||||
:param key:
|
||||
A valid key from the IANA time zone database.
|
||||
|
||||
:raises UnknownTimeZoneError:
|
||||
If an unknown value is passed, this will raise an exception that can be
|
||||
caught by :exc:`pytz_deprecation_shim.UnknownTimeZoneError` or
|
||||
``pytz.UnknownTimeZoneError``. Like
|
||||
:exc:`zoneinfo.ZoneInfoNotFoundError`, both of those are subclasses of
|
||||
:exc:`KeyError`.
|
||||
"""
|
||||
instance = _cache.get(key, None)
|
||||
if instance is None:
|
||||
if len(key) == 3 and key.lower() == "utc":
|
||||
instance = _cache.setdefault(key, UTC)
|
||||
else:
|
||||
try:
|
||||
zone = _compat.get_timezone(key)
|
||||
except KeyError:
|
||||
raise get_exception(UnknownTimeZoneError, key)
|
||||
instance = _cache.setdefault(key, wrap_zone(zone, key=key))
|
||||
|
||||
return instance
|
||||
|
||||
|
||||
def fixed_offset_timezone(offset, _cache={}):
|
||||
"""Builds a fixed offset time zone shim.
|
||||
|
||||
This is the equivalent of ``pytz.FixedOffset``. An alias is available as
|
||||
``pytz_deprecation_shim.FixedOffset`` as well.
|
||||
|
||||
:param offset:
|
||||
A fixed offset from UTC, in minutes. This must be in the range ``-1439
|
||||
<= offset <= 1439``.
|
||||
|
||||
:raises ValueError:
|
||||
For offsets whose absolute value is greater than or equal to 24 hours.
|
||||
|
||||
:return:
|
||||
A shim time zone.
|
||||
"""
|
||||
if not (-1440 < offset < 1440):
|
||||
raise ValueError("absolute offset is too large", offset)
|
||||
|
||||
instance = _cache.get(offset, None)
|
||||
if instance is None:
|
||||
if offset == 0:
|
||||
instance = _cache.setdefault(offset, UTC)
|
||||
else:
|
||||
zone = _compat.get_fixed_offset_zone(offset)
|
||||
instance = _cache.setdefault(offset, wrap_zone(zone, key=None))
|
||||
|
||||
return instance
|
||||
|
||||
|
||||
def build_tzinfo(zone, fp):
|
||||
"""Builds a shim object from a TZif file.
|
||||
|
||||
This is a shim for ``pytz.build_tzinfo``. Given a value to use as the zone
|
||||
IANA key and a file-like object containing a valid TZif file (i.e.
|
||||
conforming to :rfc:`8536`), this builds a time zone object and wraps it in
|
||||
a shim class.
|
||||
|
||||
The argument names are chosen to match those in ``pytz.build_tzinfo``.
|
||||
|
||||
:param zone:
|
||||
A string to be used as the time zone object's IANA key.
|
||||
|
||||
:param fp:
|
||||
A readable file-like object emitting bytes, pointing to a valid TZif
|
||||
file.
|
||||
|
||||
:return:
|
||||
A shim time zone.
|
||||
"""
|
||||
zone_file = _compat.get_timezone_file(fp)
|
||||
|
||||
return wrap_zone(zone_file, key=zone)
|
||||
|
||||
|
||||
def wrap_zone(tz, key=KEY_SENTINEL, _cache={}):
|
||||
"""Wrap an existing time zone object in a shim class.
|
||||
|
||||
This is likely to be useful if you would like to work internally with
|
||||
non-``pytz`` zones, but you expose an interface to callers relying on
|
||||
``pytz``'s interface. It may also be useful for passing non-``pytz`` zones
|
||||
to libraries expecting to use ``pytz``'s interface.
|
||||
|
||||
:param tz:
|
||||
A :pep:`495`-compatible time zone, such as those provided by
|
||||
:mod:`dateutil.tz` or :mod:`zoneinfo`.
|
||||
|
||||
:param key:
|
||||
The value for the IANA time zone key. This is optional for ``zoneinfo``
|
||||
zones, but required for ``dateutil.tz`` zones.
|
||||
|
||||
:return:
|
||||
A shim time zone.
|
||||
"""
|
||||
if key is KEY_SENTINEL:
|
||||
key = getattr(tz, "key", KEY_SENTINEL)
|
||||
|
||||
if key is KEY_SENTINEL:
|
||||
raise TypeError(
|
||||
"The `key` argument is required when wrapping zones that do not "
|
||||
+ "have a `key` attribute."
|
||||
)
|
||||
|
||||
instance = _cache.get((id(tz), key), None)
|
||||
if instance is None:
|
||||
instance = _cache.setdefault((id(tz), key), _PytzShimTimezone(tz, key))
|
||||
|
||||
return instance
|
||||
|
||||
|
||||
class _PytzShimTimezone(tzinfo):
|
||||
# Add instance variables for _zone and _key because this will make error
|
||||
# reporting with partially-initialized _BasePytzShimTimezone objects
|
||||
# work better.
|
||||
_zone = None
|
||||
_key = None
|
||||
|
||||
def __init__(self, zone, key):
|
||||
self._key = key
|
||||
self._zone = zone
|
||||
|
||||
def utcoffset(self, dt):
|
||||
return self._zone.utcoffset(dt)
|
||||
|
||||
def dst(self, dt):
|
||||
return self._zone.dst(dt)
|
||||
|
||||
def tzname(self, dt):
|
||||
return self._zone.tzname(dt)
|
||||
|
||||
def fromutc(self, dt):
|
||||
# The default fromutc implementation only works if tzinfo is "self"
|
||||
dt_base = dt.replace(tzinfo=self._zone)
|
||||
dt_out = self._zone.fromutc(dt_base)
|
||||
|
||||
return dt_out.replace(tzinfo=self)
|
||||
|
||||
def __str__(self):
|
||||
if self._key is not None:
|
||||
return str(self._key)
|
||||
else:
|
||||
return repr(self)
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%s, %s)" % (
|
||||
self.__class__.__name__,
|
||||
repr(self._zone),
|
||||
repr(self._key),
|
||||
)
|
||||
|
||||
def unwrap_shim(self):
|
||||
"""Returns the underlying class that the shim is a wrapper for.
|
||||
|
||||
This is a shim-specific method equivalent to
|
||||
:func:`pytz_deprecation_shim.helpers.upgrade_tzinfo`. It is provided as
|
||||
a method to allow end-users to upgrade shim timezones without requiring
|
||||
an explicit dependency on ``pytz_deprecation_shim``, e.g.:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
if getattr(tz, "unwrap_shim", None) is None:
|
||||
tz = tz.unwrap_shim()
|
||||
"""
|
||||
return self._zone
|
||||
|
||||
@property
|
||||
def zone(self):
|
||||
warnings.warn(
|
||||
"The zone attribute is specific to pytz's interface; "
|
||||
+ "please migrate to a new time zone provider. "
|
||||
+ "For more details on how to do so, see %s"
|
||||
% PYTZ_MIGRATION_GUIDE_URL,
|
||||
PytzUsageWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
return self._key
|
||||
|
||||
def localize(self, dt, is_dst=IS_DST_SENTINEL):
|
||||
warnings.warn(
|
||||
"The localize method is no longer necessary, as this "
|
||||
+ "time zone supports the fold attribute (PEP 495). "
|
||||
+ "For more details on migrating to a PEP 495-compliant "
|
||||
+ "implementation, see %s" % PYTZ_MIGRATION_GUIDE_URL,
|
||||
PytzUsageWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
if dt.tzinfo is not None:
|
||||
raise ValueError("Not naive datetime (tzinfo is already set)")
|
||||
|
||||
dt_out = dt.replace(tzinfo=self)
|
||||
|
||||
if is_dst is IS_DST_SENTINEL:
|
||||
return dt_out
|
||||
|
||||
dt_ambiguous = _compat.is_ambiguous(dt_out)
|
||||
dt_imaginary = (
|
||||
_compat.is_imaginary(dt_out) if not dt_ambiguous else False
|
||||
)
|
||||
|
||||
if is_dst is None:
|
||||
if dt_imaginary:
|
||||
raise get_exception(
|
||||
NonExistentTimeError, dt.replace(tzinfo=None)
|
||||
)
|
||||
|
||||
if dt_ambiguous:
|
||||
raise get_exception(AmbiguousTimeError, dt.replace(tzinfo=None))
|
||||
|
||||
elif dt_ambiguous or dt_imaginary:
|
||||
# Start by normalizing the folds; dt_out may have fold=0 or fold=1,
|
||||
# but we need to know the DST offset on both sides anyway, so we
|
||||
# will get one datetime representing each side of the fold, then
|
||||
# decide which one we're going to return.
|
||||
if _compat.get_fold(dt_out):
|
||||
dt_enfolded = dt_out
|
||||
dt_out = _compat.enfold(dt_out, fold=0)
|
||||
else:
|
||||
dt_enfolded = _compat.enfold(dt_out, fold=1)
|
||||
|
||||
# Now we want to decide whether the fold=0 or fold=1 represents
|
||||
# what pytz would return for `is_dst=True`
|
||||
enfolded_dst = bool(dt_enfolded.dst())
|
||||
if bool(dt_out.dst()) == enfolded_dst:
|
||||
# If this is not a transition between standard time and
|
||||
# daylight saving time, pytz will consider the larger offset
|
||||
# the DST offset.
|
||||
enfolded_dst = dt_enfolded.utcoffset() > dt_out.utcoffset()
|
||||
|
||||
# The default we've established is that dt_out is fold=0; swap it
|
||||
# for the fold=1 datetime if is_dst == True and the enfolded side
|
||||
# is DST or if is_dst == False and the enfolded side is *not* DST.
|
||||
if is_dst == enfolded_dst:
|
||||
dt_out = dt_enfolded
|
||||
|
||||
return dt_out
|
||||
|
||||
def normalize(self, dt):
|
||||
warnings.warn(
|
||||
"The normalize method is no longer necessary, as this "
|
||||
+ "time zone supports the fold attribute (PEP 495). "
|
||||
+ "For more details on migrating to a PEP 495-compliant "
|
||||
+ "implementation, see %s" % PYTZ_MIGRATION_GUIDE_URL,
|
||||
PytzUsageWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
if dt.tzinfo is None:
|
||||
raise ValueError("Naive time - no tzinfo set")
|
||||
|
||||
if dt.tzinfo is self:
|
||||
return dt
|
||||
|
||||
return dt.astimezone(self)
|
||||
|
||||
def __copy__(self):
|
||||
return self
|
||||
|
||||
def __deepcopy__(self, memo=None):
|
||||
return self
|
||||
|
||||
def __reduce__(self):
|
||||
return wrap_zone, (self._zone, self._key)
|
||||
|
||||
|
||||
UTC = wrap_zone(_compat.UTC, "UTC")
|
||||
PYTZ_MIGRATION_GUIDE_URL = (
|
||||
"https://pytz-deprecation-shim.readthedocs.io/en/latest/migration.html"
|
||||
)
|
|
@ -1,90 +0,0 @@
|
|||
"""
|
||||
This module contains helper functions to ease the transition from ``pytz`` to
|
||||
another :pep:`495`-compatible library.
|
||||
"""
|
||||
from . import _common, _compat
|
||||
from ._impl import _PytzShimTimezone
|
||||
|
||||
_PYTZ_BASE_CLASSES = None
|
||||
|
||||
|
||||
def is_pytz_zone(tz):
|
||||
"""Check if a time zone is a ``pytz`` time zone.
|
||||
|
||||
This will only import ``pytz`` if it has already been imported, and does
|
||||
not rely on the existence of the ``localize`` or ``normalize`` methods
|
||||
(since the shim classes also have these methods, but are not ``pytz``
|
||||
zones).
|
||||
"""
|
||||
|
||||
# If pytz is not in sys.modules, then we will assume the time zone is not a
|
||||
# pytz zone. It is possible that someone has manipulated sys.modules to
|
||||
# remove pytz, but that's the kind of thing that causes all kinds of other
|
||||
# problems anyway, so we'll call that an unsupported configuration.
|
||||
if not _common.pytz_imported():
|
||||
return False
|
||||
|
||||
if _PYTZ_BASE_CLASSES is None:
|
||||
_populate_pytz_base_classes()
|
||||
|
||||
return isinstance(tz, _PYTZ_BASE_CLASSES)
|
||||
|
||||
|
||||
def upgrade_tzinfo(tz):
|
||||
"""Convert a ``pytz`` or shim timezone into its modern equivalent.
|
||||
|
||||
The shim classes are thin wrappers around :mod:`zoneinfo` or
|
||||
:mod:`dateutil.tz` implementations of the :class:`datetime.tzinfo` base
|
||||
class. This function removes the shim and returns the underlying "upgraded"
|
||||
time zone.
|
||||
|
||||
When passed a ``pytz`` zone (not a shim), this returns the non-``pytz``
|
||||
equivalent. This may fail if ``pytz`` is using a data source incompatible
|
||||
with the upgraded provider's data source, or if the ``pytz`` zone was built
|
||||
from a file rather than an IANA key.
|
||||
|
||||
When passed an object that is not a shim or a ``pytz`` zone, this returns
|
||||
the original object.
|
||||
|
||||
:param tz:
|
||||
A :class:`datetime.tzinfo` object.
|
||||
|
||||
:raises KeyError:
|
||||
If a ``pytz`` zone is passed to the function with no equivalent in the
|
||||
:pep:`495`-compatible library's version of the Olson database.
|
||||
|
||||
:return:
|
||||
A :pep:`495`-compatible equivalent of any ``pytz`` or shim
|
||||
class, or the original object.
|
||||
"""
|
||||
if isinstance(tz, _PytzShimTimezone):
|
||||
return tz._zone
|
||||
|
||||
if is_pytz_zone(tz):
|
||||
if tz.zone is None:
|
||||
# This is a fixed offset zone
|
||||
offset = tz.utcoffset(None)
|
||||
offset_minutes = offset.total_seconds() / 60
|
||||
|
||||
return _compat.get_fixed_offset_zone(offset_minutes)
|
||||
|
||||
if tz.zone == "UTC":
|
||||
return _compat.UTC
|
||||
|
||||
return _compat.get_timezone(tz.zone)
|
||||
|
||||
return tz
|
||||
|
||||
|
||||
def _populate_pytz_base_classes():
|
||||
import pytz
|
||||
from pytz.tzinfo import BaseTzInfo
|
||||
|
||||
base_classes = (BaseTzInfo, pytz._FixedOffset)
|
||||
|
||||
# In releases prior to 2018.4, pytz.UTC was not a subclass of BaseTzInfo
|
||||
if not isinstance(pytz.UTC, BaseTzInfo): # pragma: nocover
|
||||
base_classes = base_classes + (type(pytz.UTC),)
|
||||
|
||||
global _PYTZ_BASE_CLASSES
|
||||
_PYTZ_BASE_CLASSES = base_classes
|
|
@ -1,6 +1,6 @@
|
|||
# IANA versions like 2020a are not valid PEP 440 identifiers; the recommended
|
||||
# way to translate the version is to use YYYY.n where `n` is a 0-based index.
|
||||
__version__ = "2021.5"
|
||||
__version__ = "2021.2.post0"
|
||||
|
||||
# This exposes the original IANA version number.
|
||||
IANA_VERSION = "2021e"
|
||||
IANA_VERSION = "2021b"
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,4 +1,4 @@
|
|||
# version 2021e
|
||||
# version 2021b
|
||||
# This zic input file is in the public domain.
|
||||
R d 1916 o - Jun 14 23s 1 S
|
||||
R d 1916 1919 - O Su>=1 23s 0 -
|
||||
|
@ -43,16 +43,6 @@ Z Africa/Ndjamena 1:0:12 - LMT 1912
|
|||
1 - WAT
|
||||
Z Africa/Abidjan -0:16:8 - LMT 1912
|
||||
0 - GMT
|
||||
L Africa/Abidjan Africa/Accra
|
||||
L Africa/Abidjan Africa/Bamako
|
||||
L Africa/Abidjan Africa/Banjul
|
||||
L Africa/Abidjan Africa/Conakry
|
||||
L Africa/Abidjan Africa/Dakar
|
||||
L Africa/Abidjan Africa/Freetown
|
||||
L Africa/Abidjan Africa/Lome
|
||||
L Africa/Abidjan Africa/Nouakchott
|
||||
L Africa/Abidjan Africa/Ouagadougou
|
||||
L Africa/Abidjan Atlantic/St_Helena
|
||||
R K 1940 o - Jul 15 0 1 S
|
||||
R K 1940 o - O 1 0 0 -
|
||||
R K 1941 o - Ap 15 0 1 S
|
||||
|
@ -96,15 +86,6 @@ Z Africa/Nairobi 2:27:16 - LMT 1908 May
|
|||
2:30 - +0230 1936 D 31 24
|
||||
2:45 - +0245 1942 Jul 31 24
|
||||
3 - EAT
|
||||
L Africa/Nairobi Africa/Addis_Ababa
|
||||
L Africa/Nairobi Africa/Asmara
|
||||
L Africa/Nairobi Africa/Dar_es_Salaam
|
||||
L Africa/Nairobi Africa/Djibouti
|
||||
L Africa/Nairobi Africa/Kampala
|
||||
L Africa/Nairobi Africa/Mogadishu
|
||||
L Africa/Nairobi Indian/Antananarivo
|
||||
L Africa/Nairobi Indian/Comoro
|
||||
L Africa/Nairobi Indian/Mayotte
|
||||
Z Africa/Monrovia -0:43:8 - LMT 1882
|
||||
-0:43:8 - MMT 1919 Mar
|
||||
-0:44:30 - MMT 1972 Ja 7
|
||||
|
@ -335,13 +316,6 @@ Z Africa/El_Aaiun -0:52:48 - LMT 1934
|
|||
1 M +01/+00
|
||||
Z Africa/Maputo 2:10:20 - LMT 1903 Mar
|
||||
2 - CAT
|
||||
L Africa/Maputo Africa/Blantyre
|
||||
L Africa/Maputo Africa/Bujumbura
|
||||
L Africa/Maputo Africa/Gaborone
|
||||
L Africa/Maputo Africa/Harare
|
||||
L Africa/Maputo Africa/Kigali
|
||||
L Africa/Maputo Africa/Lubumbashi
|
||||
L Africa/Maputo Africa/Lusaka
|
||||
R NA 1994 o - Mar 21 0 -1 WAT
|
||||
R NA 1994 2017 - S Su>=1 2 0 CAT
|
||||
R NA 1995 2017 - Ap Su>=1 2 -1 WAT
|
||||
|
@ -356,15 +330,6 @@ Z Africa/Lagos 0:13:35 - LMT 1905 Jul
|
|||
0:13:35 - LMT 1914
|
||||
0:30 - +0030 1919 S
|
||||
1 - WAT
|
||||
L Africa/Lagos Africa/Bangui
|
||||
L Africa/Lagos Africa/Brazzaville
|
||||
L Africa/Lagos Africa/Douala
|
||||
L Africa/Lagos Africa/Kinshasa
|
||||
L Africa/Lagos Africa/Libreville
|
||||
L Africa/Lagos Africa/Luanda
|
||||
L Africa/Lagos Africa/Malabo
|
||||
L Africa/Lagos Africa/Niamey
|
||||
L Africa/Lagos Africa/Porto-Novo
|
||||
Z Indian/Reunion 3:41:52 - LMT 1911 Jun
|
||||
4 - +04
|
||||
Z Africa/Sao_Tome 0:26:56 - LMT 1884
|
||||
|
@ -379,8 +344,6 @@ R SA 1943 1944 - Mar Su>=15 2 0 -
|
|||
Z Africa/Johannesburg 1:52 - LMT 1892 F 8
|
||||
1:30 - SAST 1903 Mar
|
||||
2 SA SAST
|
||||
L Africa/Johannesburg Africa/Maseru
|
||||
L Africa/Johannesburg Africa/Mbabane
|
||||
R SD 1970 o - May 1 0 1 S
|
||||
R SD 1970 1985 - O 15 0 0 -
|
||||
R SD 1971 o - Ap 30 0 1 S
|
||||
|
@ -608,7 +571,6 @@ Z Asia/Famagusta 2:15:48 - LMT 1921 N 14
|
|||
2 E EE%sT 2016 S 8
|
||||
3 - +03 2017 O 29 1u
|
||||
2 E EE%sT
|
||||
L Asia/Nicosia Europe/Nicosia
|
||||
Z Asia/Tbilisi 2:59:11 - LMT 1880
|
||||
2:59:11 - TBMT 1924 May 2
|
||||
3 - +03 1957 Mar
|
||||
|
@ -1112,8 +1074,7 @@ R P 2016 2018 - O Sa>=24 1 0 -
|
|||
R P 2019 o - Mar 29 0 1 S
|
||||
R P 2019 o - O Sa>=24 0 0 -
|
||||
R P 2020 ma - Mar Sa>=24 0 1 S
|
||||
R P 2020 o - O 24 1 0 -
|
||||
R P 2021 ma - O lastF 1 0 -
|
||||
R P 2020 ma - O Sa>=24 1 0 -
|
||||
Z Asia/Gaza 2:17:52 - LMT 1900 O
|
||||
2 Z EET/EEST 1948 May 15
|
||||
2 K EE%sT 1967 Jun 5
|
||||
|
@ -1146,12 +1107,8 @@ Z Asia/Manila -15:56 - LMT 1844 D 31
|
|||
Z Asia/Qatar 3:26:8 - LMT 1920
|
||||
4 - +04 1972 Jun
|
||||
3 - +03
|
||||
L Asia/Qatar Asia/Bahrain
|
||||
Z Asia/Riyadh 3:6:52 - LMT 1947 Mar 14
|
||||
3 - +03
|
||||
L Asia/Riyadh Antarctica/Syowa
|
||||
L Asia/Riyadh Asia/Aden
|
||||
L Asia/Riyadh Asia/Kuwait
|
||||
Z Asia/Singapore 6:55:25 - LMT 1901
|
||||
6:55:25 - SMT 1905 Jun
|
||||
7 - +07 1933
|
||||
|
@ -1221,8 +1178,6 @@ Z Asia/Dushanbe 4:35:12 - LMT 1924 May 2
|
|||
Z Asia/Bangkok 6:42:4 - LMT 1880
|
||||
6:42:4 - BMT 1920 Ap
|
||||
7 - +07
|
||||
L Asia/Bangkok Asia/Phnom_Penh
|
||||
L Asia/Bangkok Asia/Vientiane
|
||||
Z Asia/Ashgabat 3:53:32 - LMT 1924 May 2
|
||||
4 - +04 1930 Jun 21
|
||||
5 R +05/+06 1991 Mar 31 2
|
||||
|
@ -1230,7 +1185,6 @@ Z Asia/Ashgabat 3:53:32 - LMT 1924 May 2
|
|||
5 - +05
|
||||
Z Asia/Dubai 3:41:12 - LMT 1920
|
||||
4 - +04
|
||||
L Asia/Dubai Asia/Muscat
|
||||
Z Asia/Samarkand 4:27:53 - LMT 1924 May 2
|
||||
4 - +04 1930 Jun 21
|
||||
5 - +05 1981 Ap
|
||||
|
@ -1419,11 +1373,10 @@ R FJ 2011 o - Mar Su>=1 3 0 -
|
|||
R FJ 2012 2013 - Ja Su>=18 3 0 -
|
||||
R FJ 2014 o - Ja Su>=18 2 0 -
|
||||
R FJ 2014 2018 - N Su>=1 2 1 -
|
||||
R FJ 2015 2021 - Ja Su>=12 3 0 -
|
||||
R FJ 2015 ma - Ja Su>=12 3 0 -
|
||||
R FJ 2019 o - N Su>=8 2 1 -
|
||||
R FJ 2020 o - D 20 2 1 -
|
||||
R FJ 2022 ma - N Su>=8 2 1 -
|
||||
R FJ 2023 ma - Ja Su>=12 3 0 -
|
||||
R FJ 2021 ma - N Su>=8 2 1 -
|
||||
Z Pacific/Fiji 11:55:44 - LMT 1915 O 26
|
||||
12 FJ +12/+13
|
||||
Z Pacific/Gambier -8:59:48 - LMT 1912 O
|
||||
|
@ -1452,7 +1405,6 @@ Z Pacific/Guam -14:21 - LMT 1844 D 31
|
|||
9 - +09 1944 Jul 31
|
||||
10 Gu G%sT 2000 D 23
|
||||
10 - ChST
|
||||
L Pacific/Guam Pacific/Saipan
|
||||
Z Pacific/Tarawa 11:32:4 - LMT 1901
|
||||
12 - +12
|
||||
Z Pacific/Kanton 0 - -00 1937 Au 31
|
||||
|
@ -1545,7 +1497,6 @@ Z Pacific/Auckland 11:39:4 - LMT 1868 N 2
|
|||
Z Pacific/Chatham 12:13:48 - LMT 1868 N 2
|
||||
12:15 - +1215 1946
|
||||
12:45 k +1245/+1345
|
||||
L Pacific/Auckland Antarctica/McMurdo
|
||||
R CK 1978 o - N 12 0 0:30 -
|
||||
R CK 1979 1991 - Mar Su>=1 0 0 -
|
||||
R CK 1979 1990 - O lastSu 0 0:30 -
|
||||
|
@ -1569,7 +1520,6 @@ Z Pacific/Palau -15:2:4 - LMT 1844 D 31
|
|||
Z Pacific/Port_Moresby 9:48:40 - LMT 1880
|
||||
9:48:32 - PMMT 1895
|
||||
10 - +10
|
||||
L Pacific/Port_Moresby Antarctica/DumontDUrville
|
||||
Z Pacific/Bougainville 10:22:16 - LMT 1880
|
||||
9:48:32 - PMMT 1895
|
||||
10 - +10 1942 Jul
|
||||
|
@ -1582,7 +1532,6 @@ Z Pacific/Pitcairn -8:40:20 - LMT 1901
|
|||
Z Pacific/Pago_Pago 12:37:12 - LMT 1892 Jul 5
|
||||
-11:22:48 - LMT 1911
|
||||
-11 - SST
|
||||
L Pacific/Pago_Pago Pacific/Midway
|
||||
R WS 2010 o - S lastSu 0 1 -
|
||||
R WS 2011 o - Ap Sa>=1 4 0 -
|
||||
R WS 2011 o - S lastSa 3 1 -
|
||||
|
@ -1692,9 +1641,6 @@ Z Europe/London -0:1:15 - LMT 1847 D 1 0s
|
|||
1 - BST 1971 O 31 2u
|
||||
0 G %s 1996
|
||||
0 E GMT/BST
|
||||
L Europe/London Europe/Jersey
|
||||
L Europe/London Europe/Guernsey
|
||||
L Europe/London Europe/Isle_of_Man
|
||||
R IE 1971 o - O 31 2u -1 -
|
||||
R IE 1972 1980 - Mar Su>=16 2u 0 -
|
||||
R IE 1972 1980 - O Su>=23 2u -1 -
|
||||
|
@ -1958,7 +1904,6 @@ Z Europe/Helsinki 1:39:49 - LMT 1878 May 31
|
|||
1:39:49 - HMT 1921 May
|
||||
2 FI EE%sT 1983
|
||||
2 E EE%sT
|
||||
L Europe/Helsinki Europe/Mariehamn
|
||||
R F 1916 o - Jun 14 23s 1 S
|
||||
R F 1916 1919 - O Su>=1 23s 0 -
|
||||
R F 1917 o - Mar 24 23s 1 S
|
||||
|
@ -2024,7 +1969,6 @@ Z Europe/Berlin 0:53:28 - LMT 1893 Ap
|
|||
1 So CE%sT 1946
|
||||
1 DE CE%sT 1980
|
||||
1 E CE%sT
|
||||
L Europe/Zurich Europe/Busingen
|
||||
Z Europe/Gibraltar -0:21:24 - LMT 1880 Au 2 0s
|
||||
0 G %s 1957 Ap 14 2
|
||||
1 - CET 1982
|
||||
|
@ -2145,8 +2089,6 @@ Z Europe/Rome 0:49:56 - LMT 1866 D 12
|
|||
1 c CE%sT 1944 Jun 4
|
||||
1 I CE%sT 1980
|
||||
1 E CE%sT
|
||||
L Europe/Rome Europe/Vatican
|
||||
L Europe/Rome Europe/San_Marino
|
||||
R LV 1989 1996 - Mar lastSu 2s 1 S
|
||||
R LV 1989 1996 - S lastSu 2s 0 -
|
||||
Z Europe/Riga 1:36:34 - LMT 1880
|
||||
|
@ -2164,7 +2106,6 @@ Z Europe/Riga 1:36:34 - LMT 1880
|
|||
2 E EE%sT 2000 F 29
|
||||
2 - EET 2001 Ja 2
|
||||
2 E EE%sT
|
||||
L Europe/Zurich Europe/Vaduz
|
||||
Z Europe/Vilnius 1:41:16 - LMT 1880
|
||||
1:24 - WMT 1917
|
||||
1:35:36 - KMT 1919 O 10
|
||||
|
@ -2276,7 +2217,6 @@ Z Europe/Oslo 0:43 - LMT 1895
|
|||
1 c CE%sT 1945 Ap 2 2
|
||||
1 NO CE%sT 1980
|
||||
1 E CE%sT
|
||||
L Europe/Oslo Arctic/Longyearbyen
|
||||
R O 1918 1919 - S 16 2s 0 -
|
||||
R O 1919 o - Ap 15 2s 1 S
|
||||
R O 1944 o - Ap 3 2s 1 S
|
||||
|
@ -2646,12 +2586,6 @@ Z Europe/Belgrade 1:22 - LMT 1884
|
|||
1 1 CEST 1945 S 16 2s
|
||||
1 - CET 1982 N 27
|
||||
1 E CE%sT
|
||||
L Europe/Belgrade Europe/Ljubljana
|
||||
L Europe/Belgrade Europe/Podgorica
|
||||
L Europe/Belgrade Europe/Sarajevo
|
||||
L Europe/Belgrade Europe/Skopje
|
||||
L Europe/Belgrade Europe/Zagreb
|
||||
L Europe/Prague Europe/Bratislava
|
||||
R s 1918 o - Ap 15 23 1 S
|
||||
R s 1918 1919 - O 6 24s 0 -
|
||||
R s 1919 o - Ap 6 23 1 S
|
||||
|
@ -2779,7 +2713,6 @@ Z Europe/Istanbul 1:55:52 - LMT 1880
|
|||
2 1 EEST 2015 N 8 1u
|
||||
2 E EE%sT 2016 S 7
|
||||
3 - +03
|
||||
L Europe/Istanbul Asia/Istanbul
|
||||
Z Europe/Kiev 2:2:4 - LMT 1880
|
||||
2:2:4 - KMT 1924 May 2
|
||||
2 - EET 1930 Jun 21
|
||||
|
@ -2955,7 +2888,6 @@ Z America/Phoenix -7:28:18 - LMT 1883 N 18 11:31:42
|
|||
-7 - MST 1967
|
||||
-7 u M%sT 1968 Mar 21
|
||||
-7 - MST
|
||||
L America/Phoenix America/Creston
|
||||
Z America/Boise -7:44:49 - LMT 1883 N 18 12:15:11
|
||||
-8 u P%sT 1923 May 13 2
|
||||
-7 u M%sT 1974
|
||||
|
@ -3254,7 +3186,6 @@ Z America/Toronto -5:17:32 - LMT 1895
|
|||
-5 C E%sT 1946
|
||||
-5 t E%sT 1974
|
||||
-5 C E%sT
|
||||
L America/Toronto America/Nassau
|
||||
Z America/Thunder_Bay -5:57 - LMT 1895
|
||||
-6 - CST 1910
|
||||
-5 - EST 1942
|
||||
|
@ -3705,31 +3636,10 @@ Z America/Managua -5:45:8 - LMT 1890
|
|||
Z America/Panama -5:18:8 - LMT 1890
|
||||
-5:19:36 - CMT 1908 Ap 22
|
||||
-5 - EST
|
||||
L America/Panama America/Atikokan
|
||||
L America/Panama America/Cayman
|
||||
Z America/Puerto_Rico -4:24:25 - LMT 1899 Mar 28 12
|
||||
-4 - AST 1942 May 3
|
||||
-4 u A%sT 1946
|
||||
-4 - AST
|
||||
L America/Puerto_Rico America/Anguilla
|
||||
L America/Puerto_Rico America/Antigua
|
||||
L America/Puerto_Rico America/Aruba
|
||||
L America/Puerto_Rico America/Curacao
|
||||
L America/Puerto_Rico America/Blanc-Sablon
|
||||
L America/Puerto_Rico America/Dominica
|
||||
L America/Puerto_Rico America/Grenada
|
||||
L America/Puerto_Rico America/Guadeloupe
|
||||
L America/Puerto_Rico America/Kralendijk
|
||||
L America/Puerto_Rico America/Lower_Princes
|
||||
L America/Puerto_Rico America/Marigot
|
||||
L America/Puerto_Rico America/Montserrat
|
||||
L America/Puerto_Rico America/Port_of_Spain
|
||||
L America/Puerto_Rico America/St_Barthelemy
|
||||
L America/Puerto_Rico America/St_Kitts
|
||||
L America/Puerto_Rico America/St_Lucia
|
||||
L America/Puerto_Rico America/St_Thomas
|
||||
L America/Puerto_Rico America/St_Vincent
|
||||
L America/Puerto_Rico America/Tortola
|
||||
Z America/Miquelon -3:44:40 - LMT 1911 May 15
|
||||
-4 - AST 1980 May
|
||||
-3 - -03 1987
|
||||
|
@ -4311,46 +4221,114 @@ Z Etc/GMT+10 -10 - -10
|
|||
Z Etc/GMT+11 -11 - -11
|
||||
Z Etc/GMT+12 -12 - -12
|
||||
Z Factory 0 - -00
|
||||
L Africa/Abidjan Africa/Accra
|
||||
L Africa/Nairobi Africa/Addis_Ababa
|
||||
L Africa/Nairobi Africa/Asmara
|
||||
L Africa/Nairobi Africa/Asmera
|
||||
L Africa/Abidjan Africa/Bamako
|
||||
L Africa/Lagos Africa/Bangui
|
||||
L Africa/Abidjan Africa/Banjul
|
||||
L Africa/Maputo Africa/Blantyre
|
||||
L Africa/Lagos Africa/Brazzaville
|
||||
L Africa/Maputo Africa/Bujumbura
|
||||
L Africa/Abidjan Africa/Conakry
|
||||
L Africa/Abidjan Africa/Dakar
|
||||
L Africa/Nairobi Africa/Dar_es_Salaam
|
||||
L Africa/Nairobi Africa/Djibouti
|
||||
L Africa/Lagos Africa/Douala
|
||||
L Africa/Abidjan Africa/Freetown
|
||||
L Africa/Maputo Africa/Gaborone
|
||||
L Africa/Maputo Africa/Harare
|
||||
L Africa/Nairobi Africa/Kampala
|
||||
L Africa/Maputo Africa/Kigali
|
||||
L Africa/Lagos Africa/Kinshasa
|
||||
L Africa/Lagos Africa/Libreville
|
||||
L Africa/Abidjan Africa/Lome
|
||||
L Africa/Lagos Africa/Luanda
|
||||
L Africa/Maputo Africa/Lubumbashi
|
||||
L Africa/Maputo Africa/Lusaka
|
||||
L Africa/Lagos Africa/Malabo
|
||||
L Africa/Johannesburg Africa/Maseru
|
||||
L Africa/Johannesburg Africa/Mbabane
|
||||
L Africa/Nairobi Africa/Mogadishu
|
||||
L Africa/Lagos Africa/Niamey
|
||||
L Africa/Abidjan Africa/Nouakchott
|
||||
L Africa/Abidjan Africa/Ouagadougou
|
||||
L Africa/Lagos Africa/Porto-Novo
|
||||
L Africa/Abidjan Africa/Timbuktu
|
||||
L America/Puerto_Rico America/Anguilla
|
||||
L America/Puerto_Rico America/Antigua
|
||||
L America/Argentina/Catamarca America/Argentina/ComodRivadavia
|
||||
L America/Puerto_Rico America/Aruba
|
||||
L America/Panama America/Atikokan
|
||||
L America/Adak America/Atka
|
||||
L America/Puerto_Rico America/Blanc-Sablon
|
||||
L America/Argentina/Buenos_Aires America/Buenos_Aires
|
||||
L America/Argentina/Catamarca America/Catamarca
|
||||
L America/Panama America/Cayman
|
||||
L America/Panama America/Coral_Harbour
|
||||
L America/Argentina/Cordoba America/Cordoba
|
||||
L America/Phoenix America/Creston
|
||||
L America/Puerto_Rico America/Curacao
|
||||
L America/Puerto_Rico America/Dominica
|
||||
L America/Tijuana America/Ensenada
|
||||
L America/Indiana/Indianapolis America/Fort_Wayne
|
||||
L America/Nuuk America/Godthab
|
||||
L America/Puerto_Rico America/Grenada
|
||||
L America/Puerto_Rico America/Guadeloupe
|
||||
L America/Indiana/Indianapolis America/Indianapolis
|
||||
L America/Argentina/Jujuy America/Jujuy
|
||||
L America/Indiana/Knox America/Knox_IN
|
||||
L America/Puerto_Rico America/Kralendijk
|
||||
L America/Kentucky/Louisville America/Louisville
|
||||
L America/Puerto_Rico America/Lower_Princes
|
||||
L America/Puerto_Rico America/Marigot
|
||||
L America/Argentina/Mendoza America/Mendoza
|
||||
L America/Toronto America/Montreal
|
||||
L America/Puerto_Rico America/Montserrat
|
||||
L America/Toronto America/Nassau
|
||||
L America/Puerto_Rico America/Port_of_Spain
|
||||
L America/Rio_Branco America/Porto_Acre
|
||||
L America/Argentina/Cordoba America/Rosario
|
||||
L America/Tijuana America/Santa_Isabel
|
||||
L America/Denver America/Shiprock
|
||||
L America/Puerto_Rico America/St_Barthelemy
|
||||
L America/Puerto_Rico America/St_Kitts
|
||||
L America/Puerto_Rico America/St_Lucia
|
||||
L America/Puerto_Rico America/St_Thomas
|
||||
L America/Puerto_Rico America/St_Vincent
|
||||
L America/Puerto_Rico America/Tortola
|
||||
L America/Puerto_Rico America/Virgin
|
||||
L Pacific/Port_Moresby Antarctica/DumontDUrville
|
||||
L Pacific/Auckland Antarctica/McMurdo
|
||||
L Pacific/Auckland Antarctica/South_Pole
|
||||
L Asia/Riyadh Antarctica/Syowa
|
||||
L Europe/Oslo Arctic/Longyearbyen
|
||||
L Asia/Riyadh Asia/Aden
|
||||
L Asia/Ashgabat Asia/Ashkhabad
|
||||
L Asia/Qatar Asia/Bahrain
|
||||
L Asia/Kolkata Asia/Calcutta
|
||||
L Asia/Shanghai Asia/Chongqing
|
||||
L Asia/Shanghai Asia/Chungking
|
||||
L Asia/Dhaka Asia/Dacca
|
||||
L Asia/Shanghai Asia/Harbin
|
||||
L Europe/Istanbul Asia/Istanbul
|
||||
L Asia/Urumqi Asia/Kashgar
|
||||
L Asia/Kathmandu Asia/Katmandu
|
||||
L Asia/Riyadh Asia/Kuwait
|
||||
L Asia/Macau Asia/Macao
|
||||
L Asia/Dubai Asia/Muscat
|
||||
L Asia/Bangkok Asia/Phnom_Penh
|
||||
L Asia/Yangon Asia/Rangoon
|
||||
L Asia/Ho_Chi_Minh Asia/Saigon
|
||||
L Asia/Jerusalem Asia/Tel_Aviv
|
||||
L Asia/Thimphu Asia/Thimbu
|
||||
L Asia/Makassar Asia/Ujung_Pandang
|
||||
L Asia/Ulaanbaatar Asia/Ulan_Bator
|
||||
L Asia/Bangkok Asia/Vientiane
|
||||
L Atlantic/Faroe Atlantic/Faeroe
|
||||
L Europe/Oslo Atlantic/Jan_Mayen
|
||||
L Europe/Berlin Atlantic/Jan_Mayen
|
||||
L Africa/Abidjan Atlantic/St_Helena
|
||||
L Australia/Sydney Australia/ACT
|
||||
L Australia/Sydney Australia/Canberra
|
||||
L Australia/Hobart Australia/Currie
|
||||
|
@ -4382,7 +4360,22 @@ L Africa/Cairo Egypt
|
|||
L Europe/Dublin Eire
|
||||
L Etc/UTC Etc/UCT
|
||||
L Europe/London Europe/Belfast
|
||||
L Europe/Prague Europe/Bratislava
|
||||
L Europe/Zurich Europe/Busingen
|
||||
L Europe/London Europe/Guernsey
|
||||
L Europe/London Europe/Isle_of_Man
|
||||
L Europe/London Europe/Jersey
|
||||
L Europe/Belgrade Europe/Ljubljana
|
||||
L Europe/Helsinki Europe/Mariehamn
|
||||
L Asia/Nicosia Europe/Nicosia
|
||||
L Europe/Belgrade Europe/Podgorica
|
||||
L Europe/Rome Europe/San_Marino
|
||||
L Europe/Belgrade Europe/Sarajevo
|
||||
L Europe/Belgrade Europe/Skopje
|
||||
L Europe/Chisinau Europe/Tiraspol
|
||||
L Europe/Zurich Europe/Vaduz
|
||||
L Europe/Rome Europe/Vatican
|
||||
L Europe/Belgrade Europe/Zagreb
|
||||
L Europe/London GB
|
||||
L Europe/London GB-Eire
|
||||
L Etc/GMT GMT+0
|
||||
|
@ -4391,6 +4384,9 @@ L Etc/GMT GMT0
|
|||
L Etc/GMT Greenwich
|
||||
L Asia/Hong_Kong Hongkong
|
||||
L Atlantic/Reykjavik Iceland
|
||||
L Africa/Nairobi Indian/Antananarivo
|
||||
L Africa/Nairobi Indian/Comoro
|
||||
L Africa/Nairobi Indian/Mayotte
|
||||
L Asia/Tehran Iran
|
||||
L Asia/Jerusalem Israel
|
||||
L America/Jamaica Jamaica
|
||||
|
@ -4406,7 +4402,9 @@ L America/Denver Navajo
|
|||
L Asia/Shanghai PRC
|
||||
L Pacific/Kanton Pacific/Enderbury
|
||||
L Pacific/Honolulu Pacific/Johnston
|
||||
L Pacific/Pago_Pago Pacific/Midway
|
||||
L Pacific/Pohnpei Pacific/Ponape
|
||||
L Pacific/Guam Pacific/Saipan
|
||||
L Pacific/Pago_Pago Pacific/Samoa
|
||||
L Pacific/Chuuk Pacific/Truk
|
||||
L Pacific/Chuuk Pacific/Yap
|
||||
|
|
176
lib/tzdata/zones
176
lib/tzdata/zones
|
@ -2,58 +2,21 @@ Africa/Algiers
|
|||
Atlantic/Cape_Verde
|
||||
Africa/Ndjamena
|
||||
Africa/Abidjan
|
||||
Africa/Accra
|
||||
Africa/Bamako
|
||||
Africa/Banjul
|
||||
Africa/Conakry
|
||||
Africa/Dakar
|
||||
Africa/Freetown
|
||||
Africa/Lome
|
||||
Africa/Nouakchott
|
||||
Africa/Ouagadougou
|
||||
Atlantic/St_Helena
|
||||
Africa/Cairo
|
||||
Africa/Bissau
|
||||
Africa/Nairobi
|
||||
Africa/Addis_Ababa
|
||||
Africa/Asmara
|
||||
Africa/Dar_es_Salaam
|
||||
Africa/Djibouti
|
||||
Africa/Kampala
|
||||
Africa/Mogadishu
|
||||
Indian/Antananarivo
|
||||
Indian/Comoro
|
||||
Indian/Mayotte
|
||||
Africa/Monrovia
|
||||
Africa/Tripoli
|
||||
Indian/Mauritius
|
||||
Africa/Casablanca
|
||||
Africa/El_Aaiun
|
||||
Africa/Maputo
|
||||
Africa/Blantyre
|
||||
Africa/Bujumbura
|
||||
Africa/Gaborone
|
||||
Africa/Harare
|
||||
Africa/Kigali
|
||||
Africa/Lubumbashi
|
||||
Africa/Lusaka
|
||||
Africa/Windhoek
|
||||
Africa/Lagos
|
||||
Africa/Bangui
|
||||
Africa/Brazzaville
|
||||
Africa/Douala
|
||||
Africa/Kinshasa
|
||||
Africa/Libreville
|
||||
Africa/Luanda
|
||||
Africa/Malabo
|
||||
Africa/Niamey
|
||||
Africa/Porto-Novo
|
||||
Indian/Reunion
|
||||
Africa/Sao_Tome
|
||||
Indian/Mahe
|
||||
Africa/Johannesburg
|
||||
Africa/Maseru
|
||||
Africa/Mbabane
|
||||
Africa/Khartoum
|
||||
Africa/Juba
|
||||
Africa/Tunis
|
||||
|
@ -79,7 +42,6 @@ Asia/Taipei
|
|||
Asia/Macau
|
||||
Asia/Nicosia
|
||||
Asia/Famagusta
|
||||
Europe/Nicosia
|
||||
Asia/Tbilisi
|
||||
Asia/Dili
|
||||
Asia/Kolkata
|
||||
|
@ -115,21 +77,14 @@ Asia/Gaza
|
|||
Asia/Hebron
|
||||
Asia/Manila
|
||||
Asia/Qatar
|
||||
Asia/Bahrain
|
||||
Asia/Riyadh
|
||||
Antarctica/Syowa
|
||||
Asia/Aden
|
||||
Asia/Kuwait
|
||||
Asia/Singapore
|
||||
Asia/Colombo
|
||||
Asia/Damascus
|
||||
Asia/Dushanbe
|
||||
Asia/Bangkok
|
||||
Asia/Phnom_Penh
|
||||
Asia/Vientiane
|
||||
Asia/Ashgabat
|
||||
Asia/Dubai
|
||||
Asia/Muscat
|
||||
Asia/Samarkand
|
||||
Asia/Tashkent
|
||||
Asia/Ho_Chi_Minh
|
||||
|
@ -152,7 +107,6 @@ Pacific/Gambier
|
|||
Pacific/Marquesas
|
||||
Pacific/Tahiti
|
||||
Pacific/Guam
|
||||
Pacific/Saipan
|
||||
Pacific/Tarawa
|
||||
Pacific/Kanton
|
||||
Pacific/Kiritimati
|
||||
|
@ -165,17 +119,14 @@ Pacific/Nauru
|
|||
Pacific/Noumea
|
||||
Pacific/Auckland
|
||||
Pacific/Chatham
|
||||
Antarctica/McMurdo
|
||||
Pacific/Rarotonga
|
||||
Pacific/Niue
|
||||
Pacific/Norfolk
|
||||
Pacific/Palau
|
||||
Pacific/Port_Moresby
|
||||
Antarctica/DumontDUrville
|
||||
Pacific/Bougainville
|
||||
Pacific/Pitcairn
|
||||
Pacific/Pago_Pago
|
||||
Pacific/Midway
|
||||
Pacific/Apia
|
||||
Pacific/Guadalcanal
|
||||
Pacific/Fakaofo
|
||||
|
@ -185,9 +136,6 @@ Pacific/Wake
|
|||
Pacific/Efate
|
||||
Pacific/Wallis
|
||||
Europe/London
|
||||
Europe/Jersey
|
||||
Europe/Guernsey
|
||||
Europe/Isle_of_Man
|
||||
Europe/Dublin
|
||||
WET
|
||||
CET
|
||||
|
@ -208,19 +156,14 @@ America/Nuuk
|
|||
America/Thule
|
||||
Europe/Tallinn
|
||||
Europe/Helsinki
|
||||
Europe/Mariehamn
|
||||
Europe/Paris
|
||||
Europe/Berlin
|
||||
Europe/Busingen
|
||||
Europe/Gibraltar
|
||||
Europe/Athens
|
||||
Europe/Budapest
|
||||
Atlantic/Reykjavik
|
||||
Europe/Rome
|
||||
Europe/Vatican
|
||||
Europe/San_Marino
|
||||
Europe/Riga
|
||||
Europe/Vaduz
|
||||
Europe/Vilnius
|
||||
Europe/Luxembourg
|
||||
Europe/Malta
|
||||
|
@ -228,7 +171,6 @@ Europe/Chisinau
|
|||
Europe/Monaco
|
||||
Europe/Amsterdam
|
||||
Europe/Oslo
|
||||
Arctic/Longyearbyen
|
||||
Europe/Warsaw
|
||||
Europe/Lisbon
|
||||
Atlantic/Azores
|
||||
|
@ -262,19 +204,12 @@ Asia/Ust-Nera
|
|||
Asia/Kamchatka
|
||||
Asia/Anadyr
|
||||
Europe/Belgrade
|
||||
Europe/Ljubljana
|
||||
Europe/Podgorica
|
||||
Europe/Sarajevo
|
||||
Europe/Skopje
|
||||
Europe/Zagreb
|
||||
Europe/Bratislava
|
||||
Europe/Madrid
|
||||
Africa/Ceuta
|
||||
Atlantic/Canary
|
||||
Europe/Stockholm
|
||||
Europe/Zurich
|
||||
Europe/Istanbul
|
||||
Asia/Istanbul
|
||||
Europe/Kiev
|
||||
Europe/Uzhgorod
|
||||
Europe/Zaporozhye
|
||||
|
@ -301,7 +236,6 @@ America/Nome
|
|||
America/Adak
|
||||
Pacific/Honolulu
|
||||
America/Phoenix
|
||||
America/Creston
|
||||
America/Boise
|
||||
America/Indiana/Indianapolis
|
||||
America/Indiana/Marengo
|
||||
|
@ -321,7 +255,6 @@ America/Halifax
|
|||
America/Glace_Bay
|
||||
America/Moncton
|
||||
America/Toronto
|
||||
America/Nassau
|
||||
America/Thunder_Bay
|
||||
America/Nipigon
|
||||
America/Rainy_River
|
||||
|
@ -366,28 +299,7 @@ America/Jamaica
|
|||
America/Martinique
|
||||
America/Managua
|
||||
America/Panama
|
||||
America/Atikokan
|
||||
America/Cayman
|
||||
America/Puerto_Rico
|
||||
America/Anguilla
|
||||
America/Antigua
|
||||
America/Aruba
|
||||
America/Curacao
|
||||
America/Blanc-Sablon
|
||||
America/Dominica
|
||||
America/Grenada
|
||||
America/Guadeloupe
|
||||
America/Kralendijk
|
||||
America/Lower_Princes
|
||||
America/Marigot
|
||||
America/Montserrat
|
||||
America/Port_of_Spain
|
||||
America/St_Barthelemy
|
||||
America/St_Kitts
|
||||
America/St_Lucia
|
||||
America/St_Thomas
|
||||
America/St_Vincent
|
||||
America/Tortola
|
||||
America/Miquelon
|
||||
America/Grand_Turk
|
||||
America/Argentina/Buenos_Aires
|
||||
|
@ -471,46 +383,114 @@ Etc/GMT+10
|
|||
Etc/GMT+11
|
||||
Etc/GMT+12
|
||||
Factory
|
||||
Africa/Accra
|
||||
Africa/Addis_Ababa
|
||||
Africa/Asmara
|
||||
Africa/Asmera
|
||||
Africa/Bamako
|
||||
Africa/Bangui
|
||||
Africa/Banjul
|
||||
Africa/Blantyre
|
||||
Africa/Brazzaville
|
||||
Africa/Bujumbura
|
||||
Africa/Conakry
|
||||
Africa/Dakar
|
||||
Africa/Dar_es_Salaam
|
||||
Africa/Djibouti
|
||||
Africa/Douala
|
||||
Africa/Freetown
|
||||
Africa/Gaborone
|
||||
Africa/Harare
|
||||
Africa/Kampala
|
||||
Africa/Kigali
|
||||
Africa/Kinshasa
|
||||
Africa/Libreville
|
||||
Africa/Lome
|
||||
Africa/Luanda
|
||||
Africa/Lubumbashi
|
||||
Africa/Lusaka
|
||||
Africa/Malabo
|
||||
Africa/Maseru
|
||||
Africa/Mbabane
|
||||
Africa/Mogadishu
|
||||
Africa/Niamey
|
||||
Africa/Nouakchott
|
||||
Africa/Ouagadougou
|
||||
Africa/Porto-Novo
|
||||
Africa/Timbuktu
|
||||
America/Anguilla
|
||||
America/Antigua
|
||||
America/Argentina/ComodRivadavia
|
||||
America/Aruba
|
||||
America/Atikokan
|
||||
America/Atka
|
||||
America/Blanc-Sablon
|
||||
America/Buenos_Aires
|
||||
America/Catamarca
|
||||
America/Cayman
|
||||
America/Coral_Harbour
|
||||
America/Cordoba
|
||||
America/Creston
|
||||
America/Curacao
|
||||
America/Dominica
|
||||
America/Ensenada
|
||||
America/Fort_Wayne
|
||||
America/Godthab
|
||||
America/Grenada
|
||||
America/Guadeloupe
|
||||
America/Indianapolis
|
||||
America/Jujuy
|
||||
America/Knox_IN
|
||||
America/Kralendijk
|
||||
America/Louisville
|
||||
America/Lower_Princes
|
||||
America/Marigot
|
||||
America/Mendoza
|
||||
America/Montreal
|
||||
America/Montserrat
|
||||
America/Nassau
|
||||
America/Port_of_Spain
|
||||
America/Porto_Acre
|
||||
America/Rosario
|
||||
America/Santa_Isabel
|
||||
America/Shiprock
|
||||
America/St_Barthelemy
|
||||
America/St_Kitts
|
||||
America/St_Lucia
|
||||
America/St_Thomas
|
||||
America/St_Vincent
|
||||
America/Tortola
|
||||
America/Virgin
|
||||
Antarctica/DumontDUrville
|
||||
Antarctica/McMurdo
|
||||
Antarctica/South_Pole
|
||||
Antarctica/Syowa
|
||||
Arctic/Longyearbyen
|
||||
Asia/Aden
|
||||
Asia/Ashkhabad
|
||||
Asia/Bahrain
|
||||
Asia/Calcutta
|
||||
Asia/Chongqing
|
||||
Asia/Chungking
|
||||
Asia/Dacca
|
||||
Asia/Harbin
|
||||
Asia/Istanbul
|
||||
Asia/Kashgar
|
||||
Asia/Katmandu
|
||||
Asia/Kuwait
|
||||
Asia/Macao
|
||||
Asia/Muscat
|
||||
Asia/Phnom_Penh
|
||||
Asia/Rangoon
|
||||
Asia/Saigon
|
||||
Asia/Tel_Aviv
|
||||
Asia/Thimbu
|
||||
Asia/Ujung_Pandang
|
||||
Asia/Ulan_Bator
|
||||
Asia/Vientiane
|
||||
Atlantic/Faeroe
|
||||
Atlantic/Jan_Mayen
|
||||
Atlantic/St_Helena
|
||||
Australia/ACT
|
||||
Australia/Canberra
|
||||
Australia/Currie
|
||||
|
@ -542,7 +522,22 @@ Egypt
|
|||
Eire
|
||||
Etc/UCT
|
||||
Europe/Belfast
|
||||
Europe/Bratislava
|
||||
Europe/Busingen
|
||||
Europe/Guernsey
|
||||
Europe/Isle_of_Man
|
||||
Europe/Jersey
|
||||
Europe/Ljubljana
|
||||
Europe/Mariehamn
|
||||
Europe/Nicosia
|
||||
Europe/Podgorica
|
||||
Europe/San_Marino
|
||||
Europe/Sarajevo
|
||||
Europe/Skopje
|
||||
Europe/Tiraspol
|
||||
Europe/Vaduz
|
||||
Europe/Vatican
|
||||
Europe/Zagreb
|
||||
GB
|
||||
GB-Eire
|
||||
GMT+0
|
||||
|
@ -551,6 +546,9 @@ GMT0
|
|||
Greenwich
|
||||
Hongkong
|
||||
Iceland
|
||||
Indian/Antananarivo
|
||||
Indian/Comoro
|
||||
Indian/Mayotte
|
||||
Iran
|
||||
Israel
|
||||
Jamaica
|
||||
|
@ -566,7 +564,9 @@ Navajo
|
|||
PRC
|
||||
Pacific/Enderbury
|
||||
Pacific/Johnston
|
||||
Pacific/Midway
|
||||
Pacific/Ponape
|
||||
Pacific/Saipan
|
||||
Pacific/Samoa
|
||||
Pacific/Truk
|
||||
Pacific/Yap
|
||||
|
|
|
@ -1,13 +1,5 @@
|
|||
import sys
|
||||
|
||||
if sys.platform == "win32":
|
||||
from tzlocal.win32 import (
|
||||
get_localzone,
|
||||
get_localzone_name,
|
||||
reload_localzone,
|
||||
) # pragma: no cover
|
||||
if sys.platform == 'win32':
|
||||
from tzlocal.win32 import get_localzone, reload_localzone
|
||||
else:
|
||||
from tzlocal.unix import get_localzone, get_localzone_name, reload_localzone
|
||||
|
||||
|
||||
__all__ = ["get_localzone", "get_localzone_name", "reload_localzone"]
|
||||
from tzlocal.unix import get_localzone, reload_localzone
|
||||
|
|
|
@ -1,75 +1,97 @@
|
|||
import os
|
||||
import pytz
|
||||
import re
|
||||
import sys
|
||||
import warnings
|
||||
from datetime import timezone
|
||||
import pytz_deprecation_shim as pds
|
||||
|
||||
from tzlocal import utils
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
from zoneinfo import ZoneInfo # pragma: no cover
|
||||
else:
|
||||
from backports.zoneinfo import ZoneInfo # pragma: no cover
|
||||
|
||||
_cache_tz = None
|
||||
_cache_tz_name = None
|
||||
|
||||
|
||||
def _get_localzone_name(_root="/"):
|
||||
def _tz_from_env(tzenv):
|
||||
if tzenv[0] == ':':
|
||||
tzenv = tzenv[1:]
|
||||
|
||||
# TZ specifies a file
|
||||
if os.path.isabs(tzenv) and os.path.exists(tzenv):
|
||||
with open(tzenv, 'rb') as tzfile:
|
||||
return pytz.tzfile.build_tzinfo('local', tzfile)
|
||||
|
||||
# TZ specifies a zoneinfo zone.
|
||||
try:
|
||||
tz = pytz.timezone(tzenv)
|
||||
# That worked, so we return this:
|
||||
return tz
|
||||
except pytz.UnknownTimeZoneError:
|
||||
raise pytz.UnknownTimeZoneError(
|
||||
"tzlocal() does not support non-zoneinfo timezones like %s. \n"
|
||||
"Please use a timezone in the form of Continent/City")
|
||||
|
||||
|
||||
def _try_tz_from_env():
|
||||
tzenv = os.environ.get('TZ')
|
||||
if tzenv:
|
||||
try:
|
||||
return _tz_from_env(tzenv)
|
||||
except pytz.UnknownTimeZoneError:
|
||||
pass
|
||||
|
||||
|
||||
def _get_localzone(_root='/'):
|
||||
"""Tries to find the local timezone configuration.
|
||||
|
||||
This method finds the timezone name, if it can, or it returns None.
|
||||
This method prefers finding the timezone name and passing that to pytz,
|
||||
over passing in the localtime file, as in the later case the zoneinfo
|
||||
name is unknown.
|
||||
|
||||
The parameter _root makes the function look for files like /etc/localtime
|
||||
beneath the _root directory. This is primarily used by the tests.
|
||||
In normal usage you call the function without parameters."""
|
||||
|
||||
# First try the ENV setting.
|
||||
tzenv = utils._tz_name_from_env()
|
||||
tzenv = _try_tz_from_env()
|
||||
if tzenv:
|
||||
return tzenv
|
||||
|
||||
# Are we under Termux on Android?
|
||||
if os.path.exists(os.path.join(_root, "system/bin/getprop")):
|
||||
if os.path.exists('/system/bin/getprop'):
|
||||
import subprocess
|
||||
|
||||
androidtz = (
|
||||
subprocess.check_output(["getprop", "persist.sys.timezone"])
|
||||
.strip()
|
||||
.decode()
|
||||
)
|
||||
return androidtz
|
||||
androidtz = subprocess.check_output(['getprop', 'persist.sys.timezone']).strip().decode()
|
||||
return pytz.timezone(androidtz)
|
||||
|
||||
# Now look for distribution specific configuration files
|
||||
# that contain the timezone name.
|
||||
|
||||
# Stick all of them in a dict, to compare later.
|
||||
found_configs = {}
|
||||
|
||||
for configfile in ("etc/timezone", "var/db/zoneinfo"):
|
||||
for configfile in ('etc/timezone', 'var/db/zoneinfo'):
|
||||
tzpath = os.path.join(_root, configfile)
|
||||
try:
|
||||
with open(tzpath, "rt") as tzfile:
|
||||
with open(tzpath, 'rb') as tzfile:
|
||||
data = tzfile.read()
|
||||
|
||||
etctz = data.strip('/ \t\r\n')
|
||||
# Issue #3 was that /etc/timezone was a zoneinfo file.
|
||||
# That's a misconfiguration, but we need to handle it gracefully:
|
||||
if data[:5] == b'TZif2':
|
||||
continue
|
||||
|
||||
etctz = data.strip().decode()
|
||||
if not etctz:
|
||||
# Empty file, skip
|
||||
continue
|
||||
for etctz in etctz.splitlines():
|
||||
for etctz in data.decode().splitlines():
|
||||
# Get rid of host definitions and comments:
|
||||
if " " in etctz:
|
||||
etctz, dummy = etctz.split(" ", 1)
|
||||
if "#" in etctz:
|
||||
etctz, dummy = etctz.split("#", 1)
|
||||
if ' ' in etctz:
|
||||
etctz, dummy = etctz.split(' ', 1)
|
||||
if '#' in etctz:
|
||||
etctz, dummy = etctz.split('#', 1)
|
||||
if not etctz:
|
||||
continue
|
||||
tz = pytz.timezone(etctz.replace(' ', '_'))
|
||||
if _root == '/':
|
||||
# We are using a file in etc to name the timezone.
|
||||
# Verify that the timezone specified there is actually used:
|
||||
utils.assert_tz_offset(tz)
|
||||
return tz
|
||||
|
||||
found_configs[tzpath] = etctz.replace(" ", "_")
|
||||
|
||||
except (IOError, UnicodeDecodeError):
|
||||
# File doesn't exist or is a directory, or it's a binary file.
|
||||
except IOError:
|
||||
# File doesn't exist or is a directory
|
||||
continue
|
||||
|
||||
# CentOS has a ZONE setting in /etc/sysconfig/clock,
|
||||
|
@ -77,14 +99,14 @@ def _get_localzone_name(_root="/"):
|
|||
# Gentoo has a TIMEZONE setting in /etc/conf.d/clock
|
||||
# We look through these files for a timezone:
|
||||
|
||||
zone_re = re.compile(r"\s*ZONE\s*=\s*\"")
|
||||
timezone_re = re.compile(r"\s*TIMEZONE\s*=\s*\"")
|
||||
end_re = re.compile('"')
|
||||
zone_re = re.compile(r'\s*ZONE\s*=\s*\"')
|
||||
timezone_re = re.compile(r'\s*TIMEZONE\s*=\s*\"')
|
||||
end_re = re.compile('\"')
|
||||
|
||||
for filename in ("etc/sysconfig/clock", "etc/conf.d/clock"):
|
||||
for filename in ('etc/sysconfig/clock', 'etc/conf.d/clock'):
|
||||
tzpath = os.path.join(_root, filename)
|
||||
try:
|
||||
with open(tzpath, "rt") as tzfile:
|
||||
with open(tzpath, 'rt') as tzfile:
|
||||
data = tzfile.readlines()
|
||||
|
||||
for line in data:
|
||||
|
@ -96,108 +118,48 @@ def _get_localzone_name(_root="/"):
|
|||
if match is not None:
|
||||
# Some setting existed
|
||||
line = line[match.end():]
|
||||
etctz = line[: end_re.search(line).start()]
|
||||
etctz = line[:end_re.search(line).start()]
|
||||
|
||||
# We found a timezone
|
||||
found_configs[tzpath] = etctz.replace(" ", "_")
|
||||
|
||||
except (IOError, UnicodeDecodeError):
|
||||
# UnicodeDecode handles when clock is symlink to /etc/localtime
|
||||
continue
|
||||
|
||||
# systemd distributions use symlinks that include the zone name,
|
||||
# see manpage of localtime(5) and timedatectl(1)
|
||||
tzpath = os.path.join(_root, "etc/localtime")
|
||||
if os.path.exists(tzpath) and os.path.islink(tzpath):
|
||||
etctz = realtzpath = os.path.realpath(tzpath)
|
||||
start = etctz.find("/") + 1
|
||||
while start != 0:
|
||||
etctz = etctz[start:]
|
||||
try:
|
||||
pds.timezone(etctz)
|
||||
tzinfo = f"{tzpath} is a symlink to"
|
||||
found_configs[tzinfo] = etctz.replace(" ", "_")
|
||||
except pds.UnknownTimeZoneError:
|
||||
pass
|
||||
start = etctz.find("/") + 1
|
||||
|
||||
if len(found_configs) > 0:
|
||||
# We found some explicit config of some sort!
|
||||
if len(found_configs) > 1:
|
||||
# Uh-oh, multiple configs. See if they match:
|
||||
unique_tzs = set()
|
||||
zoneinfo = os.path.join(_root, "usr", "share", "zoneinfo")
|
||||
directory_depth = len(zoneinfo.split(os.path.sep))
|
||||
|
||||
for tzname in found_configs.values():
|
||||
# Look them up in /usr/share/zoneinfo, and find what they
|
||||
# really point to:
|
||||
path = os.path.realpath(os.path.join(zoneinfo, *tzname.split("/")))
|
||||
real_zone_name = "/".join(path.split(os.path.sep)[directory_depth:])
|
||||
unique_tzs.add(real_zone_name)
|
||||
|
||||
if len(unique_tzs) != 1:
|
||||
message = "Multiple conflicting time zone configurations found:\n"
|
||||
for key, value in found_configs.items():
|
||||
message += f"{key}: {value}\n"
|
||||
message += "Fix the configuration, or set the time zone in a TZ environment variable.\n"
|
||||
raise utils.ZoneInfoNotFoundError(message)
|
||||
|
||||
# We found exactly one config! Use it.
|
||||
return list(found_configs.values())[0]
|
||||
|
||||
|
||||
def _get_localzone(_root="/"):
|
||||
"""Creates a timezone object from the timezone name.
|
||||
|
||||
If there is no timezone config, it will try to create a file from the
|
||||
localtime timezone, and if there isn't one, it will default to UTC.
|
||||
|
||||
The parameter _root makes the function look for files like /etc/localtime
|
||||
beneath the _root directory. This is primarily used by the tests.
|
||||
In normal usage you call the function without parameters."""
|
||||
|
||||
# First try the ENV setting.
|
||||
tzenv = utils._tz_from_env()
|
||||
if tzenv:
|
||||
return tzenv
|
||||
|
||||
tzname = _get_localzone_name(_root)
|
||||
if tzname is None:
|
||||
# No explicit setting existed. Use localtime
|
||||
for filename in ("etc/localtime", "usr/local/etc/localtime"):
|
||||
tzpath = os.path.join(_root, filename)
|
||||
|
||||
if not os.path.exists(tzpath):
|
||||
continue
|
||||
with open(tzpath, "rb") as tzfile:
|
||||
tz = pds.wrap_zone(ZoneInfo.from_file(tzfile, key="local"))
|
||||
break
|
||||
else:
|
||||
warnings.warn("Can not find any timezone configuration, defaulting to UTC.")
|
||||
tz = timezone.utc
|
||||
else:
|
||||
tz = pds.timezone(tzname)
|
||||
|
||||
if _root == "/":
|
||||
tz = pytz.timezone(etctz.replace(' ', '_'))
|
||||
if _root == '/':
|
||||
# We are using a file in etc to name the timezone.
|
||||
# Verify that the timezone specified there is actually used:
|
||||
utils.assert_tz_offset(tz)
|
||||
return tz
|
||||
|
||||
except IOError:
|
||||
# File doesn't exist or is a directory
|
||||
continue
|
||||
|
||||
def get_localzone_name():
|
||||
"""Get the computers configured local timezone name, if any."""
|
||||
global _cache_tz_name
|
||||
if _cache_tz_name is None:
|
||||
_cache_tz_name = _get_localzone_name()
|
||||
# systemd distributions use symlinks that include the zone name,
|
||||
# see manpage of localtime(5) and timedatectl(1)
|
||||
tzpath = os.path.join(_root, 'etc/localtime')
|
||||
if os.path.exists(tzpath) and os.path.islink(tzpath):
|
||||
tzpath = os.path.realpath(tzpath)
|
||||
start = tzpath.find("/")+1
|
||||
while start != 0:
|
||||
tzpath = tzpath[start:]
|
||||
try:
|
||||
return pytz.timezone(tzpath)
|
||||
except pytz.UnknownTimeZoneError:
|
||||
pass
|
||||
start = tzpath.find("/")+1
|
||||
|
||||
return _cache_tz_name
|
||||
# No explicit setting existed. Use localtime
|
||||
for filename in ('etc/localtime', 'usr/local/etc/localtime'):
|
||||
tzpath = os.path.join(_root, filename)
|
||||
|
||||
if not os.path.exists(tzpath):
|
||||
continue
|
||||
with open(tzpath, 'rb') as tzfile:
|
||||
return pytz.tzfile.build_tzinfo('local', tzfile)
|
||||
|
||||
warnings.warn('Can not find any timezone configuration, defaulting to UTC.')
|
||||
return pytz.utc
|
||||
|
||||
def get_localzone():
|
||||
"""Get the computers configured local timezone, if any."""
|
||||
|
||||
global _cache_tz
|
||||
if _cache_tz is None:
|
||||
_cache_tz = _get_localzone()
|
||||
|
@ -207,9 +169,6 @@ def get_localzone():
|
|||
|
||||
def reload_localzone():
|
||||
"""Reload the cached localzone. You need to call this if the timezone has changed."""
|
||||
global _cache_tz_name
|
||||
global _cache_tz
|
||||
_cache_tz_name = _get_localzone_name()
|
||||
_cache_tz = _get_localzone()
|
||||
|
||||
return _cache_tz
|
||||
|
|
|
@ -1,24 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import time
|
||||
import datetime
|
||||
import calendar
|
||||
import pytz_deprecation_shim as pds
|
||||
|
||||
try:
|
||||
import zoneinfo # pragma: no cover
|
||||
except ImportError:
|
||||
from backports import zoneinfo # pragma: no cover
|
||||
|
||||
from tzlocal import windows_tz
|
||||
|
||||
|
||||
class ZoneInfoNotFoundError(pds.UnknownTimeZoneError, zoneinfo.ZoneInfoNotFoundError):
|
||||
"""An exception derived from both pytz and zoneinfo
|
||||
|
||||
This exception will be trappable both by pytz expecting clients and
|
||||
zoneinfo expecting clients.
|
||||
"""
|
||||
|
||||
|
||||
def get_system_offset():
|
||||
|
@ -38,9 +21,9 @@ def get_system_offset():
|
|||
# so we check that the difference is less than one minute, because nobody
|
||||
# has that small DST differences.
|
||||
if abs(offset - time.altzone) < 60:
|
||||
return -time.altzone # pragma: no cover
|
||||
return -time.altzone
|
||||
else:
|
||||
return -time.timezone # pragma: no cover
|
||||
return -time.timezone
|
||||
|
||||
|
||||
def get_tz_offset(tz):
|
||||
|
@ -56,70 +39,8 @@ def assert_tz_offset(tz):
|
|||
tz_offset = get_tz_offset(tz)
|
||||
system_offset = get_system_offset()
|
||||
if tz_offset != system_offset:
|
||||
msg = (
|
||||
"Timezone offset does not match system offset: {} != {}. "
|
||||
"Please, check your config files."
|
||||
).format(tz_offset, system_offset)
|
||||
msg = ('Timezone offset does not match system offset: {0} != {1}. '
|
||||
'Please, check your config files.').format(
|
||||
tz_offset, system_offset
|
||||
)
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
def _tz_name_from_env(tzenv=None):
|
||||
if tzenv is None:
|
||||
tzenv = os.environ.get("TZ")
|
||||
|
||||
if not tzenv:
|
||||
return None
|
||||
|
||||
if tzenv in windows_tz.tz_win:
|
||||
# Yup, it's a timezone
|
||||
return tzenv
|
||||
|
||||
if os.path.isabs(tzenv) and os.path.exists(tzenv):
|
||||
# It's a file specification
|
||||
parts = tzenv.split(os.sep)
|
||||
|
||||
# Is it a zone info zone?
|
||||
possible_tz = "/".join(parts[-2:])
|
||||
if possible_tz in windows_tz.tz_win:
|
||||
# Yup, it is
|
||||
return possible_tz
|
||||
|
||||
# Maybe it's a short one, like UTC?
|
||||
if parts[-1] in windows_tz.tz_win:
|
||||
# Indeed
|
||||
return parts[-1]
|
||||
|
||||
|
||||
def _tz_from_env(tzenv=None):
|
||||
if tzenv is None:
|
||||
tzenv = os.environ.get("TZ")
|
||||
|
||||
if not tzenv:
|
||||
return None
|
||||
|
||||
# Some weird format that exists:
|
||||
if tzenv[0] == ":":
|
||||
tzenv = tzenv[1:]
|
||||
|
||||
# TZ specifies a file
|
||||
if os.path.isabs(tzenv) and os.path.exists(tzenv):
|
||||
# Try to see if we can figure out the name
|
||||
tzname = _tz_name_from_env(tzenv)
|
||||
if not tzname:
|
||||
# Nope, not a standard timezone name, just take the filename
|
||||
tzname = tzenv.split(os.sep)[-1]
|
||||
with open(tzenv, "rb") as tzfile:
|
||||
zone = zoneinfo.ZoneInfo.from_file(tzfile, key=tzname)
|
||||
return pds.wrap_zone(zone)
|
||||
|
||||
# TZ must specify a zoneinfo zone.
|
||||
try:
|
||||
tz = pds.timezone(tzenv)
|
||||
# That worked, so we return this:
|
||||
return tz
|
||||
except pds.UnknownTimeZoneError:
|
||||
# Nope, it's something like "PST4DST" etc, we can't handle that.
|
||||
raise ZoneInfoNotFoundError(
|
||||
"tzlocal() does not support non-zoneinfo timezones like %s. \n"
|
||||
"Please use a timezone in the form of Continent/City"
|
||||
) from None
|
||||
|
|
|
@ -1,53 +1,32 @@
|
|||
from datetime import datetime
|
||||
import pytz_deprecation_shim as pds
|
||||
|
||||
try:
|
||||
import _winreg as winreg
|
||||
except ImportError:
|
||||
import winreg
|
||||
|
||||
import pytz
|
||||
|
||||
from tzlocal.windows_tz import win_tz
|
||||
from tzlocal import utils
|
||||
|
||||
_cache_tz = None
|
||||
_cache_tz_name = None
|
||||
|
||||
|
||||
def valuestodict(key):
|
||||
"""Convert a registry key's values to a dictionary."""
|
||||
result = {}
|
||||
dict = {}
|
||||
size = winreg.QueryInfoKey(key)[1]
|
||||
for i in range(size):
|
||||
data = winreg.EnumValue(key, i)
|
||||
result[data[0]] = data[1]
|
||||
return result
|
||||
dict[data[0]] = data[1]
|
||||
return dict
|
||||
|
||||
|
||||
def _get_dst_info(tz):
|
||||
# Find the offset for when it doesn't have DST:
|
||||
dst_offset = std_offset = None
|
||||
has_dst = False
|
||||
year = datetime.now().year
|
||||
for dt in (datetime(year, 1, 1), datetime(year, 6, 1)):
|
||||
if tz.dst(dt).total_seconds() == 0.0:
|
||||
# OK, no DST during winter, get this offset
|
||||
std_offset = tz.utcoffset(dt).total_seconds()
|
||||
else:
|
||||
has_dst = True
|
||||
|
||||
return has_dst, std_offset, dst_offset
|
||||
|
||||
|
||||
def _get_localzone_name():
|
||||
def get_localzone_name():
|
||||
# Windows is special. It has unique time zone names (in several
|
||||
# meanings of the word) available, but unfortunately, they can be
|
||||
# translated to the language of the operating system, so we need to
|
||||
# do a backwards lookup, by going through all time zones and see which
|
||||
# one matches.
|
||||
tzenv = utils._tz_name_from_env()
|
||||
if tzenv:
|
||||
return tzenv
|
||||
|
||||
handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
|
||||
|
||||
TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"
|
||||
|
@ -55,16 +34,44 @@ def _get_localzone_name():
|
|||
keyvalues = valuestodict(localtz)
|
||||
localtz.Close()
|
||||
|
||||
if "TimeZoneKeyName" in keyvalues:
|
||||
# Windows 7 and later
|
||||
if 'TimeZoneKeyName' in keyvalues:
|
||||
# Windows 7 (and Vista?)
|
||||
|
||||
# For some reason this returns a string with loads of NUL bytes at
|
||||
# least on some systems. I don't know if this is a bug somewhere, I
|
||||
# just work around it.
|
||||
tzkeyname = keyvalues["TimeZoneKeyName"].split("\x00", 1)[0]
|
||||
tzkeyname = keyvalues['TimeZoneKeyName'].split('\x00', 1)[0]
|
||||
else:
|
||||
# Don't support XP any longer
|
||||
raise LookupError("Can not find Windows timezone configuration")
|
||||
# Windows 2000 or XP
|
||||
|
||||
# This is the localized name:
|
||||
tzwin = keyvalues['StandardName']
|
||||
|
||||
# Open the list of timezones to look up the real name:
|
||||
TZKEYNAME = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones"
|
||||
tzkey = winreg.OpenKey(handle, TZKEYNAME)
|
||||
|
||||
# Now, match this value to Time Zone information
|
||||
tzkeyname = None
|
||||
for i in range(winreg.QueryInfoKey(tzkey)[0]):
|
||||
subkey = winreg.EnumKey(tzkey, i)
|
||||
sub = winreg.OpenKey(tzkey, subkey)
|
||||
data = valuestodict(sub)
|
||||
sub.Close()
|
||||
try:
|
||||
if data['Std'] == tzwin:
|
||||
tzkeyname = subkey
|
||||
break
|
||||
except KeyError:
|
||||
# This timezone didn't have proper configuration.
|
||||
# Ignore it.
|
||||
pass
|
||||
|
||||
tzkey.Close()
|
||||
handle.Close()
|
||||
|
||||
if tzkeyname is None:
|
||||
raise LookupError('Can not find Windows timezone configuration')
|
||||
|
||||
timezone = win_tz.get(tzkeyname)
|
||||
if timezone is None:
|
||||
|
@ -74,64 +81,24 @@ def _get_localzone_name():
|
|||
|
||||
# Return what we have.
|
||||
if timezone is None:
|
||||
raise utils.ZoneInfoNotFoundError(tzkeyname)
|
||||
|
||||
if keyvalues.get("DynamicDaylightTimeDisabled", 0) == 1:
|
||||
# DST is disabled, so don't return the timezone name,
|
||||
# instead return Etc/GMT+offset
|
||||
|
||||
tz = pds.timezone(timezone)
|
||||
has_dst, std_offset, dst_offset = _get_dst_info(tz)
|
||||
if not has_dst:
|
||||
# The DST is turned off in the windows configuration,
|
||||
# but this timezone doesn't have DST so it doesn't matter
|
||||
return timezone
|
||||
|
||||
if std_offset is None:
|
||||
raise utils.ZoneInfoNotFoundError(
|
||||
f"{tzkeyname} claims to not have a non-DST time!?")
|
||||
|
||||
if std_offset % 3600:
|
||||
# I can't convert this to an hourly offset
|
||||
raise utils.ZoneInfoNotFoundError(
|
||||
f"tzlocal can't support disabling DST in the {timezone} zone.")
|
||||
|
||||
# This has whole hours as offset, return it as Etc/GMT
|
||||
return f"Etc/GMT{-std_offset//3600:+.0f}"
|
||||
raise pytz.UnknownTimeZoneError('Can not find timezone ' + tzkeyname)
|
||||
|
||||
return timezone
|
||||
|
||||
|
||||
def get_localzone_name():
|
||||
"""Get the zoneinfo timezone name that matches the Windows-configured timezone."""
|
||||
global _cache_tz_name
|
||||
if _cache_tz_name is None:
|
||||
_cache_tz_name = _get_localzone_name()
|
||||
|
||||
return _cache_tz_name
|
||||
|
||||
|
||||
def get_localzone():
|
||||
"""Returns the zoneinfo-based tzinfo object that matches the Windows-configured timezone."""
|
||||
|
||||
global _cache_tz
|
||||
if _cache_tz is None:
|
||||
_cache_tz = pds.timezone(get_localzone_name())
|
||||
_cache_tz = pytz.timezone(get_localzone_name())
|
||||
|
||||
if not utils._tz_name_from_env():
|
||||
# If the timezone does NOT come from a TZ environment variable,
|
||||
# verify that it's correct. If it's from the environment,
|
||||
# we accept it, this is so you can run tests with different timezones.
|
||||
utils.assert_tz_offset(_cache_tz)
|
||||
|
||||
return _cache_tz
|
||||
|
||||
|
||||
def reload_localzone():
|
||||
"""Reload the cached localzone. You need to call this if the timezone has changed."""
|
||||
global _cache_tz
|
||||
global _cache_tz_name
|
||||
_cache_tz_name = _get_localzone_name()
|
||||
_cache_tz = pds.timezone(_cache_tz_name)
|
||||
_cache_tz = pytz.timezone(get_localzone_name())
|
||||
utils.assert_tz_offset(_cache_tz)
|
||||
return _cache_tz
|
||||
|
|
|
@ -104,7 +104,6 @@ win_tz = {'AUS Central Standard Time': 'Australia/Darwin',
|
|||
'Saratov Standard Time': 'Europe/Saratov',
|
||||
'Singapore Standard Time': 'Asia/Singapore',
|
||||
'South Africa Standard Time': 'Africa/Johannesburg',
|
||||
'South Sudan Standard Time': 'Africa/Juba',
|
||||
'Sri Lanka Standard Time': 'Asia/Colombo',
|
||||
'Sudan Standard Time': 'Africa/Khartoum',
|
||||
'Syria Standard Time': 'Asia/Damascus',
|
||||
|
@ -119,7 +118,7 @@ win_tz = {'AUS Central Standard Time': 'Australia/Darwin',
|
|||
'Turks And Caicos Standard Time': 'America/Grand_Turk',
|
||||
'US Eastern Standard Time': 'America/Indianapolis',
|
||||
'US Mountain Standard Time': 'America/Phoenix',
|
||||
'UTC': 'Etc/UTC',
|
||||
'UTC': 'Etc/GMT',
|
||||
'UTC+12': 'Etc/GMT-12',
|
||||
'UTC+13': 'Etc/GMT-13',
|
||||
'UTC-02': 'Etc/GMT+2',
|
||||
|
@ -137,8 +136,7 @@ win_tz = {'AUS Central Standard Time': 'Australia/Darwin',
|
|||
'West Asia Standard Time': 'Asia/Tashkent',
|
||||
'West Bank Standard Time': 'Asia/Hebron',
|
||||
'West Pacific Standard Time': 'Pacific/Port_Moresby',
|
||||
'Yakutsk Standard Time': 'Asia/Yakutsk',
|
||||
'Yukon Standard Time': 'America/Whitehorse'}
|
||||
'Yakutsk Standard Time': 'Asia/Yakutsk'}
|
||||
|
||||
# Old name for the win_tz variable:
|
||||
tz_names = win_tz
|
||||
|
@ -168,7 +166,7 @@ tz_win = {'Africa/Abidjan': 'Greenwich Standard Time',
|
|||
'Africa/Gaborone': 'South Africa Standard Time',
|
||||
'Africa/Harare': 'South Africa Standard Time',
|
||||
'Africa/Johannesburg': 'South Africa Standard Time',
|
||||
'Africa/Juba': 'South Sudan Standard Time',
|
||||
'Africa/Juba': 'E. Africa Standard Time',
|
||||
'Africa/Kampala': 'E. Africa Standard Time',
|
||||
'Africa/Khartoum': 'Sudan Standard Time',
|
||||
'Africa/Kigali': 'South Africa Standard Time',
|
||||
|
@ -236,8 +234,8 @@ tz_win = {'Africa/Abidjan': 'Greenwich Standard Time',
|
|||
'America/Creston': 'US Mountain Standard Time',
|
||||
'America/Cuiaba': 'Central Brazilian Standard Time',
|
||||
'America/Curacao': 'SA Western Standard Time',
|
||||
'America/Danmarkshavn': 'Greenwich Standard Time',
|
||||
'America/Dawson': 'Yukon Standard Time',
|
||||
'America/Danmarkshavn': 'UTC',
|
||||
'America/Dawson': 'Pacific Standard Time',
|
||||
'America/Dawson_Creek': 'US Mountain Standard Time',
|
||||
'America/Denver': 'Mountain Standard Time',
|
||||
'America/Detroit': 'Eastern Standard Time',
|
||||
|
@ -347,14 +345,14 @@ tz_win = {'Africa/Abidjan': 'Greenwich Standard Time',
|
|||
'America/Tortola': 'SA Western Standard Time',
|
||||
'America/Vancouver': 'Pacific Standard Time',
|
||||
'America/Virgin': 'SA Western Standard Time',
|
||||
'America/Whitehorse': 'Yukon Standard Time',
|
||||
'America/Whitehorse': 'Pacific Standard Time',
|
||||
'America/Winnipeg': 'Central Standard Time',
|
||||
'America/Yakutat': 'Alaskan Standard Time',
|
||||
'America/Yellowknife': 'Mountain Standard Time',
|
||||
'Antarctica/Casey': 'Central Pacific Standard Time',
|
||||
'Antarctica/Casey': 'Singapore Standard Time',
|
||||
'Antarctica/Davis': 'SE Asia Standard Time',
|
||||
'Antarctica/DumontDUrville': 'West Pacific Standard Time',
|
||||
'Antarctica/Macquarie': 'Tasmania Standard Time',
|
||||
'Antarctica/Macquarie': 'Central Pacific Standard Time',
|
||||
'Antarctica/Mawson': 'West Asia Standard Time',
|
||||
'Antarctica/McMurdo': 'New Zealand Standard Time',
|
||||
'Antarctica/Palmer': 'SA Eastern Standard Time',
|
||||
|
@ -503,7 +501,7 @@ tz_win = {'Africa/Abidjan': 'Greenwich Standard Time',
|
|||
'Canada/Newfoundland': 'Newfoundland Standard Time',
|
||||
'Canada/Pacific': 'Pacific Standard Time',
|
||||
'Canada/Saskatchewan': 'Canada Central Standard Time',
|
||||
'Canada/Yukon': 'Yukon Standard Time',
|
||||
'Canada/Yukon': 'Pacific Standard Time',
|
||||
'Chile/Continental': 'Pacific SA Standard Time',
|
||||
'Chile/EasterIsland': 'Easter Island Standard Time',
|
||||
'Cuba': 'Cuba Standard Time',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue