summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLowell Alleman <lowell@kintyre.co>2021-03-29 12:00:50 -0400
committerkiorky <kiorky@cryptelium.net>2021-04-07 17:01:14 +0200
commit0065a73b453fb4c671cd4dc9a3448ee7df3b02f5 (patch)
treeb04ea70acd72beb9f103ef6e443711b5fc60b14a
parent60ced5cd2a96f0a62d9ef87d72065aaf36349ffb (diff)
downloadcroniter-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.py33
-rwxr-xr-xsrc/croniter/tests/test_croniter.py32
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)