初始提交:OCI 面板前端(含 GenAI 网关一期)
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
/** 5 段 cron 单字段匹配:支持 *、n、*\/k、a-b、逗号组合;超出子集返回 false */
|
||||
function fieldMatch(field: string, v: number): boolean {
|
||||
return field.split(',').some((seg) => {
|
||||
const step = /^\*\/(\d+)$/.exec(seg)
|
||||
if (step) return Number(step[1]) > 0 && v % Number(step[1]) === 0
|
||||
const range = /^(\d+)-(\d+)$/.exec(seg)
|
||||
if (range) return v >= Number(range[1]) && v <= Number(range[2])
|
||||
return seg === '*' || seg === String(v)
|
||||
})
|
||||
}
|
||||
|
||||
/** 计算 5 段 cron 的下次执行时间(本地时区):
|
||||
* 从下一分钟起逐分钟扫描 8 天,表达式非法或窗口内无匹配返回 null。
|
||||
* 日与周同时受限时按 AND 近似(面板生成的表达式不会同时限制)。 */
|
||||
export function nextCronRun(expr: string): Date | null {
|
||||
const p = expr.trim().split(/\s+/)
|
||||
if (p.length !== 5) return null
|
||||
const t = new Date()
|
||||
t.setSeconds(0, 0)
|
||||
for (let i = 0; i < 8 * 24 * 60; i++) {
|
||||
t.setMinutes(t.getMinutes() + 1)
|
||||
if (
|
||||
fieldMatch(p[0], t.getMinutes()) &&
|
||||
fieldMatch(p[1], t.getHours()) &&
|
||||
fieldMatch(p[2], t.getDate()) &&
|
||||
fieldMatch(p[3], t.getMonth() + 1) &&
|
||||
fieldMatch(p[4], t.getDay())
|
||||
)
|
||||
return new Date(t)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const pad2 = (n: number) => String(n).padStart(2, '0')
|
||||
|
||||
/** 下次执行的短文案(MM-DD HH:mm);无法计算返回空串 */
|
||||
export function nextCronRunLabel(expr: string): string {
|
||||
const d = nextCronRun(expr)
|
||||
if (!d) return ''
|
||||
return `${pad2(d.getMonth() + 1)}-${pad2(d.getDate())} ${pad2(d.getHours())}:${pad2(d.getMinutes())}`
|
||||
}
|
||||
Reference in New Issue
Block a user