export type ScheduleRepeatMode = 'daily' | 'weekly' | 'interval' export type ScheduleFrequency = { mode: ScheduleRepeatMode hour: number minute: number weekday: number intervalHours: number } export const WEEKDAY_OPTIONS: Array<{ value: number; label: string }> = [ { value: 1, label: '週一' }, { value: 2, label: '週二' }, { value: 3, label: '週三' }, { value: 4, label: '週四' }, { value: 5, label: '週五' }, { value: 6, label: '週六' }, { value: 0, label: '週日' }, ] export const INTERVAL_HOUR_OPTIONS = [1, 2, 3, 4, 6, 8, 12, 24] as const export const HOUR_OPTIONS = Array.from({ length: 24 }, (_, hour) => hour) export const MINUTE_OPTIONS = Array.from({ length: 60 }, (_, minute) => minute) export function defaultScheduleFrequency(): ScheduleFrequency { return { mode: 'daily', hour: 9, minute: 0, weekday: 1, intervalHours: 6, } } export function buildCronFromFrequency(freq: ScheduleFrequency): string { switch (freq.mode) { case 'daily': return `${freq.minute} ${freq.hour} * * *` case 'weekly': return `${freq.minute} ${freq.hour} * * ${freq.weekday}` case 'interval': return `0 */${freq.intervalHours} * * *` } } export function parseCronToFrequency(cron: string): ScheduleFrequency | null { const parts = cron.trim().split(/\s+/) if (parts.length !== 5) return null const [minute, hour, dom, month, dow] = parts if ( minute === '0' && dom === '*' && month === '*' && dow === '*' && /^\*\/(\d+)$/.test(hour) ) { const intervalHours = Number.parseInt(hour.slice(2), 10) if (intervalHours >= 1 && intervalHours <= 24) { return { mode: 'interval', hour: 0, minute: 0, weekday: 1, intervalHours, } } } if ( dom === '*' && month === '*' && dow === '*' && /^\d+$/.test(minute) && /^\d+$/.test(hour) ) { return { mode: 'daily', hour: Number.parseInt(hour, 10), minute: Number.parseInt(minute, 10), weekday: 1, intervalHours: 6, } } if ( dom === '*' && month === '*' && /^\d+$/.test(minute) && /^\d+$/.test(hour) && /^\d+$/.test(dow) ) { return { mode: 'weekly', hour: Number.parseInt(hour, 10), minute: Number.parseInt(minute, 10), weekday: Number.parseInt(dow, 10), intervalHours: 6, } } return null } function padTimePart(value: number): string { return String(value).padStart(2, '0') } export function describeCron(cron: string): string { const freq = parseCronToFrequency(cron) if (!freq) return cron const time = `${padTimePart(freq.hour)}:${padTimePart(freq.minute)}` switch (freq.mode) { case 'daily': return `每天 ${time}` case 'weekly': { const weekday = WEEKDAY_OPTIONS.find((item) => item.value === freq.weekday)?.label ?? `週${freq.weekday}` return `每${weekday} ${time}` } case 'interval': return `每 ${freq.intervalHours} 小時` } } export function frequencyFromCron(cron: string): ScheduleFrequency { return parseCronToFrequency(cron) ?? defaultScheduleFrequency() }