Simplify the cron-to-schedule inference

Rewrite inferScheduleFromCron: bail once on a pinned month, group the branches
by cadence (sub-hourly / hourly / daily / weekly / monthly), and share the
hour+minute via one `clock` object instead of repeating the same guards in
eight stacked conditionals. Behavior-preserving — verified the generate↔infer
round-trip across every preset.
This commit is contained in:
Paulo Castellano 2026-06-13 17:31:02 -03:00
parent c79b4a4a84
commit cf3cbfba9e

View file

@ -26,33 +26,40 @@ const inferScheduleFromCron = (cron: string): Partial<ScheduleData> | null => {
if (parts.length !== 5) return null;
const [minute, hour, dom, month, dow] = parts;
const intervalOf = (token: string): number | null =>
token.startsWith('*/') && /^\d+$/.test(token.slice(2)) ? Number(token.slice(2)) : null;
const isNum = (t: string) => /^\d+$/.test(t);
if (month !== '*') return null; // no preset pins a month
if (minute === '*' && hour === '*' && dom === '*' && month === '*' && dow === '*') {
return { schedule_field: ScheduleField.Minutes, schedule_minutes_interval: 1 };
const interval = (token: string): number | null => (/^\*\/\d+$/.test(token) ? Number(token.slice(2)) : null);
const isNum = (token: string) => /^\d+$/.test(token);
// Sub-hourly: every minute or every Nth minute, regardless of hour/day.
if (hour === '*' && dom === '*' && dow === '*') {
const every = minute === '*' ? 1 : interval(minute);
return every === null ? null : { schedule_field: ScheduleField.Minutes, schedule_minutes_interval: every };
}
const m = intervalOf(minute);
if (m !== null && hour === '*' && dom === '*' && month === '*' && dow === '*') {
return { schedule_field: ScheduleField.Minutes, schedule_minutes_interval: m };
if (!isNum(minute)) return null;
const schedule_minute = Number(minute);
// Hourly: a fixed minute past every Nth hour.
const everyHours = interval(hour);
if (everyHours !== null && dom === '*' && dow === '*') {
return { schedule_field: ScheduleField.Hours, schedule_hours_interval: everyHours, schedule_minute };
}
const h = intervalOf(hour);
if (isNum(minute) && h !== null && dom === '*' && month === '*' && dow === '*') {
return { schedule_field: ScheduleField.Hours, schedule_hours_interval: h, schedule_minute: Number(minute) };
if (!isNum(hour)) return null;
const clock = { schedule_hour: Number(hour), schedule_minute };
// Daily: at `clock`, every Nth day (a bare `*` means every day).
if (dow === '*' && (dom === '*' || interval(dom) !== null)) {
return { schedule_field: ScheduleField.Days, schedule_days_interval: interval(dom) ?? 1, ...clock };
}
const d = intervalOf(dom);
if (isNum(minute) && isNum(hour) && d !== null && month === '*' && dow === '*') {
return { schedule_field: ScheduleField.Days, schedule_days_interval: d, schedule_hour: Number(hour), schedule_minute: Number(minute) };
// Weekly: at `clock`, on the listed weekdays.
if (dom === '*' && /^\d+(,\d+)*$/.test(dow)) {
return { schedule_field: ScheduleField.Weeks, schedule_weekdays: dow.split(',').map(Number), ...clock };
}
if (isNum(minute) && isNum(hour) && dom === '*' && month === '*' && dow === '*') {
return { schedule_field: ScheduleField.Days, schedule_days_interval: 1, schedule_hour: Number(hour), schedule_minute: Number(minute) };
}
if (isNum(minute) && isNum(hour) && dom === '*' && month === '*' && /^\d+(,\d+)*$/.test(dow)) {
return { schedule_field: ScheduleField.Weeks, schedule_weekdays: dow.split(',').map(Number), schedule_hour: Number(hour), schedule_minute: Number(minute) };
}
if (isNum(minute) && isNum(hour) && isNum(dom) && month === '*' && dow === '*') {
return { schedule_field: ScheduleField.Months, schedule_day_of_month: Number(dom), schedule_hour: Number(hour), schedule_minute: Number(minute) };
// Monthly: at `clock`, on a fixed day of the month.
if (dow === '*' && isNum(dom)) {
return { schedule_field: ScheduleField.Months, schedule_day_of_month: Number(dom), ...clock };
}
return null;
};