function parseField(source, min, max) { const values = new Set(); for (const part of source.split(",")) { const match = /^(\*|\d+(?:-\d+)?)(?:\/(\d+))?$/.exec(part); if (!match) throw new Error("Campo cron inválido."); const step = Number(match[2] || 1); if (step < 1) throw new Error("Passo cron inválido."); const base = match[1], range = base === "*" ? [min, max] : base.includes("-") ? base.split("-").map(Number) : [Number(base), match[2] ? max : Number(base)]; if (range[0] < min || range[1] > max || range[0] > range[1]) throw new Error("Valor cron fora do intervalo."); for (let i = range[0]; i <= range[1]; i += step) values.add(i); } return values; } export function compileCron(expression, timezone = "Europe/Lisbon") { const fields = String(expression).trim().split(/\s+/); if (fields.length === 5) fields.unshift("0"); if (fields.length !== 6) throw new Error("O cron precisa de 5 ou 6 campos."); const limits = [ [0, 59], [0, 59], [0, 23], [1, 31], [1, 12], [0, 7], ], sets = fields.map((v, i) => parseField(v, ...limits[i])); if (sets[5].has(7)) sets[5].add(0); const formatter = new Intl.DateTimeFormat("en-GB", { timeZone: timezone, second: "2-digit", minute: "2-digit", hour: "2-digit", day: "2-digit", month: "2-digit", weekday: "short", hourCycle: "h23", }); return (date) => { const p = Object.fromEntries( formatter.formatToParts(date).map((p) => [p.type, p.value]), ), values = [ +p.second, +p.minute, +p.hour, +p.day, +p.month, ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].indexOf(p.weekday), ]; const day = sets[3].has(values[3]), week = sets[5].has(values[5]); return ( [0, 1, 2, 4].every((i) => sets[i].has(values[i])) && (fields[3] !== "*" && fields[5] !== "*" ? day || week : day && week) ); }; }