diff --git a/cron/jobs.py b/cron/jobs.py index a59ee936bd..e1b40cc71d 100644 --- a/cron/jobs.py +++ b/cron/jobs.py @@ -794,11 +794,99 @@ def parse_duration(s: str) -> int: value = int(match.group(1)) if match.group(1) else 1 unit = match.group(2)[0] # First char: m, h, or d - + multipliers = {'m': 1, 'h': 60, 'd': 1440} return value * multipliers[unit] +# Natural-language day-spec phrases for the documented "every monday 9am" / +# "every day at 9am" schedule forms. Cron weekday numbering is +# 0=Sunday … 6=Saturday (croniter's default). +_WEEKDAY_TO_CRON_DOW = { + "sunday": "0", "sun": "0", + "monday": "1", "mon": "1", + "tuesday": "2", "tue": "2", "tues": "2", + "wednesday": "3", "wed": "3", "weds": "3", + "thursday": "4", "thu": "4", "thur": "4", "thurs": "4", + "friday": "5", "fri": "5", + "saturday": "6", "sat": "6", +} + +# Keyword day-specs that expand to a cron weekday field. +_DAYSPEC_TO_CRON_DOW = { + "day": "*", "daily": "*", "everyday": "*", + "weekday": "1-5", "weekdays": "1-5", + "weekend": "0,6", "weekends": "0,6", +} + + +def _parse_clock_time(text: str) -> Optional[tuple]: + """Parse a wall-clock time into a ``(hour, minute)`` 24-hour tuple. + + Accepts ``9am``, ``9:30am``, ``9 am``, ``14:00``, ``7`` (bare hour, 24h), + ``noon``/``midday``, and ``midnight``. Returns None when the text is not a + recognized clock time so the caller can reject the schedule cleanly. + """ + t = text.strip().lower().replace(" ", "") + if not t: + return None + if t in ("noon", "midday"): + return (12, 0) + if t == "midnight": + return (0, 0) + match = re.match(r'^(\d{1,2})(?::(\d{2}))?(am|pm)?$', t) + if not match: + return None + hour = int(match.group(1)) + minute = int(match.group(2) or 0) + meridiem = match.group(3) + if meridiem: + if not 1 <= hour <= 12: + return None + if meridiem == "am": + hour = 0 if hour == 12 else hour + else: # pm + hour = 12 if hour == 12 else hour + 12 + if hour > 23 or minute > 59: + return None + return (hour, minute) + + +def _natural_every_to_cron(rest: str) -> Optional[str]: + """Convert a documented ``every [at]