thread-master/web/src/lib/scheduleCron.ts

130 lines
3.1 KiB
TypeScript
Raw Normal View History

2026-06-26 08:37:04 +00:00
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()
}