Skip to content

Commit

Permalink
Formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
SijmenHuizenga committed May 5, 2024
1 parent 9bc4e99 commit bd4d828
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 28 deletions.
22 changes: 13 additions & 9 deletions schedule/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
[2] https://github.com/Rykian/clockwork
[3] https://adam.herokuapp.com/past/2010/6/30/replace_cron_with_clockwork/
"""

from collections.abc import Hashable
import datetime
import functools
Expand Down Expand Up @@ -732,7 +733,9 @@ def _schedule_next_run(self) -> None:
while next_run <= now:
next_run += period

next_run = self._fix_zones(next_run, fixate_time=(self.at_time is not None))
next_run = self._align_utc_offset(
next_run, fixate_time=(self.at_time is not None)
)

# To keep the api consistent with older versions, we have to set the 'next_run' to a naive timestamp in the local timezone.
# Because we want to stay backwards compatible with older versions.
Expand Down Expand Up @@ -769,7 +772,9 @@ def _weekday_index(self, day: str) -> int:
)
return weekdays.index(day)

def _move_to_time(self, moment: datetime.datetime, time: datetime.time) -> datetime.datetime:
def _move_to_time(
self, moment: datetime.datetime, time: datetime.time
) -> datetime.datetime:
kwargs = {"second": time.second, "microsecond": 0}

if self.unit == "days" or self.start_day is not None:
Expand All @@ -782,15 +787,17 @@ def _move_to_time(self, moment: datetime.datetime, time: datetime.time) -> datet

# When we set the time elements, we might end up in a different UTC-offset than the current offset.
# This happens when we cross into or out of daylight saving time.
moment = self._fix_zones(moment, True)
moment = self._align_utc_offset(moment, fixate_time=True)

return moment

def _fix_zones(self, moment: datetime.datetime, fixate_time: bool) -> datetime.datetime:
def _align_utc_offset(
self, moment: datetime.datetime, fixate_time: bool
) -> datetime.datetime:
if self.at_time_zone is None:
return moment
# Normalize adjusts the timezone to the correct offset at the given moment
# while keeping the moment in time the same.
# Normalize adjusts the timezone to the correct offset at the given
# moment while keeping the moment in time the same.
offset_before_normalize = moment.utcoffset()
moment = self.at_time_zone.normalize(moment)
offset_after_normalize = moment.utcoffset()
Expand Down Expand Up @@ -823,8 +830,6 @@ def _fix_zones(self, moment: datetime.datetime, fixate_time: bool) -> datetime.d
moment = moment + (offset_after_normalize - offset_before_normalize)
return moment



def _is_overdue(self, when: datetime.datetime):
return self.cancel_after is not None and when > self.cancel_after

Expand Down Expand Up @@ -920,4 +925,3 @@ def _schedule_decorator(decorated_function):
return decorated_function

return _schedule_decorator

55 changes: 36 additions & 19 deletions test_schedule.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Unit tests for schedule.py"""

import datetime
import functools
import mock
Expand Down Expand Up @@ -35,6 +36,7 @@ def make_mock_job(name=None):
job.__name__ = name or "job"
return job


class mock_datetime:
"""
Monkey-patch datetime for predictable results
Expand Down Expand Up @@ -618,7 +620,6 @@ def test_tz_daily_midnight(self):
assert next.hour == 7
assert next.minute == 0


def test_tz_daily_half_hour_offset(self):
mock_job = self.make_tz_mock_job()
with mock_datetime(2022, 4, 8, 10, 0):
Expand All @@ -630,10 +631,10 @@ def test_tz_daily_half_hour_offset(self):
assert next.hour == 16
assert next.minute == 30


def test_tz_daily_dst(self):
mock_job = self.make_tz_mock_job()
import pytz

with mock_datetime(2022, 3, 20, 10, 0):
# Current Berlin time: 10:00 (local) (NOT during daylight saving)
# Current NY time: 04:00 (during daylight saving)
Expand All @@ -644,7 +645,6 @@ def test_tz_daily_dst(self):
assert next.hour == 15
assert next.minute == 30


def test_tz_daily_dst_skip_hour(self):
mock_job = self.make_tz_mock_job()
# Test the DST-case that is described in the documentation
Expand All @@ -664,7 +664,6 @@ def test_tz_daily_dst_skip_hour(self):
assert job.next_run.hour == 2
assert job.next_run.minute == 30


def test_tz_daily_dst_overlap_hour(self):
mock_job = self.make_tz_mock_job()
# Test the DST-case that is described in the documentation
Expand All @@ -684,7 +683,6 @@ def test_tz_daily_dst_overlap_hour(self):
assert job.next_run.hour == 2
assert job.next_run.minute == 30


def test_tz_daily_exact_future_scheduling(self):
mock_job = self.make_tz_mock_job()
with mock_datetime(2022, 3, 20, 10, 0):
Expand All @@ -700,7 +698,6 @@ def test_tz_daily_exact_future_scheduling(self):
)
assert schedule.idle_seconds() == expected_delta.total_seconds()


def test_tz_daily_utc(self):
mock_job = self.make_tz_mock_job()
with mock_datetime(2023, 9, 18, 10, 59, 0, TZ_AUCKLAND):
Expand All @@ -722,7 +719,6 @@ def test_tz_daily_utc(self):
assert next.hour == 12
assert next.minute == 0


def test_tz_daily_issue_592(self):
mock_job = self.make_tz_mock_job()
with mock_datetime(2023, 7, 15, 13, 0, 0, TZ_UTC):
Expand All @@ -736,7 +732,6 @@ def test_tz_daily_issue_592(self):
assert next.hour == 13
assert next.minute == 45


def test_tz_daily_exact_seconds_precision(self):
mock_job = self.make_tz_mock_job()
with mock_datetime(2023, 10, 19, 15, 0, 0, TZ_UTC):
Expand All @@ -752,7 +747,6 @@ def test_tz_daily_exact_seconds_precision(self):
assert next.minute == 00
assert next.second == 20


def test_tz_weekly_sunday_conversion(self):
mock_job = self.make_tz_mock_job()
with mock_datetime(2023, 10, 22, 23, 0, 0, TZ_UTC):
Expand Down Expand Up @@ -804,7 +798,6 @@ def test_tz_daily_end_month_offset(self):
assert next.hour == 0
assert next.minute == 0


def test_tz_daily_leap_year(self):
mock_job = self.make_tz_mock_job()
with mock_datetime(2024, 2, 28, 23, 50):
Expand Down Expand Up @@ -850,7 +843,6 @@ def test_tz_daily_dst_starting_point(self):
assert next.hour == 3
assert next.minute == 0


def test_tz_daily_dst_ending_point(self):
mock_job = self.make_tz_mock_job()
with mock_datetime(2023, 10, 29, 2, 30, fold=1):
Expand Down Expand Up @@ -931,7 +923,12 @@ def test_tz_hourly_intermediate_conversion(self):
# Exected next run in Newfoundland: 4 may, 09:14:45
# Expected next run in Chatham: 5 may, 00:29:45
schedule.clear()
next = schedule.every(10).hours.at("14:45", "Canada/Newfoundland").do(mock_job).next_run
next = (
schedule.every(10)
.hours.at("14:45", "Canada/Newfoundland")
.do(mock_job)
.next_run
)
assert next.day == 5
assert next.hour == 0
assert next.minute == 29
Expand Down Expand Up @@ -980,7 +977,7 @@ def test_tz_minutes_year_round(self):
# Expected time: 3 Nov, 02:43:13 Chatham
assert job.next_run.day == 3
assert job.next_run.hour == 2
assert job.next_run.minute == 43 # Within the fold, first occurrence
assert job.next_run.minute == 43 # Within the fold, first occurrence
assert job.next_run.second == 13
with mock_datetime(2024, 11, 3, 2, 23, 55, TZ_CHATHAM, fold=1):
# Time is during the fold. Local time has moved back 1 hour, this is
Expand Down Expand Up @@ -1052,7 +1049,12 @@ def test_tz_weekly_large_interval_forward(self):
with mock_datetime(2024, 3, 28, 11, 0, 0, TZ_BERLIN):
# At March 31st 2024, 02:00:00 clocks were turned forward 1 hour
schedule.clear()
next = schedule.every(7).days.at("11:00", "Europe/Berlin").do(mock_job).next_run
next = (
schedule.every(7)
.days.at("11:00", "Europe/Berlin")
.do(mock_job)
.next_run
)
assert next.month == 4
assert next.day == 4
assert next.hour == 11
Expand All @@ -1062,11 +1064,17 @@ def test_tz_weekly_large_interval_forward(self):
def test_tz_weekly_large_interval_backward(self):
mock_job = self.make_tz_mock_job()
import pytz

# Testing scheduling large intervals that skip over clock move back
with mock_datetime(2024, 10, 25, 11, 0, 0, TZ_BERLIN):
# At March 31st 2024, 02:00:00 clocks were turned forward 1 hour
schedule.clear()
next = schedule.every(7).days.at("11:00", "Europe/Berlin").do(mock_job).next_run
next = (
schedule.every(7)
.days.at("11:00", "Europe/Berlin")
.do(mock_job)
.next_run
)
assert next.month == 11
assert next.day == 1
assert next.hour == 11
Expand All @@ -1083,7 +1091,12 @@ def test_tz_daily_skip_dst_change(self):
# Expected time Anchorage: 3 Nov, 14:00 (UTC-09:00)
# Expected time Berlin: 4 Nov, 00:00
schedule.clear()
next = schedule.every().day.at("14:00", "America/Anchorage").do(mock_job).next_run
next = (
schedule.every()
.day.at("14:00", "America/Anchorage")
.do(mock_job)
.next_run
)
assert next.day == 4
assert next.hour == 0
assert next.minute == 00
Expand All @@ -1103,7 +1116,9 @@ def test_tz_daily_different_simultaneous_dst_change(self):
# Expected time Berlin: 31 Mar, 10:00 (UTC+02:00)
# Expected time Berlin Extra: 31 Mar, 11:00 (UTC+03:00)
schedule.clear()
next = schedule.every().day.at("10:00", "Europe/Berlin").do(mock_job).next_run
next = (
schedule.every().day.at("10:00", "Europe/Berlin").do(mock_job).next_run
)
assert next.day == 31
assert next.hour == 11
assert next.minute == 00
Expand All @@ -1122,7 +1137,9 @@ def test_tz_daily_opposite_dst_change(self):
# Expected time Berlin: 31 Mar, 10:00 (UTC+02:00) +9 hour
# Expected time Berlin Inverted: 31 Mar, 09:00 (UTC+01:00)
schedule.clear()
next = schedule.every().day.at("10:00", "Europe/Berlin").do(mock_job).next_run
next = (
schedule.every().day.at("10:00", "Europe/Berlin").do(mock_job).next_run
)
assert next.day == 31
assert next.hour == 9
assert next.minute == 00
Expand Down Expand Up @@ -1303,7 +1320,7 @@ def test_run_pending(self):

def test_run_every_weekday_at_specific_time_today(self):
mock_job = make_mock_job()
with mock_datetime(2010, 1, 6, 13, 16): # january 6 2010 == Wednesday
with mock_datetime(2010, 1, 6, 13, 16): # january 6 2010 == Wednesday
every().wednesday.at("14:12").do(mock_job)
schedule.run_pending()
assert mock_job.call_count == 0
Expand Down

0 comments on commit bd4d828

Please sign in to comment.