diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 067c772b..e566aee5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,14 +18,19 @@ jobs: - 3.8 - 3.9 tox-environment: - - django22 - django31 - django32 - include: - - python-version: 3.8 - tox-environment: djangomain - - python-version: 3.9 - tox-environment: djangomain + - django40 + # include: + # - python-version: 3.8 + # tox-environment: djangomain + # - python-version: 3.9 + # tox-environment: djangomain + exclude: + - python-version: 3.6 + tox-environment: django40 + - python-version: 3.7 + tox-environment: django40 env: TOXENV: ${{ matrix.tox-environment }} diff --git a/CHANGELOG b/CHANGELOG index dadceb05..2724d9c1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +0.9.6 - +========== + +- Fix bug #522. 'backports.zoneinfo.ZoneInfo' object has no attribute 'localize' + + 0.9.5 - 2021-05-29 ========== diff --git a/schedule/models/events.py b/schedule/models/events.py index f1a8c04a..010d5345 100644 --- a/schedule/models/events.py +++ b/schedule/models/events.py @@ -1,5 +1,6 @@ import datetime +import pytz from dateutil import rrule from django.conf import settings as django_settings from django.contrib.contenttypes import fields @@ -186,16 +187,14 @@ def get_rrule_object(self, tzinfo): if timezone.is_naive(self.start): dtstart = self.start else: - dtstart = tzinfo.normalize(self.start).replace(tzinfo=None) + dtstart = self.start.astimezone(tzinfo).replace(tzinfo=None) if self.end_recurring_period is None: until = None elif timezone.is_naive(self.end_recurring_period): until = self.end_recurring_period else: - until = tzinfo.normalize( - self.end_recurring_period.astimezone(tzinfo) - ).replace(tzinfo=None) + until = self.end_recurring_period.astimezone(tzinfo).replace(tzinfo=None) return rrule.rrule(frequency, dtstart=dtstart, until=until, **params) @@ -216,9 +215,9 @@ def get_occurrence(self, date): rule = self.get_rrule_object(tzinfo) if rule: next_occurrence = rule.after( - tzinfo.normalize(date).replace(tzinfo=None), inc=True + date.astimezone(tzinfo).replace(tzinfo=None), inc=True ) - next_occurrence = tzinfo.localize(next_occurrence) + next_occurrence = pytz.timezone(str(tzinfo)).localize(next_occurrence) else: next_occurrence = self.start if next_occurrence == date: @@ -251,7 +250,7 @@ def _get_occurrence_list(self, start, end): start_rule = self.get_rrule_object(tzinfo) start = start.replace(tzinfo=None) if timezone.is_aware(end): - end = tzinfo.normalize(end).replace(tzinfo=None) + end = end.astimezone(tzinfo).replace(tzinfo=None) o_starts = [] @@ -272,7 +271,7 @@ def _get_occurrence_list(self, start, end): # Create the Occurrence objects for the found start dates for o_start in o_starts: - o_start = tzinfo.localize(o_start) + o_start = pytz.timezone(str(tzinfo)).localize(o_start) if use_naive: o_start = timezone.make_naive(o_start, tzinfo) o_end = o_start + duration @@ -308,7 +307,7 @@ def _occurrences_after_generator(self, after=None): difference = self.end - self.start loop_counter = 0 for o_start in date_iter: - o_start = tzinfo.localize(o_start) + o_start = pytz.timezone(str(tzinfo)).localize(o_start) o_end = o_start + difference if o_end > after: yield self._create_occurrence(o_start, o_end) diff --git a/schedule/periods.py b/schedule/periods.py index 2b0668f4..9ca5d922 100644 --- a/schedule/periods.py +++ b/schedule/periods.py @@ -61,7 +61,7 @@ def _normalize_timezone_to_utc(self, point_in_time, tzinfo): if point_in_time.tzinfo is not None: return point_in_time.astimezone(pytz.utc) if tzinfo is not None: - return tzinfo.localize(point_in_time).astimezone(pytz.utc) + return pytz.timezone(str(tzinfo)).localize(point_in_time) if settings.USE_TZ: return pytz.utc.localize(point_in_time) else: @@ -228,8 +228,8 @@ def _get_year_range(self, year): start = naive_start end = naive_end if self.tzinfo is not None: - local_start = self.tzinfo.localize(naive_start) - local_end = self.tzinfo.localize(naive_end) + local_start = pytz.timezone(str(self.tzinfo)).localize(naive_start) + local_end = pytz.timezone(str(self.tzinfo)).localize(naive_end) start = local_start.astimezone(pytz.utc) end = local_end.astimezone(pytz.utc) @@ -319,8 +319,8 @@ def _get_month_range(self, month): start = naive_start end = naive_end if self.tzinfo is not None: - local_start = self.tzinfo.localize(naive_start) - local_end = self.tzinfo.localize(naive_end) + local_start = pytz.timezone(str(self.tzinfo)).localize(naive_start) + local_end = pytz.timezone(str(self.tzinfo)).localize(naive_end) start = local_start.astimezone(pytz.utc) end = local_end.astimezone(pytz.utc) @@ -402,8 +402,8 @@ def _get_week_range(self, week): naive_end = naive_start + datetime.timedelta(days=7) if self.tzinfo is not None: - local_start = self.tzinfo.localize(naive_start) - local_end = self.tzinfo.localize(naive_end) + local_start = pytz.timezone(str(self.tzinfo)).localize(naive_start) + local_end = pytz.timezone(str(self.tzinfo)).localize(naive_end) start = local_start.astimezone(pytz.utc) end = local_end.astimezone(pytz.utc) else: @@ -456,8 +456,8 @@ def _get_day_range(self, date): date + datetime.timedelta(days=1), datetime.time.min ) if self.tzinfo is not None: - local_start = self.tzinfo.localize(naive_start) - local_end = self.tzinfo.localize(naive_end) + local_start = pytz.timezone(str(self.tzinfo)).localize(naive_start) + local_end = pytz.timezone(str(self.tzinfo)).localize(naive_end) start = local_start.astimezone(pytz.utc) end = local_end.astimezone(pytz.utc) else: diff --git a/tests/test_calendar.py b/tests/test_calendar.py index 1e6011c8..6b98bb6a 100644 --- a/tests/test_calendar.py +++ b/tests/test_calendar.py @@ -23,6 +23,7 @@ class Meta: class TestCalendar(TestCase): def test_get_recent_events_without_events_is_empty(self): calendar = Calendar() + calendar.save() self.assertEqual(list(calendar.get_recent()), []) def test_get_recent_events_with_events_return_the_event(self): diff --git a/tox.ini b/tox.ini index 870332b7..779ce27b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,24 +1,24 @@ [tox] envlist = lint - django22 django31 django32 - djangomain + django40 +# djangomain [testenv] commands = {envpython} -Wa -b -m coverage run -m django test --settings=tests.settings deps = - django22: Django>=2.2,<2.3 django31: Django~=3.1.0 django32: Django~=3.2.0 - djangomain: https://github.com/django/django/archive/main.tar.gz + django40: Django~=4.0.0 +# djangomain: https://github.com/django/django/archive/main.tar.gz coverage [testenv:lint] basepython = python3 commands = - black --target-version py36 --check --diff . + black --target-version py39 --check --diff . flake8 isort --check-only --diff . {envpython} -m django check --settings=tests.settings