diff options
author | Mathieu Le Marec - Pasquet <kiorky@cryptelium.net> | 2024-11-24 22:04:05 +0100 |
---|---|---|
committer | Mathieu Le Marec - Pasquet <kiorky@cryptelium.net> | 2023-04-07 15:42:48 +0200 |
commit | 137434227162c9c16b3e4acfcbfc4d5c7615bc28 (patch) | |
tree | f7573acbd5c5981178c8b21e78a6e9f7d38a046c | |
parent | 9bc488fe77ea905a821ae0b3d066128db8cac879 (diff) | |
download | croniter-137434227162c9c16b3e4acfcbfc4d5c7615bc28.tar.gz |
Fix DOW hash parsing
This fixes #33
-rw-r--r-- | src/croniter/croniter.py | 43 | ||||
-rwxr-xr-x | src/croniter/tests/test_croniter.py | 62 |
2 files changed, 88 insertions, 17 deletions
diff --git a/src/croniter/croniter.py b/src/croniter/croniter.py index 1e4b199..af3130f 100644 --- a/src/croniter/croniter.py +++ b/src/croniter/croniter.py @@ -3,6 +3,7 @@ from __future__ import absolute_import, print_function, division +import copy import math import re import sys @@ -21,10 +22,20 @@ except ImportError: OrderedDict = dict # py26 degraded mode, expanders order will not be immutable +M_ALPHAS = {'jan': 1, 'feb': 2, 'mar': 3, 'apr': 4, 'may': 5, 'jun': 6, + 'jul': 7, 'aug': 8, 'sep': 9, 'oct': 10, 'nov': 11, 'dec': 12} +DOW_ALPHAS = {'sun': 0, 'mon': 1, 'tue': 2, 'wed': 3, 'thu': 4, 'fri': 5, 'sat': 6} +ALPHAS = {} +for i in M_ALPHAS, DOW_ALPHAS: + ALPHAS.update(i) step_search_re = re.compile(r'^([^-]+)-([^-/]+)(/(\d+))?$') only_int_re = re.compile(r'^\d+$') + +WEEKDAYS = '|'.join(DOW_ALPHAS.keys()) +MONTHS = '|'.join(M_ALPHAS.keys()) star_or_int_re = re.compile(r'^(\d+|\*)$') -special_weekday_re = re.compile(r'^(\w+)#(\d+)|l(\d+)$') +special_dow_re = re.compile(rf'^(?P<pre>((?P<he>(({WEEKDAYS})(-({WEEKDAYS}))?)' + rf'|(({MONTHS})(-({MONTHS}))?)|\w+)#)|l)(?P<last>\d+)$') hash_expression_re = re.compile( r'^(?P<hash_type>h|r)(\((?P<range_begin>\d+)-(?P<range_end>\d+)\))?(\/(?P<divisor>\d+))?$' ) @@ -106,10 +117,9 @@ class croniter(object): {}, # 1: hour {"l": "l"}, # 2: dom # 3: mon - {'jan': 1, 'feb': 2, 'mar': 3, 'apr': 4, 'may': 5, 'jun': 6, - 'jul': 7, 'aug': 8, 'sep': 9, 'oct': 10, 'nov': 11, 'dec': 12}, + copy.deepcopy(M_ALPHAS), # 4: dow - {'sun': 0, 'mon': 1, 'tue': 2, 'wed': 3, 'thu': 4, 'fri': 5, 'sat': 6}, + copy.deepcopy(DOW_ALPHAS), # command/user {} ) @@ -635,27 +645,26 @@ class croniter(object): while len(e_list) > 0: e = e_list.pop() + nth = None if i == 4: - # Handle special case in the day-of-week expression - m = special_weekday_re.match(str(e)) - if m: - orig_e = e - e, nth, last = m.groups() - if nth: + # Handle special case in the dow expression: 2#3, l3 + special_dow_rem = special_dow_re.match(str(e)) + if special_dow_rem: + g = special_dow_rem.groupdict() + he, last = g.get('he', ''), g.get('last', '') + if he: + e = he try: - nth = int(nth) + nth = int(last) assert (nth >= 1 and nth <= 5) - except (ValueError, AssertionError): + except (KeyError, ValueError, AssertionError): raise CroniterBadCronError( "[{0}] is not acceptable. Invalid day_of_week " - "value: '{1}'".format(expr_format, orig_e)) + "value: '{1}'".format(expr_format, nth)) elif last: - nth = "l" e = last - del last, orig_e - else: - nth = None + nth = g['pre'] # 'l' # Before matching step_search_re, normalize "*" to "{min}-{max}". # Example: in the minute field, "*/5" normalizes to "0-59/5" diff --git a/src/croniter/tests/test_croniter.py b/src/croniter/tests/test_croniter.py index 3a95490..a9d8ade 100755 --- a/src/croniter/tests/test_croniter.py +++ b/src/croniter/tests/test_croniter.py @@ -1473,6 +1473,68 @@ class CroniterTest(base.TestCase): self.assertEqual(n1, datetime_to_timestamp(base) + 60) + def test_issue_k33(self): + y = 2018 + # At 11:30 PM, between day 1 and 7 of the month, Monday through Friday, only in January + ret = [] + for i in range(10): + cron = croniter("30 23 1-7 JAN MON-FRI#1", datetime(y+i, 1, 1), ret_type=datetime) + for j in range(7): + d = cron.get_next() + if d.year == y + i: + ret.append(d) + rets = [datetime(2018, 1, 1, 23, 30), + datetime(2018, 1, 2, 23, 30), + datetime(2018, 1, 3, 23, 30), + datetime(2018, 1, 4, 23, 30), + datetime(2018, 1, 5, 23, 30), + datetime(2019, 1, 1, 23, 30), + datetime(2019, 1, 2, 23, 30), + datetime(2019, 1, 3, 23, 30), + datetime(2019, 1, 4, 23, 30), + datetime(2019, 1, 7, 23, 30), + datetime(2020, 1, 1, 23, 30), + datetime(2020, 1, 2, 23, 30), + datetime(2020, 1, 3, 23, 30), + datetime(2020, 1, 6, 23, 30), + datetime(2020, 1, 7, 23, 30), + datetime(2021, 1, 1, 23, 30), + datetime(2021, 1, 4, 23, 30), + datetime(2021, 1, 5, 23, 30), + datetime(2021, 1, 6, 23, 30), + datetime(2021, 1, 7, 23, 30), + datetime(2022, 1, 3, 23, 30), + datetime(2022, 1, 4, 23, 30), + datetime(2022, 1, 5, 23, 30), + datetime(2022, 1, 6, 23, 30), + datetime(2022, 1, 7, 23, 30), + datetime(2023, 1, 2, 23, 30), + datetime(2023, 1, 3, 23, 30), + datetime(2023, 1, 4, 23, 30), + datetime(2023, 1, 5, 23, 30), + datetime(2023, 1, 6, 23, 30), + datetime(2024, 1, 1, 23, 30), + datetime(2024, 1, 2, 23, 30), + datetime(2024, 1, 3, 23, 30), + datetime(2024, 1, 4, 23, 30), + datetime(2024, 1, 5, 23, 30), + datetime(2025, 1, 1, 23, 30), + datetime(2025, 1, 2, 23, 30), + datetime(2025, 1, 3, 23, 30), + datetime(2025, 1, 6, 23, 30), + datetime(2025, 1, 7, 23, 30), + datetime(2026, 1, 1, 23, 30), + datetime(2026, 1, 2, 23, 30), + datetime(2026, 1, 5, 23, 30), + datetime(2026, 1, 6, 23, 30), + datetime(2026, 1, 7, 23, 30), + datetime(2027, 1, 1, 23, 30), + datetime(2027, 1, 4, 23, 30), + datetime(2027, 1, 5, 23, 30), + datetime(2027, 1, 6, 23, 30), + datetime(2027, 1, 7, 23, 30)] + self.assertEqual(ret, rets) + croniter.expand("30 6 1-7 MAY MON#1") if __name__ == '__main__': unittest.main() |