mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-05 20:51:15 -07:00
Bump apscheduler from 3.8.0 to 3.9.1 (#1675)
* Bump apscheduler from 3.8.0 to 3.9.1 Bumps [apscheduler](https://github.com/agronholm/apscheduler) from 3.8.0 to 3.9.1. - [Release notes](https://github.com/agronholm/apscheduler/releases) - [Changelog](https://github.com/agronholm/apscheduler/blob/3.9.1/docs/versionhistory.rst) - [Commits](https://github.com/agronholm/apscheduler/compare/3.8.0...3.9.1) --- updated-dependencies: - dependency-name: apscheduler dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * Update apscheduler==3.9.1 * Update pytz==2022.1 * Add pytz-deprecation-shim==0.1.0.post0 * Update tzdata==2022.1 * Update tzlocal==4.2 * Update requirements.txt Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> [skip ci]
This commit is contained in:
parent
942e09e59e
commit
54c9214b03
46 changed files with 1029 additions and 223 deletions
|
@ -106,7 +106,7 @@ class MongoDBJobStore(BaseJobStore):
|
|||
raise JobLookupError(job_id)
|
||||
|
||||
def remove_all_jobs(self):
|
||||
self.collection.remove()
|
||||
self.collection.delete_many({})
|
||||
|
||||
def shutdown(self):
|
||||
self.client.close()
|
||||
|
@ -133,7 +133,7 @@ class MongoDBJobStore(BaseJobStore):
|
|||
|
||||
# Remove all the jobs we failed to restore
|
||||
if failed_job_ids:
|
||||
self.collection.remove({'_id': {'$in': failed_job_ids}})
|
||||
self.collection.delete_many({'_id': {'$in': failed_job_ids}})
|
||||
|
||||
return jobs
|
||||
|
||||
|
|
|
@ -191,12 +191,11 @@ class BaseScheduler(six.with_metaclass(ABCMeta)):
|
|||
self.state = STATE_STOPPED
|
||||
|
||||
# Shut down all executors
|
||||
with self._executors_lock:
|
||||
with self._executors_lock, self._jobstores_lock:
|
||||
for executor in six.itervalues(self._executors):
|
||||
executor.shutdown(wait)
|
||||
|
||||
# Shut down all job stores
|
||||
with self._jobstores_lock:
|
||||
# Shut down all job stores
|
||||
for jobstore in six.itervalues(self._jobstores):
|
||||
jobstore.shutdown()
|
||||
|
||||
|
|
|
@ -9,13 +9,16 @@ except (ImportError, RuntimeError): # pragma: nocover
|
|||
from PyQt4.QtCore import QObject, QTimer
|
||||
except ImportError:
|
||||
try:
|
||||
from PySide2.QtCore import QObject, QTimer # noqa
|
||||
from PySide6.QtCore import QObject, QTimer # noqa
|
||||
except ImportError:
|
||||
try:
|
||||
from PySide.QtCore import QObject, QTimer # noqa
|
||||
from PySide2.QtCore import QObject, QTimer # noqa
|
||||
except ImportError:
|
||||
raise ImportError('QtScheduler requires either PyQt5, PyQt4, PySide2 '
|
||||
'or PySide installed')
|
||||
try:
|
||||
from PySide.QtCore import QObject, QTimer # noqa
|
||||
except ImportError:
|
||||
raise ImportError('QtScheduler requires either PyQt5, PyQt4, PySide6, PySide2 '
|
||||
'or PySide installed')
|
||||
|
||||
|
||||
class QtScheduler(BaseScheduler):
|
||||
|
|
|
@ -6,7 +6,8 @@ import six
|
|||
from apscheduler.triggers.base import BaseTrigger
|
||||
from apscheduler.triggers.cron.fields import (
|
||||
BaseField, MonthField, WeekField, DayOfMonthField, DayOfWeekField, DEFAULT_VALUES)
|
||||
from apscheduler.util import datetime_ceil, convert_to_datetime, datetime_repr, astimezone
|
||||
from apscheduler.util import (
|
||||
datetime_ceil, convert_to_datetime, datetime_repr, astimezone, localize, normalize)
|
||||
|
||||
|
||||
class CronTrigger(BaseTrigger):
|
||||
|
@ -143,7 +144,7 @@ class CronTrigger(BaseTrigger):
|
|||
i += 1
|
||||
|
||||
difference = datetime(**values) - dateval.replace(tzinfo=None)
|
||||
return self.timezone.normalize(dateval + difference), fieldnum
|
||||
return normalize(dateval + difference), fieldnum
|
||||
|
||||
def _set_field_value(self, dateval, fieldnum, new_value):
|
||||
values = {}
|
||||
|
@ -156,7 +157,7 @@ class CronTrigger(BaseTrigger):
|
|||
else:
|
||||
values[field.name] = new_value
|
||||
|
||||
return self.timezone.localize(datetime(**values))
|
||||
return localize(datetime(**values), self.timezone)
|
||||
|
||||
def get_next_fire_time(self, previous_fire_time, now):
|
||||
if previous_fire_time:
|
||||
|
|
|
@ -4,7 +4,9 @@ from math import ceil
|
|||
from tzlocal import get_localzone
|
||||
|
||||
from apscheduler.triggers.base import BaseTrigger
|
||||
from apscheduler.util import convert_to_datetime, timedelta_seconds, datetime_repr, astimezone
|
||||
from apscheduler.util import (
|
||||
convert_to_datetime, normalize, timedelta_seconds, datetime_repr,
|
||||
astimezone)
|
||||
|
||||
|
||||
class IntervalTrigger(BaseTrigger):
|
||||
|
@ -63,7 +65,7 @@ class IntervalTrigger(BaseTrigger):
|
|||
next_fire_time = self._apply_jitter(next_fire_time, self.jitter, now)
|
||||
|
||||
if not self.end_date or next_fire_time <= self.end_date:
|
||||
return self.timezone.normalize(next_fire_time)
|
||||
return normalize(next_fire_time)
|
||||
|
||||
def __getstate__(self):
|
||||
return {
|
||||
|
|
|
@ -34,7 +34,7 @@ except ImportError:
|
|||
__all__ = ('asint', 'asbool', 'astimezone', 'convert_to_datetime', 'datetime_to_utc_timestamp',
|
||||
'utc_timestamp_to_datetime', 'timedelta_seconds', 'datetime_ceil', 'get_callable_name',
|
||||
'obj_to_ref', 'ref_to_obj', 'maybe_ref', 'repr_escape', 'check_callable_args',
|
||||
'TIMEOUT_MAX')
|
||||
'normalize', 'localize', 'TIMEOUT_MAX')
|
||||
|
||||
|
||||
class _Undefined(object):
|
||||
|
@ -90,9 +90,7 @@ def astimezone(obj):
|
|||
if isinstance(obj, six.string_types):
|
||||
return timezone(obj)
|
||||
if isinstance(obj, tzinfo):
|
||||
if not hasattr(obj, 'localize') or not hasattr(obj, 'normalize'):
|
||||
raise TypeError('Only timezones from the pytz library are supported')
|
||||
if obj.zone == 'local':
|
||||
if obj.tzname(None) == 'local':
|
||||
raise ValueError(
|
||||
'Unable to determine the name of the local timezone -- you must explicitly '
|
||||
'specify the name of the local timezone. Please refrain from using timezones like '
|
||||
|
@ -162,11 +160,7 @@ def convert_to_datetime(input, tz, arg_name):
|
|||
if isinstance(tz, six.string_types):
|
||||
tz = timezone(tz)
|
||||
|
||||
try:
|
||||
return tz.localize(datetime_, is_dst=None)
|
||||
except AttributeError:
|
||||
raise TypeError(
|
||||
'Only pytz timezones are supported (need the localize() and normalize() methods)')
|
||||
return localize(datetime_, tz)
|
||||
|
||||
|
||||
def datetime_to_utc_timestamp(timeval):
|
||||
|
@ -431,3 +425,14 @@ def iscoroutinefunction_partial(f):
|
|||
# The asyncio version of iscoroutinefunction includes testing for @coroutine
|
||||
# decorations vs. the inspect version which does not.
|
||||
return iscoroutinefunction(f)
|
||||
|
||||
|
||||
def normalize(dt):
|
||||
return datetime.fromtimestamp(dt.timestamp(), dt.tzinfo)
|
||||
|
||||
|
||||
def localize(dt, tzinfo):
|
||||
if hasattr(tzinfo, 'localize'):
|
||||
return tzinfo.localize(dt)
|
||||
|
||||
return normalize(dt.replace(tzinfo=tzinfo))
|
||||
|
|
|
@ -22,8 +22,8 @@ from pytz.tzfile import build_tzinfo
|
|||
|
||||
|
||||
# The IANA (nee Olson) database is updated several times a year.
|
||||
OLSON_VERSION = '2021c'
|
||||
VERSION = '2021.3' # pip compatible version number.
|
||||
OLSON_VERSION = '2022a'
|
||||
VERSION = '2022.1' # pip compatible version number.
|
||||
__version__ = VERSION
|
||||
|
||||
OLSEN_VERSION = OLSON_VERSION # Old releases had this misspelling
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -72,11 +72,11 @@ Leap 2016 Dec 31 23:59:60 + S
|
|||
# Any additional leap seconds will come after this.
|
||||
# This Expires line is commented out for now,
|
||||
# so that pre-2020a zic implementations do not reject this file.
|
||||
#Expires 2022 Jun 28 00:00:00
|
||||
#Expires 2022 Dec 28 00:00:00
|
||||
|
||||
# POSIX timestamps for the data in this file:
|
||||
#updated 1467936000 (2016-07-08 00:00:00 UTC)
|
||||
#expires 1656374400 (2022-06-28 00:00:00 UTC)
|
||||
#expires 1672185600 (2022-12-28 00:00:00 UTC)
|
||||
|
||||
# Updated through IERS Bulletin C62
|
||||
# File expires on: 28 June 2022
|
||||
# Updated through IERS Bulletin C63
|
||||
# File expires on: 28 December 2022
|
||||
|
|
|
@ -1111,8 +1111,10 @@ R P 2016 2018 - Mar Sa>=24 1 1 S
|
|||
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 ma - O Sa>=24 1 0 -
|
||||
R P 2020 2021 - Mar Sa>=24 0 1 S
|
||||
R P 2020 o - O 24 1 0 -
|
||||
R P 2021 ma - O F>=23 1 0 -
|
||||
R P 2022 ma - Mar Su>=25 0 1 S
|
||||
Z Asia/Gaza 2:17:52 - LMT 1900 O
|
||||
2 Z EET/EEST 1948 May 15
|
||||
2 K EE%sT 1967 Jun 5
|
||||
|
@ -1418,10 +1420,11 @@ 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 ma - Ja Su>=12 3 0 -
|
||||
R FJ 2015 2021 - Ja Su>=12 3 0 -
|
||||
R FJ 2019 o - N Su>=8 2 1 -
|
||||
R FJ 2020 o - D 20 2 1 -
|
||||
R FJ 2021 ma - N Su>=8 2 1 -
|
||||
R FJ 2022 ma - N Su>=8 2 1 -
|
||||
R FJ 2023 ma - Ja Su>=12 3 0 -
|
||||
Z Pacific/Fiji 11:55:44 - LMT 1915 O 26
|
||||
12 FJ +12/+13
|
||||
Z Pacific/Gambier -8:59:48 - LMT 1912 O
|
||||
|
@ -2429,8 +2432,8 @@ Z Europe/Simferopol 2:16:24 - LMT 1880
|
|||
1 c CE%sT 1944 Ap 13
|
||||
3 R MSK/MSD 1990
|
||||
3 - MSK 1990 Jul 1 2
|
||||
2 - EET 1992
|
||||
2 e EE%sT 1994 May
|
||||
2 - EET 1992 Mar 20
|
||||
2 c EE%sT 1994 May
|
||||
3 e MSK/MSD 1996 Mar 31 0s
|
||||
3 1 MSD 1996 O 27 3s
|
||||
3 R MSK/MSD 1997
|
||||
|
@ -2785,7 +2788,7 @@ Z Europe/Kiev 2:2:4 - LMT 1880
|
|||
1 c CE%sT 1943 N 6
|
||||
3 R MSK/MSD 1990 Jul 1 2
|
||||
2 1 EEST 1991 S 29 3
|
||||
2 e EE%sT 1995
|
||||
2 c EE%sT 1996 May 13
|
||||
2 E EE%sT
|
||||
Z Europe/Uzhgorod 1:29:12 - LMT 1890 O
|
||||
1 - CET 1940
|
||||
|
@ -2795,8 +2798,8 @@ Z Europe/Uzhgorod 1:29:12 - LMT 1890 O
|
|||
3 R MSK/MSD 1990
|
||||
3 - MSK 1990 Jul 1 2
|
||||
1 - CET 1991 Mar 31 3
|
||||
2 - EET 1992
|
||||
2 e EE%sT 1995
|
||||
2 - EET 1992 Mar 20
|
||||
2 c EE%sT 1996 May 13
|
||||
2 E EE%sT
|
||||
Z Europe/Zaporozhye 2:20:40 - LMT 1880
|
||||
2:20 - +0220 1924 May 2
|
||||
|
@ -2804,7 +2807,8 @@ Z Europe/Zaporozhye 2:20:40 - LMT 1880
|
|||
3 - MSK 1941 Au 25
|
||||
1 c CE%sT 1943 O 25
|
||||
3 R MSK/MSD 1991 Mar 31 2
|
||||
2 e EE%sT 1995
|
||||
2 e EE%sT 1992 Mar 20
|
||||
2 c EE%sT 1996 May 13
|
||||
2 E EE%sT
|
||||
R u 1918 1919 - Mar lastSu 2 1 D
|
||||
R u 1918 1919 - O lastSu 2 0 S
|
||||
|
@ -4086,12 +4090,12 @@ R x 2016 2018 - May Su>=9 3u 0 -
|
|||
R x 2016 2018 - Au Su>=9 4u 1 -
|
||||
R x 2019 ma - Ap Su>=2 3u 0 -
|
||||
R x 2019 ma - S Su>=2 4u 1 -
|
||||
Z America/Santiago -4:42:46 - LMT 1890
|
||||
-4:42:46 - SMT 1910 Ja 10
|
||||
Z America/Santiago -4:42:45 - LMT 1890
|
||||
-4:42:45 - SMT 1910 Ja 10
|
||||
-5 - -05 1916 Jul
|
||||
-4:42:46 - SMT 1918 S 10
|
||||
-4:42:45 - SMT 1918 S 10
|
||||
-4 - -04 1919 Jul
|
||||
-4:42:46 - SMT 1927 S
|
||||
-4:42:45 - SMT 1927 S
|
||||
-5 x -05/-04 1932 S
|
||||
-4 - -04 1942 Jun
|
||||
-5 - -05 1942 Au
|
||||
|
@ -4101,11 +4105,11 @@ Z America/Santiago -4:42:46 - LMT 1890
|
|||
-5 - -05 1947 May 21 23
|
||||
-4 x -04/-03
|
||||
Z America/Punta_Arenas -4:43:40 - LMT 1890
|
||||
-4:42:46 - SMT 1910 Ja 10
|
||||
-4:42:45 - SMT 1910 Ja 10
|
||||
-5 - -05 1916 Jul
|
||||
-4:42:46 - SMT 1918 S 10
|
||||
-4:42:45 - SMT 1918 S 10
|
||||
-4 - -04 1919 Jul
|
||||
-4:42:46 - SMT 1927 S
|
||||
-4:42:45 - SMT 1927 S
|
||||
-5 x -05/-04 1932 S
|
||||
-4 - -04 1942 Jun
|
||||
-5 - -05 1942 Au
|
||||
|
|
34
lib/pytz_deprecation_shim/__init__.py
Normal file
34
lib/pytz_deprecation_shim/__init__.py
Normal file
|
@ -0,0 +1,34 @@
|
|||
__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
|
13
lib/pytz_deprecation_shim/_common.py
Normal file
13
lib/pytz_deprecation_shim/_common.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
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
|
15
lib/pytz_deprecation_shim/_compat.py
Normal file
15
lib/pytz_deprecation_shim/_compat.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
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
|
43
lib/pytz_deprecation_shim/_compat_py2.py
Normal file
43
lib/pytz_deprecation_shim/_compat_py2.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
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)
|
58
lib/pytz_deprecation_shim/_compat_py3.py
Normal file
58
lib/pytz_deprecation_shim/_compat_py3.py
Normal file
|
@ -0,0 +1,58 @@
|
|||
# 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
|
75
lib/pytz_deprecation_shim/_exceptions.py
Normal file
75
lib/pytz_deprecation_shim/_exceptions.py
Normal file
|
@ -0,0 +1,75 @@
|
|||
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)
|
296
lib/pytz_deprecation_shim/_impl.py
Normal file
296
lib/pytz_deprecation_shim/_impl.py
Normal file
|
@ -0,0 +1,296 @@
|
|||
# -*- 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"
|
||||
)
|
90
lib/pytz_deprecation_shim/helpers.py
Normal file
90
lib/pytz_deprecation_shim/helpers.py
Normal file
|
@ -0,0 +1,90 @@
|
|||
"""
|
||||
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__ = "2022.1"
|
||||
|
||||
# This exposes the original IANA version number.
|
||||
IANA_VERSION = "2021e"
|
||||
IANA_VERSION = "2022a"
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -72,11 +72,11 @@ Leap 2016 Dec 31 23:59:60 + S
|
|||
# Any additional leap seconds will come after this.
|
||||
# This Expires line is commented out for now,
|
||||
# so that pre-2020a zic implementations do not reject this file.
|
||||
#Expires 2022 Jun 28 00:00:00
|
||||
#Expires 2022 Dec 28 00:00:00
|
||||
|
||||
# POSIX timestamps for the data in this file:
|
||||
#updated 1467936000 (2016-07-08 00:00:00 UTC)
|
||||
#expires 1656374400 (2022-06-28 00:00:00 UTC)
|
||||
#expires 1672185600 (2022-12-28 00:00:00 UTC)
|
||||
|
||||
# Updated through IERS Bulletin C62
|
||||
# File expires on: 28 June 2022
|
||||
# Updated through IERS Bulletin C63
|
||||
# File expires on: 28 December 2022
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# version 2021e
|
||||
# version 2022a
|
||||
# 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 -
|
||||
|
@ -1111,9 +1111,10 @@ R P 2016 2018 - Mar Sa>=24 1 1 S
|
|||
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 2021 - Mar Sa>=24 0 1 S
|
||||
R P 2020 o - O 24 1 0 -
|
||||
R P 2021 ma - O lastF 1 0 -
|
||||
R P 2021 ma - O F>=23 1 0 -
|
||||
R P 2022 ma - Mar Su>=25 0 1 S
|
||||
Z Asia/Gaza 2:17:52 - LMT 1900 O
|
||||
2 Z EET/EEST 1948 May 15
|
||||
2 K EE%sT 1967 Jun 5
|
||||
|
@ -2431,8 +2432,8 @@ Z Europe/Simferopol 2:16:24 - LMT 1880
|
|||
1 c CE%sT 1944 Ap 13
|
||||
3 R MSK/MSD 1990
|
||||
3 - MSK 1990 Jul 1 2
|
||||
2 - EET 1992
|
||||
2 e EE%sT 1994 May
|
||||
2 - EET 1992 Mar 20
|
||||
2 c EE%sT 1994 May
|
||||
3 e MSK/MSD 1996 Mar 31 0s
|
||||
3 1 MSD 1996 O 27 3s
|
||||
3 R MSK/MSD 1997
|
||||
|
@ -2787,7 +2788,7 @@ Z Europe/Kiev 2:2:4 - LMT 1880
|
|||
1 c CE%sT 1943 N 6
|
||||
3 R MSK/MSD 1990 Jul 1 2
|
||||
2 1 EEST 1991 S 29 3
|
||||
2 e EE%sT 1995
|
||||
2 c EE%sT 1996 May 13
|
||||
2 E EE%sT
|
||||
Z Europe/Uzhgorod 1:29:12 - LMT 1890 O
|
||||
1 - CET 1940
|
||||
|
@ -2797,8 +2798,8 @@ Z Europe/Uzhgorod 1:29:12 - LMT 1890 O
|
|||
3 R MSK/MSD 1990
|
||||
3 - MSK 1990 Jul 1 2
|
||||
1 - CET 1991 Mar 31 3
|
||||
2 - EET 1992
|
||||
2 e EE%sT 1995
|
||||
2 - EET 1992 Mar 20
|
||||
2 c EE%sT 1996 May 13
|
||||
2 E EE%sT
|
||||
Z Europe/Zaporozhye 2:20:40 - LMT 1880
|
||||
2:20 - +0220 1924 May 2
|
||||
|
@ -2806,7 +2807,8 @@ Z Europe/Zaporozhye 2:20:40 - LMT 1880
|
|||
3 - MSK 1941 Au 25
|
||||
1 c CE%sT 1943 O 25
|
||||
3 R MSK/MSD 1991 Mar 31 2
|
||||
2 e EE%sT 1995
|
||||
2 e EE%sT 1992 Mar 20
|
||||
2 c EE%sT 1996 May 13
|
||||
2 E EE%sT
|
||||
R u 1918 1919 - Mar lastSu 2 1 D
|
||||
R u 1918 1919 - O lastSu 2 0 S
|
||||
|
@ -4088,12 +4090,12 @@ R x 2016 2018 - May Su>=9 3u 0 -
|
|||
R x 2016 2018 - Au Su>=9 4u 1 -
|
||||
R x 2019 ma - Ap Su>=2 3u 0 -
|
||||
R x 2019 ma - S Su>=2 4u 1 -
|
||||
Z America/Santiago -4:42:46 - LMT 1890
|
||||
-4:42:46 - SMT 1910 Ja 10
|
||||
Z America/Santiago -4:42:45 - LMT 1890
|
||||
-4:42:45 - SMT 1910 Ja 10
|
||||
-5 - -05 1916 Jul
|
||||
-4:42:46 - SMT 1918 S 10
|
||||
-4:42:45 - SMT 1918 S 10
|
||||
-4 - -04 1919 Jul
|
||||
-4:42:46 - SMT 1927 S
|
||||
-4:42:45 - SMT 1927 S
|
||||
-5 x -05/-04 1932 S
|
||||
-4 - -04 1942 Jun
|
||||
-5 - -05 1942 Au
|
||||
|
@ -4103,11 +4105,11 @@ Z America/Santiago -4:42:46 - LMT 1890
|
|||
-5 - -05 1947 May 21 23
|
||||
-4 x -04/-03
|
||||
Z America/Punta_Arenas -4:43:40 - LMT 1890
|
||||
-4:42:46 - SMT 1910 Ja 10
|
||||
-4:42:45 - SMT 1910 Ja 10
|
||||
-5 - -05 1916 Jul
|
||||
-4:42:46 - SMT 1918 S 10
|
||||
-4:42:45 - SMT 1918 S 10
|
||||
-4 - -04 1919 Jul
|
||||
-4:42:46 - SMT 1927 S
|
||||
-4:42:45 - SMT 1927 S
|
||||
-5 x -05/-04 1932 S
|
||||
-4 - -04 1942 Jun
|
||||
-5 - -05 1942 Au
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
import sys
|
||||
if sys.platform == 'win32':
|
||||
from tzlocal.win32 import get_localzone, reload_localzone
|
||||
|
||||
if sys.platform == "win32":
|
||||
from tzlocal.win32 import (
|
||||
get_localzone,
|
||||
get_localzone_name,
|
||||
reload_localzone,
|
||||
) # pragma: no cover
|
||||
else:
|
||||
from tzlocal.unix import get_localzone, reload_localzone
|
||||
from tzlocal.unix import get_localzone, get_localzone_name, reload_localzone
|
||||
|
||||
|
||||
__all__ = ["get_localzone", "get_localzone_name", "reload_localzone"]
|
||||
|
|
|
@ -1,97 +1,75 @@
|
|||
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 _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='/'):
|
||||
def _get_localzone_name(_root="/"):
|
||||
"""Tries to find the local timezone configuration.
|
||||
|
||||
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.
|
||||
This method finds the timezone name, if it can, or it returns None.
|
||||
|
||||
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."""
|
||||
|
||||
tzenv = _try_tz_from_env()
|
||||
# First try the ENV setting.
|
||||
tzenv = utils._tz_name_from_env()
|
||||
if tzenv:
|
||||
return tzenv
|
||||
|
||||
# Are we under Termux on Android?
|
||||
if os.path.exists('/system/bin/getprop'):
|
||||
if os.path.exists(os.path.join(_root, "system/bin/getprop")):
|
||||
import subprocess
|
||||
androidtz = subprocess.check_output(['getprop', 'persist.sys.timezone']).strip().decode()
|
||||
return pytz.timezone(androidtz)
|
||||
|
||||
androidtz = (
|
||||
subprocess.check_output(["getprop", "persist.sys.timezone"])
|
||||
.strip()
|
||||
.decode()
|
||||
)
|
||||
return androidtz
|
||||
|
||||
# Now look for distribution specific configuration files
|
||||
# that contain the timezone name.
|
||||
for configfile in ('etc/timezone', 'var/db/zoneinfo'):
|
||||
|
||||
# Stick all of them in a dict, to compare later.
|
||||
found_configs = {}
|
||||
|
||||
for configfile in ("etc/timezone", "var/db/zoneinfo"):
|
||||
tzpath = os.path.join(_root, configfile)
|
||||
try:
|
||||
with open(tzpath, 'rb') as tzfile:
|
||||
with open(tzpath, "rt") as tzfile:
|
||||
data = tzfile.read()
|
||||
|
||||
# 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()
|
||||
etctz = data.strip('/ \t\r\n')
|
||||
if not etctz:
|
||||
# Empty file, skip
|
||||
continue
|
||||
for etctz in data.decode().splitlines():
|
||||
for etctz in etctz.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
|
||||
|
||||
except IOError:
|
||||
# File doesn't exist or is a directory
|
||||
found_configs[tzpath] = etctz.replace(" ", "_")
|
||||
|
||||
except (IOError, UnicodeDecodeError):
|
||||
# File doesn't exist or is a directory, or it's a binary file.
|
||||
continue
|
||||
|
||||
# CentOS has a ZONE setting in /etc/sysconfig/clock,
|
||||
|
@ -99,14 +77,14 @@ def _get_localzone(_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:
|
||||
|
@ -118,48 +96,108 @@ def _get_localzone(_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
|
||||
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:
|
||||
# File doesn't exist or is a directory
|
||||
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')
|
||||
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
|
||||
etctz = realtzpath = os.path.realpath(tzpath)
|
||||
start = etctz.find("/") + 1
|
||||
while start != 0:
|
||||
tzpath = tzpath[start:]
|
||||
etctz = etctz[start:]
|
||||
try:
|
||||
return pytz.timezone(tzpath)
|
||||
except pytz.UnknownTimeZoneError:
|
||||
pds.timezone(etctz)
|
||||
tzinfo = f"{tzpath} is a symlink to"
|
||||
found_configs[tzinfo] = etctz.replace(" ", "_")
|
||||
except pds.UnknownTimeZoneError:
|
||||
pass
|
||||
start = tzpath.find("/")+1
|
||||
start = etctz.find("/") + 1
|
||||
|
||||
# No explicit setting existed. Use localtime
|
||||
for filename in ('etc/localtime', 'usr/local/etc/localtime'):
|
||||
tzpath = os.path.join(_root, filename)
|
||||
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))
|
||||
|
||||
if not os.path.exists(tzpath):
|
||||
continue
|
||||
with open(tzpath, 'rb') as tzfile:
|
||||
return pytz.tzfile.build_tzinfo('local', tzfile)
|
||||
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 == "/":
|
||||
# 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
|
||||
|
||||
|
||||
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()
|
||||
|
||||
return _cache_tz_name
|
||||
|
||||
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()
|
||||
|
@ -169,6 +207,9 @@ 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,7 +1,24 @@
|
|||
# -*- 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():
|
||||
|
@ -21,9 +38,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
|
||||
return -time.altzone # pragma: no cover
|
||||
else:
|
||||
return -time.timezone
|
||||
return -time.timezone # pragma: no cover
|
||||
|
||||
|
||||
def get_tz_offset(tz):
|
||||
|
@ -39,8 +56,73 @@ 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: {0} != {1}. '
|
||||
'Please, check your config files.').format(
|
||||
tz_offset, system_offset
|
||||
)
|
||||
msg = (
|
||||
"Timezone offset does not match system offset: {} != {}. "
|
||||
"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[0] == ":":
|
||||
tzenv = tzenv[1:]
|
||||
|
||||
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, expand it, if possible
|
||||
parts = os.path.realpath(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,32 +1,53 @@
|
|||
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."""
|
||||
dict = {}
|
||||
result = {}
|
||||
size = winreg.QueryInfoKey(key)[1]
|
||||
for i in range(size):
|
||||
data = winreg.EnumValue(key, i)
|
||||
dict[data[0]] = data[1]
|
||||
return dict
|
||||
result[data[0]] = data[1]
|
||||
return result
|
||||
|
||||
|
||||
def get_localzone_name():
|
||||
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():
|
||||
# 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"
|
||||
|
@ -34,44 +55,16 @@ def get_localzone_name():
|
|||
keyvalues = valuestodict(localtz)
|
||||
localtz.Close()
|
||||
|
||||
if 'TimeZoneKeyName' in keyvalues:
|
||||
# Windows 7 (and Vista?)
|
||||
if "TimeZoneKeyName" in keyvalues:
|
||||
# Windows 7 and later
|
||||
|
||||
# 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:
|
||||
# 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')
|
||||
# Don't support XP any longer
|
||||
raise LookupError("Can not find Windows timezone configuration")
|
||||
|
||||
timezone = win_tz.get(tzkeyname)
|
||||
if timezone is None:
|
||||
|
@ -81,24 +74,64 @@ def get_localzone_name():
|
|||
|
||||
# Return what we have.
|
||||
if timezone is None:
|
||||
raise pytz.UnknownTimeZoneError('Can not find timezone ' + tzkeyname)
|
||||
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}"
|
||||
|
||||
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 = pytz.timezone(get_localzone_name())
|
||||
_cache_tz = pds.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)
|
||||
|
||||
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
|
||||
_cache_tz = pytz.timezone(get_localzone_name())
|
||||
global _cache_tz_name
|
||||
_cache_tz_name = _get_localzone_name()
|
||||
_cache_tz = pds.timezone(_cache_tz_name)
|
||||
utils.assert_tz_offset(_cache_tz)
|
||||
return _cache_tz
|
||||
|
|
|
@ -104,6 +104,7 @@ 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',
|
||||
|
@ -118,7 +119,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/GMT',
|
||||
'UTC': 'Etc/UTC',
|
||||
'UTC+12': 'Etc/GMT-12',
|
||||
'UTC+13': 'Etc/GMT-13',
|
||||
'UTC-02': 'Etc/GMT+2',
|
||||
|
@ -136,7 +137,8 @@ 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'}
|
||||
'Yakutsk Standard Time': 'Asia/Yakutsk',
|
||||
'Yukon Standard Time': 'America/Whitehorse'}
|
||||
|
||||
# Old name for the win_tz variable:
|
||||
tz_names = win_tz
|
||||
|
@ -166,7 +168,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': 'E. Africa Standard Time',
|
||||
'Africa/Juba': 'South Sudan Standard Time',
|
||||
'Africa/Kampala': 'E. Africa Standard Time',
|
||||
'Africa/Khartoum': 'Sudan Standard Time',
|
||||
'Africa/Kigali': 'South Africa Standard Time',
|
||||
|
@ -234,8 +236,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': 'UTC',
|
||||
'America/Dawson': 'Pacific Standard Time',
|
||||
'America/Danmarkshavn': 'Greenwich Standard Time',
|
||||
'America/Dawson': 'Yukon Standard Time',
|
||||
'America/Dawson_Creek': 'US Mountain Standard Time',
|
||||
'America/Denver': 'Mountain Standard Time',
|
||||
'America/Detroit': 'Eastern Standard Time',
|
||||
|
@ -345,14 +347,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': 'Pacific Standard Time',
|
||||
'America/Whitehorse': 'Yukon Standard Time',
|
||||
'America/Winnipeg': 'Central Standard Time',
|
||||
'America/Yakutat': 'Alaskan Standard Time',
|
||||
'America/Yellowknife': 'Mountain Standard Time',
|
||||
'Antarctica/Casey': 'Singapore Standard Time',
|
||||
'Antarctica/Casey': 'Central Pacific Standard Time',
|
||||
'Antarctica/Davis': 'SE Asia Standard Time',
|
||||
'Antarctica/DumontDUrville': 'West Pacific Standard Time',
|
||||
'Antarctica/Macquarie': 'Central Pacific Standard Time',
|
||||
'Antarctica/Macquarie': 'Tasmania Standard Time',
|
||||
'Antarctica/Mawson': 'West Asia Standard Time',
|
||||
'Antarctica/McMurdo': 'New Zealand Standard Time',
|
||||
'Antarctica/Palmer': 'SA Eastern Standard Time',
|
||||
|
@ -501,7 +503,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': 'Pacific Standard Time',
|
||||
'Canada/Yukon': 'Yukon Standard Time',
|
||||
'Chile/Continental': 'Pacific SA Standard Time',
|
||||
'Chile/EasterIsland': 'Easter Island Standard Time',
|
||||
'Cuba': 'Cuba Standard Time',
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
apscheduler==3.8.0
|
||||
apscheduler==3.9.1
|
||||
importlib-resources==5.6.0
|
||||
pyinstaller==4.9
|
||||
pyopenssl==22.0.0
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
appdirs==1.4.4
|
||||
apscheduler==3.8.0
|
||||
apscheduler==3.9.1
|
||||
arrow==1.2.2
|
||||
backports.csv==1.0.7
|
||||
backports.functools-lru-cache==1.6.4
|
||||
|
@ -33,7 +33,7 @@ PyJWT==2.3.0
|
|||
pyparsing==3.0.7
|
||||
python-dateutil==2.8.2
|
||||
python-twitter==3.5
|
||||
pytz==2021.3
|
||||
pytz==2022.1
|
||||
requests==2.27.1
|
||||
requests-oauthlib==1.3.1
|
||||
rumps==0.3.0; platform_system == "Darwin"
|
||||
|
@ -42,8 +42,8 @@ six==1.16.0
|
|||
soupsieve==2.3.1
|
||||
tempora==5.0.1
|
||||
tokenize-rt==4.2.1
|
||||
tzdata==2021.5
|
||||
tzlocal==2.1 # apscheduler==3.8.0 requires tzlocal~=2.0
|
||||
tzdata==2022.1
|
||||
tzlocal==4.2
|
||||
urllib3==1.26.8
|
||||
webencodings==0.5.1
|
||||
websocket-client==1.2.3
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue