diff options
author | Lowell Alleman <lowell@kintyre.co> | 2021-03-29 12:00:50 -0400 |
---|---|---|
committer | kiorky <kiorky@cryptelium.net> | 2021-04-07 17:01:14 +0200 |
commit | 0065a73b453fb4c671cd4dc9a3448ee7df3b02f5 (patch) | |
tree | b04ea70acd72beb9f103ef6e443711b5fc60b14a | |
parent | 60ced5cd2a96f0a62d9ef87d72065aaf36349ffb (diff) | |
download | croniter-0065a73b453fb4c671cd4dc9a3448ee7df3b02f5.tar.gz |
More consolidation of nth/last-wday functionality
- Remove _get_last_weekday_of_month and replaced it with
_get_nth_weekday_of_month() and avoid making multiple calls to the calendar
module when both '#' and 'L' syntax are used at the same time.
- Updated unittests for get_last_weekday_of_month() to instead simply call
_get_nth_weekday_of_month()[-1].
- Create unit test for new function.
Note that _get_nth_weekday_of_month() is a good candidate for caching as
repeated calls to calendar add up over time, especially when evaluating
multiple cron expressions concurrently.
-rw-r--r-- | src/croniter/croniter.py | 33 | ||||
-rwxr-xr-x | src/croniter/tests/test_croniter.py | 32 |
2 files changed, 33 insertions, 32 deletions
diff --git a/src/croniter/croniter.py b/src/croniter/croniter.py index 17e883d..45ccaf1 100644 --- a/src/croniter/croniter.py +++ b/src/croniter/croniter.py @@ -394,19 +394,14 @@ class croniter(object): candidates = [] for wday, nth in nth_weekday_of_month.items(): - c = None - if nth != {"l"}: - w = (wday + 6) % 7 - c = calendar.Calendar(w).monthdayscalendar(d.year, d.month) - if c[0][0] == 0: - c.pop(0) + c = self._get_nth_weekday_of_month(d.year, d.month, wday) for n in nth: if n == "l": - candidate = self._get_last_weekday_of_month(d.year, d.month, wday) + candidate = c[-1] elif len(c) < n: continue else: - candidate = c[n - 1][0] + candidate = c[n - 1] if ( (is_prev and candidate <= d.day) or (not is_prev and d.day <= candidate) @@ -548,21 +543,15 @@ class croniter(object): return (candidate - x - range_val) @staticmethod - def _get_last_weekday_of_month(year, month, day_of_week): - """ Given the year/month of timestamp, determine the last day of the - month which is a particular day of the week. Calendar week starts on - Sunday, to match cron's day_of_week convention. + def _get_nth_weekday_of_month(year, month, day_of_week): + """ For a given year/month return a list of days in nth-day-of-month order. + The last weekday of the month is always [-1]. """ - # How expensive is this? Easily cache by (year, month, dow) - day_of_week = int(day_of_week) - cal = calendar.Calendar(6).monthdayscalendar(year, month) - week = -1 - while True: - day = cal[week][day_of_week] - if day == 0: # 0 means absent / different month - week -= 1 - else: - return day + w = (day_of_week + 6) % 7 + c = calendar.Calendar(w).monthdayscalendar(year, month) + if c[0][0] == 0: + c.pop(0) + return tuple(i[0] for i in c) def is_leap(self, year): if year % 400 == 0 or (year % 4 == 0 and year % 100 != 0): diff --git a/src/croniter/tests/test_croniter.py b/src/croniter/tests/test_croniter.py index c171983..1083b62 100755 --- a/src/croniter/tests/test_croniter.py +++ b/src/croniter/tests/test_croniter.py @@ -1103,19 +1103,31 @@ class CroniterTest(base.TestCase): '2020-03-29T02:01:00+02:00', '2020-03-29T03:01:00+02:00']) - def test_wdom_core_simple(self): - f = croniter._get_last_weekday_of_month + + def test_nth_wday_simple(self): + f = lambda y,m,w: croniter._get_nth_weekday_of_month(y,m,w) + sun, mon, tue, wed, thu, fri, sat = range(7) + + self.assertEqual(f(2000, 1, mon), (3, 10, 17, 24, 31)) + self.assertEqual(f(2000, 2, tue), (1, 8, 15, 22, 29)) # Leap year + self.assertEqual(f(2000, 3, wed), (1, 8, 15, 22, 29)) + self.assertEqual(f(2000, 4, thu), (6, 13, 20, 27)) + self.assertEqual(f(2000, 2, fri), (4, 11, 18, 25)) + self.assertEqual(f(2000, 2, sat), (5, 12, 19, 26)) + + def test_nth_as_last_wday_simple(self): + f = lambda y,m,w: croniter._get_nth_weekday_of_month(y,m,w)[-1] sun, mon, tue, wed, thu, fri, sat = range(7) - self.assertEqual(f(2021, 3, sun), 28) - self.assertEqual(f(2035, 12, sat), 29) - self.assertEqual(f(2000, 1, fri), 28) - self.assertEqual(f(2014, 8, mon), 25) - self.assertEqual(f(2022, 2, tue), 22) - self.assertEqual(f(1999, 10, wed), 27) - self.assertEqual(f(2005, 7, thu), 28) + self.assertEqual(f(2000, 2, tue), 29) + self.assertEqual(f(2000, 2, sun), 27) + self.assertEqual(f(2000, 2, mon), 28) + self.assertEqual(f(2000, 2, wed), 23) + self.assertEqual(f(2000, 2, thu), 24) + self.assertEqual(f(2000, 2, fri), 25) + self.assertEqual(f(2000, 2, sat), 26) def test_wdom_core_leap_year(self): - f = croniter._get_last_weekday_of_month + f = lambda y,m,w: croniter._get_nth_weekday_of_month(y,m,w)[-1] sun, mon, tue, wed, thu, fri, sat = range(7) self.assertEqual(f(2000, 2, tue), 29) self.assertEqual(f(2000, 2, sun), 27) |