130 lines
3.1 KiB
TypeScript
130 lines
3.1 KiB
TypeScript
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()
|
|
} |