From 54c9214b039ae929e00d8b47287e92e6f7c065db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 May 2022 20:32:37 -0700 Subject: [PATCH] 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] * 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] --- lib/apscheduler/jobstores/mongodb.py | 4 +- lib/apscheduler/schedulers/base.py | 5 +- lib/apscheduler/schedulers/qt.py | 11 +- lib/apscheduler/triggers/cron/__init__.py | 7 +- lib/apscheduler/triggers/interval.py | 6 +- lib/apscheduler/util.py | 23 +- lib/pytz/__init__.py | 4 +- lib/pytz/zoneinfo/America/Punta_Arenas | Bin 1902 -> 1902 bytes lib/pytz/zoneinfo/America/Santiago | Bin 2529 -> 2529 bytes lib/pytz/zoneinfo/Asia/Gaza | Bin 2422 -> 2422 bytes lib/pytz/zoneinfo/Asia/Hebron | Bin 2450 -> 2450 bytes lib/pytz/zoneinfo/Chile/Continental | Bin 2529 -> 2529 bytes lib/pytz/zoneinfo/Europe/Kiev | Bin 2088 -> 2120 bytes lib/pytz/zoneinfo/Europe/Simferopol | Bin 1453 -> 1469 bytes lib/pytz/zoneinfo/Europe/Uzhgorod | Bin 2050 -> 2066 bytes lib/pytz/zoneinfo/Europe/Zaporozhye | Bin 2106 -> 2138 bytes lib/pytz/zoneinfo/Pacific/Fiji | Bin 1077 -> 1049 bytes lib/pytz/zoneinfo/leapseconds | 8 +- lib/pytz/zoneinfo/tzdata.zi | 38 +-- lib/pytz_deprecation_shim/__init__.py | 34 +++ lib/pytz_deprecation_shim/_common.py | 13 + lib/pytz_deprecation_shim/_compat.py | 15 ++ lib/pytz_deprecation_shim/_compat_py2.py | 43 ++++ lib/pytz_deprecation_shim/_compat_py3.py | 58 +++++ lib/pytz_deprecation_shim/_exceptions.py | 75 ++++++ lib/pytz_deprecation_shim/_impl.py | 296 ++++++++++++++++++++++ lib/pytz_deprecation_shim/helpers.py | 90 +++++++ lib/tzdata/__init__.py | 4 +- lib/tzdata/zoneinfo/America/Punta_Arenas | Bin 1209 -> 1209 bytes lib/tzdata/zoneinfo/America/Santiago | Bin 1282 -> 1282 bytes lib/tzdata/zoneinfo/Asia/Gaza | Bin 1230 -> 1240 bytes lib/tzdata/zoneinfo/Asia/Hebron | Bin 1248 -> 1258 bytes lib/tzdata/zoneinfo/Chile/Continental | Bin 1282 -> 1282 bytes lib/tzdata/zoneinfo/Europe/Kiev | Bin 549 -> 558 bytes lib/tzdata/zoneinfo/Europe/Simferopol | Bin 865 -> 865 bytes lib/tzdata/zoneinfo/Europe/Uzhgorod | Bin 530 -> 539 bytes lib/tzdata/zoneinfo/Europe/Zaporozhye | Bin 560 -> 569 bytes lib/tzdata/zoneinfo/leapseconds | 8 +- lib/tzdata/zoneinfo/tzdata.zi | 34 +-- lib/tzlocal/__init__.py | 14 +- lib/tzlocal/unix.py | 219 +++++++++------- lib/tzlocal/utils.py | 94 ++++++- lib/tzlocal/win32.py | 119 +++++---- lib/tzlocal/windows_tz.py | 20 +- package/requirements-package.txt | 2 +- requirements.txt | 8 +- 46 files changed, 1029 insertions(+), 223 deletions(-) create mode 100644 lib/pytz_deprecation_shim/__init__.py create mode 100644 lib/pytz_deprecation_shim/_common.py create mode 100644 lib/pytz_deprecation_shim/_compat.py create mode 100644 lib/pytz_deprecation_shim/_compat_py2.py create mode 100644 lib/pytz_deprecation_shim/_compat_py3.py create mode 100644 lib/pytz_deprecation_shim/_exceptions.py create mode 100644 lib/pytz_deprecation_shim/_impl.py create mode 100644 lib/pytz_deprecation_shim/helpers.py diff --git a/lib/apscheduler/jobstores/mongodb.py b/lib/apscheduler/jobstores/mongodb.py index ea3097dd..5a00f941 100644 --- a/lib/apscheduler/jobstores/mongodb.py +++ b/lib/apscheduler/jobstores/mongodb.py @@ -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 diff --git a/lib/apscheduler/schedulers/base.py b/lib/apscheduler/schedulers/base.py index 3dfb7437..444de8ef 100644 --- a/lib/apscheduler/schedulers/base.py +++ b/lib/apscheduler/schedulers/base.py @@ -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() diff --git a/lib/apscheduler/schedulers/qt.py b/lib/apscheduler/schedulers/qt.py index dda77d79..600f6e67 100644 --- a/lib/apscheduler/schedulers/qt.py +++ b/lib/apscheduler/schedulers/qt.py @@ -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): diff --git a/lib/apscheduler/triggers/cron/__init__.py b/lib/apscheduler/triggers/cron/__init__.py index fec6e3b5..b5389dd2 100644 --- a/lib/apscheduler/triggers/cron/__init__.py +++ b/lib/apscheduler/triggers/cron/__init__.py @@ -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: diff --git a/lib/apscheduler/triggers/interval.py b/lib/apscheduler/triggers/interval.py index 61094aa1..b0e2dbdd 100644 --- a/lib/apscheduler/triggers/interval.py +++ b/lib/apscheduler/triggers/interval.py @@ -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 { diff --git a/lib/apscheduler/util.py b/lib/apscheduler/util.py index 1e643bff..d929a482 100644 --- a/lib/apscheduler/util.py +++ b/lib/apscheduler/util.py @@ -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)) diff --git a/lib/pytz/__init__.py b/lib/pytz/__init__.py index 6ef4366b..900e8caa 100644 --- a/lib/pytz/__init__.py +++ b/lib/pytz/__init__.py @@ -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 diff --git a/lib/pytz/zoneinfo/America/Punta_Arenas b/lib/pytz/zoneinfo/America/Punta_Arenas index a5a8af52c2f26baf6f85a1786f69593491ad5195..13bd1d9a7755df59c009b4c4e5fbe44257e7ab51 100644 GIT binary patch delta 88 zcmaFI_l|FZv7qbhn5O~rYaSh4$WVA-LtOdMjV@M9jJqe-GqozZ{s#h(JctIV0ns3J ZAR450vnum9X0T!}4lWyA17ka5E&yJ$E=d3Y delta 88 zcmaFI_l|FZv7p=Rn5O~rYaSh2$WVA-LtOc>jV@M9jJqb+GqozZ{RaY&JctIV0ns3J ZAR450vnum9X0T!}4lWyA17ka5E&yM&E=>Rc diff --git a/lib/pytz/zoneinfo/America/Santiago b/lib/pytz/zoneinfo/America/Santiago index 816a0428188d99f437004312ee73c3860ee0f54f..aa2906063f3bb2be42e800c208c6a4453a610031 100644 GIT binary patch delta 99 zcmaDT{7`s;v7qbhn5O~rYaSh4$WVA-LtOdMjV|2G?7JBl{{P>*dvX|at>)4HK+tdC f?h2woszEeJ2Z#pg0?{CyoBuEuv%jV|2G?7J8k{{P>*YjPNKt>&@+K+tdC f?gpYkszEeJ2Z#pg0?{CyoBuEuv%I^7F@!jYzv~j#8ix+rNO0!h}K*VS>4{WcTsP3hlm2(T5^^^aFHDP z16+a@mxh{7TSG(;In+?U=bq)WexK6`cfyT8m*iIrpEPX;7Rk5 zS;>FiYauiyg?2@Y?;$BQTy-BmuZvcY$P4YM_z!u e0eOX?$KoB$wA~lzpGrjLDuJAsR_Z8kR8|u`xZq=OLoTaguEX0P`grt*= zL~LS24BpKoCTD3RzUO?)?;HQm>&N`qZMeZJXLWIWRF>xFG~L}L%VWdpG$%FF*{+$d z4qd7Hy885|YaegAe)XvvkH4~c_@LR#SJ`qdbh~;dJG1w)n?94h#JS`qE3)5KmHfb| z=6@V$vGqyVWvO3)*R4@pQCl7{3ViAZLkNkwu4O)`>=q$Byr36L`& cr!X{yI7clNiH2M4k^Y&X!MGi_6LzxYA58?5rT_o{ diff --git a/lib/pytz/zoneinfo/Asia/Hebron b/lib/pytz/zoneinfo/Asia/Hebron index 1a206a70c7da285a55cf3790e116e633fbd0a14a..0078bf01c3db4a013a599ca995fbb72b81460a47 100644 GIT binary patch delta 430 zcmW;Ize@sf7{~GBFvyUoZVRHl#8ix+rJ<#Uh}Ncutgc^LS&4et4G{(2q)W~c1T9iS ze}GHS;?mOev_wNh5G_H}_kGXuT0ifn?$n)j)b_m*S(ynmyZk4sqj_DMev$Rwnz+Lq zao=*X@pC1c4~w$(d@kG9DcR|?G= zEDSX(k)_C5WHGWDS&pnn3Xlr)NikaFAW29Tl7{3ViJ>MFNewl*NHUU*q$Byr3y?P$ ddMsYi2<+cRAv)AQm5wi@l8H4oYs7NJ9)aE0xHA0J=K||U_#3K9wBu}VQpGDCe+B!>Ps-<0sO~oc8 zCL4*^#7JWBOiW^8ktW{jyvy$$@9U|AE8**qQNTRRFL{fcCE|QF7Bk4#!asuQG dhQ*dvX|at>)4HK+tdC f?h2woszEeJ2Z#pg0?{CyoBuEuv%jV|2G?7J8k{{P>*YjPNKt>&@+K+tdC f?gpYkszEeJ2Z#pg0?{CyoBuEuv%a6({$xFA0R0|N+yKqV0KPt>SXzRJ;{b>wG*_Lf%-I!kUh=uSD)px3guLBC{O zgF(vt2E*9X3>%MRFvfE8z#uOl)#2m@b{8=j1qKcV1}y_dSq26Kd-6GUeHKPW28PLO z%qo~(m~74b(HZCmke@*Q0MQ`7fM}3^Ks3ltAR6Q^5DoGhhz9u&M1%aeIiF<<(;)EV HUv_N(G=W0U delta 396 zcmX>hutH#hxF9bB0|N+yKqV0KPSmJWzVaqP>&W>4?JegLbe3!k(4Dd&L9by_b7}xS#+70|N+yfHx2eOw=e9zRJ;{b>wG*_Lf%-I!kUh=uSD)Fmc0CU2ZOJ zE_P;iW=;+sUP6#>GB>L$i;MyT$7DZN7j{M&W>4?JegLbe3!k(4Dd&Vd93Px=dV5 zTfdPa;paO__CTf%_UwMw`2_Lg%AI!iVN=uX*?pw}`vK)+YOD7-SR}L?`cIS7&BmWSsnuNg310 zlZ}{PIs@Gd@&m}-AR6R$5Djuahz9uqM1%YRqCtKE(IEeTXpo;a$FXc>8URndVAlcw DC`vv8 diff --git a/lib/pytz/zoneinfo/Europe/Zaporozhye b/lib/pytz/zoneinfo/Europe/Zaporozhye index e42edfc8506b9b99362b36d90c8b8c4db67d50d8..f0406c12a6b519347d6a7c091424da9d0f241236 100644 GIT binary patch delta 408 zcmdlba7$oSYzRJ;{b>wG*_Lf%-I!kUh=uSD)px3guLBC{O zgF(vt2E*9X3>!~mF~)H7Kp`(5)#2m`?0!593^ED~Tnr3a28{BP@39-QFfuYQOy*@) z!F2azC+45dK!<}o0&+Zv26+HPgFFGEK^_6oAkTnkkcU7t$WtI1 F+5qwkK=c3r delta 372 zcmca5uuEWqxF9bB0|N+yKot=4PSmJXzVaqP>&W>4?JegLbe3!k(4Dd&L9bR delta 57 zcmbQqv6W+jGNZ#pl^XWM{v!_(A6=d}|Ip;?%!-p67=hxGmoR?e21zgg0Z?-DWF|L8 HptKVJt~D1- diff --git a/lib/pytz/zoneinfo/leapseconds b/lib/pytz/zoneinfo/leapseconds index 834b96ea..ffa5eb80 100644 --- a/lib/pytz/zoneinfo/leapseconds +++ b/lib/pytz/zoneinfo/leapseconds @@ -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 diff --git a/lib/pytz/zoneinfo/tzdata.zi b/lib/pytz/zoneinfo/tzdata.zi index e16ab09f..e21fc920 100644 --- a/lib/pytz/zoneinfo/tzdata.zi +++ b/lib/pytz/zoneinfo/tzdata.zi @@ -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 diff --git a/lib/pytz_deprecation_shim/__init__.py b/lib/pytz_deprecation_shim/__init__.py new file mode 100644 index 00000000..8b451620 --- /dev/null +++ b/lib/pytz_deprecation_shim/__init__.py @@ -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 diff --git a/lib/pytz_deprecation_shim/_common.py b/lib/pytz_deprecation_shim/_common.py new file mode 100644 index 00000000..ace322e9 --- /dev/null +++ b/lib/pytz_deprecation_shim/_common.py @@ -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 diff --git a/lib/pytz_deprecation_shim/_compat.py b/lib/pytz_deprecation_shim/_compat.py new file mode 100644 index 00000000..5b734592 --- /dev/null +++ b/lib/pytz_deprecation_shim/_compat.py @@ -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 diff --git a/lib/pytz_deprecation_shim/_compat_py2.py b/lib/pytz_deprecation_shim/_compat_py2.py new file mode 100644 index 00000000..f473d267 --- /dev/null +++ b/lib/pytz_deprecation_shim/_compat_py2.py @@ -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) diff --git a/lib/pytz_deprecation_shim/_compat_py3.py b/lib/pytz_deprecation_shim/_compat_py3.py new file mode 100644 index 00000000..8881abac --- /dev/null +++ b/lib/pytz_deprecation_shim/_compat_py3.py @@ -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 diff --git a/lib/pytz_deprecation_shim/_exceptions.py b/lib/pytz_deprecation_shim/_exceptions.py new file mode 100644 index 00000000..58d7af0a --- /dev/null +++ b/lib/pytz_deprecation_shim/_exceptions.py @@ -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) diff --git a/lib/pytz_deprecation_shim/_impl.py b/lib/pytz_deprecation_shim/_impl.py new file mode 100644 index 00000000..54430479 --- /dev/null +++ b/lib/pytz_deprecation_shim/_impl.py @@ -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" +) diff --git a/lib/pytz_deprecation_shim/helpers.py b/lib/pytz_deprecation_shim/helpers.py new file mode 100644 index 00000000..6b05b130 --- /dev/null +++ b/lib/pytz_deprecation_shim/helpers.py @@ -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 diff --git a/lib/tzdata/__init__.py b/lib/tzdata/__init__.py index 07c7b3a2..7e6440bb 100644 --- a/lib/tzdata/__init__.py +++ b/lib/tzdata/__init__.py @@ -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" diff --git a/lib/tzdata/zoneinfo/America/Punta_Arenas b/lib/tzdata/zoneinfo/America/Punta_Arenas index 5c9a20b947f3763da250afdf005bc7f0136ad537..c04210406f7bd23172481db68ae2f7f9dcd8e9d2 100644 GIT binary patch delta 50 ycmdnVxs!83o}%l2AebHVGyp`;uX%J7L@#70JOH9M#FZc2*#DN9arfj}78?LbULYR; delta 50 ycmdnVxs!83o}$}-AebHVGyp`;uX%I~L@#70JOH9M#FZc0*#DN9ao6Np78?Lbsvsc% diff --git a/lib/tzdata/zoneinfo/America/Santiago b/lib/tzdata/zoneinfo/America/Santiago index 8d6032264b656186dc23df35402b1be0079fd670..cde8dbbf049e1bbeabf3bc757828aa1884e673c6 100644 GIT binary patch delta 64 zcmZqTYT}xZrg`)~5cC_kyMpN1F;4?P^!%DfVDW_vg$F?V4RPg1H}=c2u$8MTZe?-hHL=j~H8jvO)ic#Mo3_y@rEt@b|kws#10Snh;OO{YZrpbLQu6*W3I=+SmdM0`% I`bMT)0IN?8kN^Mx delta 38 ucmaFG`G9jm7-RXy@B_?~6#M aKA1L)JQ`$ delta 86 zcmZ3-vXo^)7^B+6@UKo+-Xt&pf!2}p0U%m?%ee#)t+Qk!n6Eo!1DLPZG8xR*FWEBz WByW(i7EBw)-kdy*QGN10#sUCG#TYXH diff --git a/lib/tzdata/zoneinfo/Europe/Simferopol b/lib/tzdata/zoneinfo/Europe/Simferopol index 88a6f3bdb4691ace47a6d670899a9d049ef5a123..40d23c029a647297b4901286652d2db5407d32dd 100644 GIT binary patch delta 48 tcmaFJ_K1*UbD+y>LSQ_eJOPG|hZ2mo;X4?+L{ delta 48 vcmaFJ_Knzy_=Ic(`kgz$O@fRZics&l; diff --git a/lib/tzdata/zoneinfo/Europe/Uzhgorod b/lib/tzdata/zoneinfo/Europe/Uzhgorod index a5755685e390bd5ad54402ee61b1319e9a66296e..d4c35914191fa2dd7a3e07105af0d4494681badb 100644 GIT binary patch delta 96 zcmbQlGMi;W7^B+6@V8!9IT{#%K#M aKA1L)J#M aKA1L)Ju?PT78yYeI delta 86 zcmdnVvVmnn7^C{c@V`!1-Xt&pf!2}p0U%m?%ee#)t+Qk!n6Eo!1DLPZG8xR*FWEBz WByW(i7EBw)-kdy-QDgFP#v%Yx92jN* diff --git a/lib/tzdata/zoneinfo/leapseconds b/lib/tzdata/zoneinfo/leapseconds index 834b96ea..ffa5eb80 100644 --- a/lib/tzdata/zoneinfo/leapseconds +++ b/lib/tzdata/zoneinfo/leapseconds @@ -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 diff --git a/lib/tzdata/zoneinfo/tzdata.zi b/lib/tzdata/zoneinfo/tzdata.zi index 1948c725..c38197ea 100644 --- a/lib/tzdata/zoneinfo/tzdata.zi +++ b/lib/tzdata/zoneinfo/tzdata.zi @@ -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 diff --git a/lib/tzlocal/__init__.py b/lib/tzlocal/__init__.py index c8196d66..98ed04fd 100644 --- a/lib/tzlocal/__init__.py +++ b/lib/tzlocal/__init__.py @@ -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"] diff --git a/lib/tzlocal/unix.py b/lib/tzlocal/unix.py index 8574965a..eaf96d92 100644 --- a/lib/tzlocal/unix.py +++ b/lib/tzlocal/unix.py @@ -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 diff --git a/lib/tzlocal/utils.py b/lib/tzlocal/utils.py index 5a677990..d1026b32 100644 --- a/lib/tzlocal/utils.py +++ b/lib/tzlocal/utils.py @@ -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 diff --git a/lib/tzlocal/win32.py b/lib/tzlocal/win32.py index fcc42a23..720ab2b7 100644 --- a/lib/tzlocal/win32.py +++ b/lib/tzlocal/win32.py @@ -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 diff --git a/lib/tzlocal/windows_tz.py b/lib/tzlocal/windows_tz.py index 86ba807d..0d285037 100644 --- a/lib/tzlocal/windows_tz.py +++ b/lib/tzlocal/windows_tz.py @@ -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', diff --git a/package/requirements-package.txt b/package/requirements-package.txt index c7d7245e..5acd462f 100644 --- a/package/requirements-package.txt +++ b/package/requirements-package.txt @@ -1,4 +1,4 @@ -apscheduler==3.8.0 +apscheduler==3.9.1 importlib-resources==5.6.0 pyinstaller==4.9 pyopenssl==22.0.0 diff --git a/requirements.txt b/requirements.txt index d433889a..12ec6726 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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