diff --git a/back/shared/cronSchedule.ts b/back/shared/cronSchedule.ts index 061a072c..a34174f1 100644 --- a/back/shared/cronSchedule.ts +++ b/back/shared/cronSchedule.ts @@ -22,6 +22,7 @@ export interface CronSchedule { // Validation and registration repeatedly see identical rules in large snapshots. // Cache only immutable syntax, never dates or running tasks, and bound user input. const scheduleCache = new Map(); +const daysInMonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; // Canonicalize legacy shorthand, aliases, names and numeric-start steps before // node-cron sees them. Extend legacy aliases explicitly; reject H and bare /N. @@ -58,17 +59,73 @@ export function parseCronSchedule(schedule: unknown): CronSchedule { const expression = CronExpressionParser.parse(parts.join(' ')); if (!expression.hasNext()) throw new Error('Cron schedule has no next execution'); - const normalized = expression.stringify(true).replace(/\?/g, '*').split(' '); - const dayCount = expression.fields.dayOfMonth.values.length; + let normalized = parts.map((part) => part.replace(/\?/g, '*')); + // cron-parser includes Sunday in stepped ranges ending in 7 even when the + // step does not land on 7. node-cron accepts those ranges with different days. + if (/(^|,)(?:\d+|[a-z]{3})-0*7\/\d+(?=,|$)/i.test(normalized[5])) { + normalized[5] = expression.fields.dayOfWeek.values.join(','); + } + // Most rules are already supported; only inspect individual fields on failure. + if (!validate(normalized.join(' '))) { + const fields = [ + expression.fields.second, + expression.fields.minute, + expression.fields.hour, + expression.fields.dayOfMonth, + expression.fields.month, + expression.fields.dayOfWeek, + ]; + normalized = normalized.map((value, index) => { + // Preserve supported lists, ranges and names. Expand only fields node-cron + // cannot consume, such as numeric/name-start steps; never compress them. + const probe = ['0', '0', '0', '*', '*', '*']; + probe[index] = value; + if (validate(probe.join(' '))) return value; + const values = fields[index].values; + if (index === 5 && expression.fields.dayOfWeek.nthDay) { + // The legacy normalizer treated *#N / ?#N as an unrestricted weekday. + if (values.length === 8) return '*'; + // # applies to a single weekday; normalize its numeric spelling only. + return values.length === 1 + ? `${values[0]}#${expression.fields.dayOfWeek.nthDay}` + : value; + } + return values.join(','); + }); + } + const months = expression.fields.month.values; + // cron-parser 4 clipped dates outside a single selected month's maximum + // before deciding whether the day field covered the whole month. + const dayCount = expression.fields.dayOfMonth.values.filter( + (day) => + months.length !== 1 || + typeof day !== 'number' || + day <= daysInMonth[months[0] - 1], + ).length; const weekCount = expression.fields.dayOfWeek.values.length; + // Preserve the legacy count-based wildcard rules, including repeated Sunday + // and L tokens: an ignored day field must not remain an AND constraint. + const weekdayPattern = [ + ...normalized.slice(0, 3), + '*', + ...normalized.slice(4), + ].join(' '); const patterns = - dayCount < 31 && weekCount < 8 - ? [ - [...normalized.slice(0, 5), '*'].join(' '), - [...normalized.slice(0, 3), '*', ...normalized.slice(4)].join(' '), - ] - : [normalized.join(' ')]; - if (!patterns.every((pattern) => validate(pattern))) { + weekCount === 8 + ? [[...normalized.slice(0, 5), '*'].join(' ')] + : dayCount < 31 + ? [[...normalized.slice(0, 5), '*'].join(' '), weekdayPattern] + : [weekdayPattern]; + const valid = patterns.map((pattern) => validate(pattern)); + if (patterns.length === 2 && !valid[0] && valid[1]) { + // A date branch can be impossible for all chosen months while the weekday + // branch still runs. Drop it only if its syntax is valid with any month. + if (validate([...normalized.slice(0, 4), '*', '*'].join(' '))) { + patterns.shift(); + valid.shift(); + } + } + if (!valid.every(Boolean)) { throw new Error('Unsupported cron schedule'); } const parsed = { diff --git a/docs/development/node-cron-migration.md b/docs/development/node-cron-migration.md index 1b32011e..8bbc3f13 100644 --- a/docs/development/node-cron-migration.md +++ b/docs/development/node-cron-migration.md @@ -10,6 +10,10 @@ Node 调度路径使用固定版本 `node-cron@4.6.0`,普通任务(含额外 - 日期与星期同时指定时保留 OR 语义,不采用 node-cron 默认的 AND。两个原生任务分别处理日期和星期,并按计划时间过滤交集,避免重复执行;保留旧解析器的月份全日期范围及全局 nth-week 限制。 - 原生任务先暂停创建并验证,再替换旧任务。构造失败时销毁已准备的实例并保留旧快照。 - 进程内迟到的触发通过 `execution:missed` 加入既有执行队列,记录补执行日志。并发和单任务积压上限仍由青龙队列控制;这不提供进程退出期间的持久化补跑或分布式 exactly-once 保证。 +- 保留 node-cron 已支持的列表、范围和名称,不调用 cron-parser 的 `stringify()` 重写整条表达式。先校验整条表达式,通过则跳过逐字段探测;失败时才检查并展开不支持的字段(如数字/名称起点步长)。保留原有 `?` 和 `#` 兼容处理,避免有效的 `6,12,18` 被压缩为不兼容的 `6/6`;日期/星期拆分后的最终校验仍然保留。 +- 星期步长范围以 `7` 结尾时,先按 cron-parser 的解析值展开。例如 `2-7/4` 在现有解析器中包含星期日,而 node-cron 原样执行会遗漏星期日;仅通过语法校验不足以保证执行含义一致。 +- 单月规则先剔除超出该月最大日期的值再计算日期数量,保留旧版日期/星期的通配计数语义。被判为通配的字段不再作为另一侧的 AND 约束,避免 `2-31 4 MON` 和包含 `L`、重复星期日的边界规则漏执行。 +- 日期与星期按 OR 拆分后,若日期分支在所有指定月份中都不可达,而星期分支有效,则仅保留星期分支;移除前仍检查日期分支在任意月份下语法有效。`31 2,4 MON` 因而可以执行,单独的 `31 2,4 *` 仍拒绝。 - 标准化结果使用上限 512 条的只读缓存,仅缓存语法,不缓存运行实例或下次执行时间。避免大批相同表达式重复解析导致恢复 RPC 超时。 - 回调异常记录日志,后续调度继续;取消时调用原生 `destroy()` 释放计时器和注册表。 - 持久化无效任务在恢复时跳过并记录日志,无效订阅不会产生未处理的启动拒绝。API、导入和执行使用同一套表达式校验。 @@ -31,6 +35,7 @@ Node 调度路径使用固定版本 `node-cron@4.6.0`,普通任务(含额外 - `test/fixtures/legacy-cron.json` 是迁移前使用 node-schedule 2.1.1 / cron-parser 4.9.0 保存的快照:269 条语法判定,18 组日历序列,每组 10 次执行。 - 真正的 node-cron 实例配合虚拟时钟验证日期/星期 OR、交集去重、闰年、L/#、8 秒迟到补执行、取消释放、回调拒绝和夏令时。 +- `test/fixtures/cron-calendar-boundaries.json` 保存 64 组规则与起点,以独立 cron-parser 4.9.0 / luxon 3.7.2 或公历计算产生预期结果,每组在 UTC、Asia/Shanghai 下各核对 4 次实际执行,包括 2100 年不闰和长周期第五个星期日的边界。时区子测试清理继承的 `NODE_TEST_CONTEXT` 并检查子测试输出,防止嵌套测试入口被跳过而出现假通过。 - 注册构造失败回滚、无效订阅隔离、空表达式立即运行,以及现有调度恢复/503/子进程测试覆盖接入流程。 - 隔离的正式 Debian 镜像使用实际编译产物验证 10,000 条有效任务和 2 条错误任务的恢复、300 个同时到期的 Python 脚本、并发上限 16 及 HTTP 健康检查。具体结果见本次测试报告;这是生产镜像隔离验证,不是线上长期运行证明。 @@ -47,3 +52,11 @@ Node 调度路径使用固定版本 `node-cron@4.6.0`,普通任务(含额外 完整回归 267 通过、3 项原有跳过;Node 20 的 10 项修复回归及 Alpine 镜像的 24 项兼容/宏测试通过。 额外使用 9,700 个不同表达式和 300 个同时到期任务验证恢复(另有 2 个无效任务),不再局限于共享两个表达式。300/300 脚本完成,无重复或遗漏,最大并发 16,270 个退出码 0、30 个预设退出码 3 均落库。恢复后健康检查 1,650/1,650 成功、任务列表 33/33 成功;启动恢复阶段另有 260 次健康检查失败,整批脚本耗时 60.27 秒。较大快照恢复仍需等待,不承诺启动阶段始终可用或任意规模都能在时限内恢复。 + +### 深度兼容性审计(2026-10-17) + +保持 cron-parser 5.4.0 和 node-cron 4.6.0,扫描 19,811 个表达式,覆盖六个字段的数字、前导零、列表、范围、起点步长、名称、L/#/? 及日期/星期组合。发现并修复星期步长末尾 7、单月日期裁剪、通配计数边界和不可达日期 OR 分支问题;47 个初始失败样例已保存为回归数据。 + +UTC 和 Asia/Shanghai 各比较 17,795 条规则的连续 4 次实际触发,共核对 142,360 个触发时刻,修复后无偏差、无构造失败。每个时区另外有 71 个通配 `#` 组合沿用已明确保留的旧适配规则,不与原解析器直接对照;22 个原版拒绝的日期输入仍拒绝,11 个参考迭代器触及搜索上限的长周期规则改用独立公历预期验证。 + +持久化回归覆盖 64 组规则与起点、两个时区,共 128 组日历用例;还保留 H、裸 `/N`、非标准 L 写法及不可达规则的拒绝行为。完整回归 745 通过、8 跳过,后端编译通过。本次为确定性语法与日历测试,不涉及生产环境压力或长期稳定性验证。 diff --git a/test/back/cron-calendar-boundaries.test.cjs b/test/back/cron-calendar-boundaries.test.cjs new file mode 100644 index 00000000..89de9685 --- /dev/null +++ b/test/back/cron-calendar-boundaries.test.cjs @@ -0,0 +1,68 @@ +const test = require('node:test'); +const assert = require('node:assert/strict'); +const { spawnSync } = require('node:child_process'); +const { createCronJob } = require('../../back/shared/cronScheduler'); +const { isValidCronSchedule } = require('../../back/shared/cronSchedule'); +const native = require('node-cron'); +const fixture = require('../fixtures/cron-calendar-boundaries.json'); + +// Each deployment gets a fresh timezone formatter cache in node-cron. +const timezone = process.env.QL_CRON_BOUNDARY_TEST_TZ; +if (!timezone) { + for (const tz of ['UTC', 'Asia/Shanghai']) { + test(`cron calendar boundary regressions in ${tz}`, () => { + const env = { ...process.env, TZ: tz, QL_CRON_BOUNDARY_TEST_TZ: tz }; + // A nested --test runner otherwise exits successfully without running tests. + delete env.NODE_TEST_CONTEXT; + const result = spawnSync( + process.execPath, + ['-r', 'ts-node/register/transpile-only', '--test', __filename], + { + env, + encoding: 'utf8', + }, + ); + assert.equal(result.status, 0, result.stdout + result.stderr); + assert.match(result.stdout, /calendar boundary:/); + }); + } +} else { + for (const { schedule, currentDate, next } of fixture.cases) { + test(`calendar boundary: ${schedule} from ${currentDate}`, async (t) => { + t.mock.timers.enable({ + apis: ['Date', 'setTimeout', 'setInterval'], + now: new Date(currentDate), + }); + const before = native.getTasks().size; + const actual = []; + const job = createCronJob( + schedule, + (date) => actual.push(date.toISOString()), + { name: 'calendar-boundary', logger: { warn() {}, error() {} } }, + ); + t.after(() => job.cancel()); + + for (const [index, expected] of next[timezone].entries()) { + t.mock.timers.tick(new Date(expected).getTime() - Date.now() - 1); + await new Promise(setImmediate); + assert.deepEqual(actual, next[timezone].slice(0, index)); + t.mock.timers.tick(1); + await new Promise(setImmediate); + assert.deepEqual(actual, next[timezone].slice(0, index + 1)); + } + job.cancel(); + assert.equal(native.getTasks().size, before); + }); + } + + test('unreachable dates and unsupported L spellings remain invalid', () => { + for (const schedule of [ + '0 0 31 2,4 *', + '0 0 31 2,4 0-7', + '0 0 1L 2,4 MON', + '0 0 * * 05L', + ]) { + assert.equal(isValidCronSchedule(schedule), false, schedule); + } + }); +} diff --git a/test/back/cron-macros.test.cjs b/test/back/cron-macros.test.cjs index af562f85..ae102863 100644 --- a/test/back/cron-macros.test.cjs +++ b/test/back/cron-macros.test.cjs @@ -7,10 +7,13 @@ const { isValidCronSchedule } = require('../../back/shared/cronSchedule'); if (!process.env.QL_MACRO_TEST_TZ) { for (const tz of ['UTC', 'Asia/Shanghai']) { test(`macro execution in ${tz}`, () => { + const env = { ...process.env, TZ: tz, QL_MACRO_TEST_TZ: tz }; + delete env.NODE_TEST_CONTEXT; const result = require('node:child_process').spawnSync(process.execPath, ['-r', 'ts-node/register/transpile-only', '--test', __filename], - { env: { ...process.env, TZ: tz, QL_MACRO_TEST_TZ: tz }, encoding: 'utf8' }); + { env, encoding: 'utf8' }); assert.equal(result.status, 0, result.stdout + result.stderr); + assert.match(result.stdout, /@annually fires at the expected local times/); }); } } diff --git a/test/back/cron-parser-compatibility.test.cjs b/test/back/cron-parser-compatibility.test.cjs new file mode 100644 index 00000000..eaf8c1a8 --- /dev/null +++ b/test/back/cron-parser-compatibility.test.cjs @@ -0,0 +1,189 @@ +const test = require('node:test'); +const assert = require('node:assert/strict'); +const { validate } = require('node-cron'); +const { + parseCronSchedule, + isValidCronSchedule, + getInvalidCronSchedules, +} = require('../../back/shared/cronSchedule'); +const { createCronJob } = require('../../back/shared/cronScheduler'); + +const logger = { warn() {}, error() {} }; + +// Issue #3099: cron-parser 5.4 stringifies these hours as 6/6, which +// node-cron rejects. Preserve supported fields and observe actual executions. +const cases = [ + ...[ + '0 6,12,18 * * *', + '0 0 6,12,18 * * *', + '0 6-18/6 * * *', + '0 6/6 * * *', + ].map((schedule) => ({ + schedule, + next: [ + '2026-10-17T06:00:00.000Z', + '2026-10-17T12:00:00.000Z', + '2026-10-17T18:00:00.000Z', + '2026-10-18T06:00:00.000Z', + ], + })), + { + schedule: '5,15,25 * * * *', + next: [ + '2026-10-17T00:05:00.000Z', + '2026-10-17T00:15:00.000Z', + '2026-10-17T00:25:00.000Z', + '2026-10-17T01:05:00.000Z', + ], + }, + { + schedule: '5,15,25 * * * * *', + next: [ + '2026-10-17T00:00:05.000Z', + '2026-10-17T00:00:15.000Z', + '2026-10-17T00:00:25.000Z', + '2026-10-17T00:01:05.000Z', + ], + }, + { + schedule: '5/20 * * * * *', + next: [ + '2026-10-17T00:00:05.000Z', + '2026-10-17T00:00:25.000Z', + '2026-10-17T00:00:45.000Z', + '2026-10-17T00:01:05.000Z', + ], + }, + { + schedule: '5/20 * * * *', + next: [ + '2026-10-17T00:05:00.000Z', + '2026-10-17T00:25:00.000Z', + '2026-10-17T00:45:00.000Z', + '2026-10-17T01:05:00.000Z', + ], + }, + { + schedule: '0 0 1/10,L * MON', + next: [ + '2026-10-19T00:00:00.000Z', + '2026-10-21T00:00:00.000Z', + '2026-10-26T00:00:00.000Z', + '2026-10-31T00:00:00.000Z', + '2026-11-01T00:00:00.000Z', + ], + }, + { + schedule: '0 0 1 JAN/3 *', + next: [ + '2027-01-01T00:00:00.000Z', + '2027-04-01T00:00:00.000Z', + '2027-07-01T00:00:00.000Z', + '2027-10-01T00:00:00.000Z', + ], + }, + { + schedule: '0 0 * * MON/2', + next: [ + '2026-10-18T00:00:00.000Z', + '2026-10-19T00:00:00.000Z', + '2026-10-21T00:00:00.000Z', + '2026-10-23T00:00:00.000Z', + '2026-10-25T00:00:00.000Z', + ], + }, + { + schedule: '0 0 * * 00#02', + next: [ + '2026-11-08T00:00:00.000Z', + '2026-12-13T00:00:00.000Z', + '2027-01-10T00:00:00.000Z', + '2027-02-14T00:00:00.000Z', + ], + }, + ...['*#2', '?#2'].map((weekday) => ({ + schedule: `0 0 * * ${weekday}`, + next: [ + '2026-10-18T00:00:00.000Z', + '2026-10-19T00:00:00.000Z', + '2026-10-20T00:00:00.000Z', + '2026-10-21T00:00:00.000Z', + ], + })), +]; + +test('supported fields retain their lists, ranges, names and special days', () => { + for (const [schedule, patterns] of [ + ['0 6,12,18 * * *', ['0 0 6,12,18 * * *']], + ['5,15,25 * * JAN-MAR MON-FRI', ['0 5,15,25 * * JAN-MAR MON-FRI']], + ['0 0 L JAN *', ['0 0 0 L JAN *']], + ['0 0 * JAN MON#2', ['0 0 0 * JAN MON#2']], + ['0 0 1,15 JAN MON', ['0 0 0 1,15 JAN *', '0 0 0 * JAN MON']], + ['0 0 ? * MON', ['0 0 0 * * MON']], + ]) { + assert.deepEqual(parseCronSchedule(schedule).patterns, patterns, schedule); + } +}); + +for (const [schedule, patterns, maxValidations] of [ + ['0 6,12,18 * * *', ['0 0 6,12,18 * * *'], 2], + ['0 0 1,15 JAN MON', ['0 0 0 1,15 JAN *', '0 0 0 * JAN MON'], 3], +]) { + test(`supported expressions skip field validation: ${schedule}`, () => { + const load = require('../helpers/load-security-module.cjs'); + const validations = []; + const isolated = load('back/shared/cronSchedule.ts', { + 'node-cron': { + validate(pattern) { + validations.push(pattern); + return validate(pattern); + }, + }, + }); + + assert.deepEqual(isolated.parseCronSchedule(schedule).patterns, patterns); + assert.ok(validations.length <= maxValidations, validations.join('\n')); + assert.equal(validations[0], `0 ${schedule}`); + }); +} + +for (const { schedule, next } of cases) { + test(`cron-parser normalization preserves executions: ${schedule}`, async (t) => { + const previousTZ = process.env.TZ; + process.env.TZ = 'UTC'; + t.after(() => { + if (previousTZ === undefined) delete process.env.TZ; + else process.env.TZ = previousTZ; + }); + t.mock.timers.enable({ + apis: ['Date', 'setTimeout', 'setInterval'], + now: new Date('2026-10-17T00:00:00Z'), + }); + + assert.equal(isValidCronSchedule(schedule), true); + assert.ok(parseCronSchedule(schedule).patterns.every(validate)); + assert.deepEqual( + getInvalidCronSchedules({ + schedule, + extra_schedules: [{ schedule }], + }), + [], + ); + + const actual = []; + const job = createCronJob( + schedule, + (date) => actual.push(date.toISOString()), + { name: 'issue-3099', logger }, + ); + t.after(() => job.cancel()); + for (const [index, expected] of next.entries()) { + t.mock.timers.tick(new Date(expected).getTime() - Date.now() - 1); + await new Promise(setImmediate); + assert.deepEqual(actual, next.slice(0, index)); + t.mock.timers.tick(1); + await new Promise(setImmediate); + assert.deepEqual(actual, next.slice(0, index + 1)); + } + }); +} diff --git a/test/fixtures/cron-calendar-boundaries.json b/test/fixtures/cron-calendar-boundaries.json new file mode 100644 index 00000000..0cc6f295 --- /dev/null +++ b/test/fixtures/cron-calendar-boundaries.json @@ -0,0 +1,1168 @@ +{ + "source": "cron-parser 4.9.0 / luxon 3.7.2 reference sequences; Gregorian calendar checks where the legacy iterator exceeds its search limit", + "cases": [ + { + "schedule": "0 0 0 * * 2-7/4", + "currentDate": "2026-12-31T23:59:58Z", + "next": { + "UTC": [ + "2027-01-02T00:00:00.000Z", + "2027-01-03T00:00:00.000Z", + "2027-01-05T00:00:00.000Z", + "2027-01-09T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-01-01T16:00:00.000Z", + "2027-01-02T16:00:00.000Z", + "2027-01-04T16:00:00.000Z", + "2027-01-08T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 * * 5-7/7", + "currentDate": "2024-02-27T23:59:58Z", + "next": { + "UTC": [ + "2024-03-01T00:00:00.000Z", + "2024-03-03T00:00:00.000Z", + "2024-03-08T00:00:00.000Z", + "2024-03-10T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2024-02-29T16:00:00.000Z", + "2024-03-02T16:00:00.000Z", + "2024-03-07T16:00:00.000Z", + "2024-03-09T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 * * 6-7/8", + "currentDate": "2026-12-31T23:59:58Z", + "next": { + "UTC": [ + "2027-01-02T00:00:00.000Z", + "2027-01-03T00:00:00.000Z", + "2027-01-09T00:00:00.000Z", + "2027-01-10T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-01-01T16:00:00.000Z", + "2027-01-02T16:00:00.000Z", + "2027-01-08T16:00:00.000Z", + "2027-01-09T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 01 * 6-7/8", + "currentDate": "2024-02-27T23:59:58Z", + "next": { + "UTC": [ + "2024-03-01T00:00:00.000Z", + "2024-03-02T00:00:00.000Z", + "2024-03-03T00:00:00.000Z", + "2024-03-09T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2024-02-29T16:00:00.000Z", + "2024-03-01T16:00:00.000Z", + "2024-03-02T16:00:00.000Z", + "2024-03-08T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 20-54/15 0 * * 6-7/8", + "currentDate": "2024-02-27T23:59:58Z", + "next": { + "UTC": [ + "2024-03-02T00:20:00.000Z", + "2024-03-02T00:35:00.000Z", + "2024-03-02T00:50:00.000Z", + "2024-03-03T00:20:00.000Z" + ], + "Asia/Shanghai": [ + "2024-03-01T16:20:00.000Z", + "2024-03-01T16:35:00.000Z", + "2024-03-01T16:50:00.000Z", + "2024-03-02T16:20:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 18-22/28 * 5-7/7", + "currentDate": "2026-04-29T23:59:58Z", + "next": { + "UTC": [ + "2026-05-01T00:00:00.000Z", + "2026-05-03T00:00:00.000Z", + "2026-05-08T00:00:00.000Z", + "2026-05-10T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2026-04-30T16:00:00.000Z", + "2026-05-02T16:00:00.000Z", + "2026-05-07T16:00:00.000Z", + "2026-05-09T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 45,54 0 20/5 * 2-7/4", + "currentDate": "2096-02-28T23:59:58Z", + "next": { + "UTC": [ + "2096-03-03T00:45:00.000Z", + "2096-03-03T00:54:00.000Z", + "2096-03-04T00:45:00.000Z", + "2096-03-04T00:54:00.000Z" + ], + "Asia/Shanghai": [ + "2096-03-02T16:45:00.000Z", + "2096-03-02T16:54:00.000Z", + "2096-03-03T16:45:00.000Z", + "2096-03-03T16:54:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2,27 * 6-7/8", + "currentDate": "2026-04-29T23:59:58Z", + "next": { + "UTC": [ + "2026-05-02T00:00:00.000Z", + "2026-05-03T00:00:00.000Z", + "2026-05-09T00:00:00.000Z", + "2026-05-10T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2026-05-01T16:00:00.000Z", + "2026-05-02T16:00:00.000Z", + "2026-05-08T16:00:00.000Z", + "2026-05-09T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 23-25 * 2-7/4", + "currentDate": "2024-02-27T23:59:58Z", + "next": { + "UTC": [ + "2024-03-02T00:00:00.000Z", + "2024-03-03T00:00:00.000Z", + "2024-03-05T00:00:00.000Z", + "2024-03-09T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2024-03-01T16:00:00.000Z", + "2024-03-02T16:00:00.000Z", + "2024-03-04T16:00:00.000Z", + "2024-03-08T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 06 * 6-7/8", + "currentDate": "2026-04-29T23:59:58Z", + "next": { + "UTC": [ + "2026-05-02T00:00:00.000Z", + "2026-05-03T00:00:00.000Z", + "2026-05-06T00:00:00.000Z", + "2026-05-09T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2026-05-01T16:00:00.000Z", + "2026-05-02T16:00:00.000Z", + "2026-05-05T16:00:00.000Z", + "2026-05-08T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 5,25 * 6-7/8", + "currentDate": "2026-04-29T23:59:58Z", + "next": { + "UTC": [ + "2026-05-02T00:00:00.000Z", + "2026-05-03T00:00:00.000Z", + "2026-05-05T00:00:00.000Z", + "2026-05-09T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2026-05-01T16:00:00.000Z", + "2026-05-02T16:00:00.000Z", + "2026-05-04T16:00:00.000Z", + "2026-05-08T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2-31 2 MON", + "currentDate": "2096-02-28T23:59:58Z", + "next": { + "UTC": [ + "2096-02-29T00:00:00.000Z", + "2097-02-02T00:00:00.000Z", + "2097-02-03T00:00:00.000Z", + "2097-02-04T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2097-02-01T16:00:00.000Z", + "2097-02-02T16:00:00.000Z", + "2097-02-03T16:00:00.000Z", + "2097-02-04T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2-31 2 MON#2", + "currentDate": "2024-02-27T23:59:58Z", + "next": { + "UTC": [ + "2025-02-08T00:00:00.000Z", + "2025-02-09T00:00:00.000Z", + "2025-02-10T00:00:00.000Z", + "2025-02-11T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2025-02-07T16:00:00.000Z", + "2025-02-08T16:00:00.000Z", + "2025-02-09T16:00:00.000Z", + "2025-02-10T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2-31 2 5L", + "currentDate": "2026-04-29T23:59:58Z", + "next": { + "UTC": [ + "2027-02-02T00:00:00.000Z", + "2027-02-03T00:00:00.000Z", + "2027-02-04T00:00:00.000Z", + "2027-02-05T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-02-01T16:00:00.000Z", + "2027-02-02T16:00:00.000Z", + "2027-02-03T16:00:00.000Z", + "2027-02-04T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2-31 4 MON", + "currentDate": "2026-04-29T23:59:58Z", + "next": { + "UTC": [ + "2026-04-30T00:00:00.000Z", + "2027-04-02T00:00:00.000Z", + "2027-04-03T00:00:00.000Z", + "2027-04-04T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-04-01T16:00:00.000Z", + "2027-04-02T16:00:00.000Z", + "2027-04-03T16:00:00.000Z", + "2027-04-04T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2-31 4 MON#2", + "currentDate": "2026-12-31T23:59:58Z", + "next": { + "UTC": [ + "2027-04-08T00:00:00.000Z", + "2027-04-09T00:00:00.000Z", + "2027-04-10T00:00:00.000Z", + "2027-04-11T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-04-07T16:00:00.000Z", + "2027-04-08T16:00:00.000Z", + "2027-04-09T16:00:00.000Z", + "2027-04-10T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2-31 4 5L", + "currentDate": "2096-02-28T23:59:58Z", + "next": { + "UTC": [ + "2096-04-02T00:00:00.000Z", + "2096-04-03T00:00:00.000Z", + "2096-04-04T00:00:00.000Z", + "2096-04-05T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2096-04-01T16:00:00.000Z", + "2096-04-02T16:00:00.000Z", + "2096-04-03T16:00:00.000Z", + "2096-04-04T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2-31 APR MON", + "currentDate": "2096-02-28T23:59:58Z", + "next": { + "UTC": [ + "2096-04-02T00:00:00.000Z", + "2096-04-03T00:00:00.000Z", + "2096-04-04T00:00:00.000Z", + "2096-04-05T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2096-04-01T16:00:00.000Z", + "2096-04-02T16:00:00.000Z", + "2096-04-03T16:00:00.000Z", + "2096-04-04T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2-31 APR MON#2", + "currentDate": "2024-02-27T23:59:58Z", + "next": { + "UTC": [ + "2024-04-08T00:00:00.000Z", + "2024-04-09T00:00:00.000Z", + "2024-04-10T00:00:00.000Z", + "2024-04-11T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2024-04-07T16:00:00.000Z", + "2024-04-08T16:00:00.000Z", + "2024-04-09T16:00:00.000Z", + "2024-04-10T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2-31 APR 5L", + "currentDate": "2026-04-29T23:59:58Z", + "next": { + "UTC": [ + "2026-04-30T00:00:00.000Z", + "2027-04-02T00:00:00.000Z", + "2027-04-03T00:00:00.000Z", + "2027-04-04T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-04-01T16:00:00.000Z", + "2027-04-02T16:00:00.000Z", + "2027-04-03T16:00:00.000Z", + "2027-04-04T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 3-31 2 MON", + "currentDate": "2026-04-29T23:59:58Z", + "next": { + "UTC": [ + "2027-02-01T00:00:00.000Z", + "2027-02-03T00:00:00.000Z", + "2027-02-04T00:00:00.000Z", + "2027-02-05T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-01-31T16:00:00.000Z", + "2027-02-02T16:00:00.000Z", + "2027-02-03T16:00:00.000Z", + "2027-02-04T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 3-31 2 MON#2", + "currentDate": "2026-12-31T23:59:58Z", + "next": { + "UTC": [ + "2027-02-08T00:00:00.000Z", + "2027-02-09T00:00:00.000Z", + "2027-02-10T00:00:00.000Z", + "2027-02-11T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-02-07T16:00:00.000Z", + "2027-02-08T16:00:00.000Z", + "2027-02-09T16:00:00.000Z", + "2027-02-10T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 3-31 2 5L", + "currentDate": "2096-02-28T23:59:58Z", + "next": { + "UTC": [ + "2096-02-29T00:00:00.000Z", + "2097-02-03T00:00:00.000Z", + "2097-02-04T00:00:00.000Z", + "2097-02-05T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2097-02-02T16:00:00.000Z", + "2097-02-03T16:00:00.000Z", + "2097-02-04T16:00:00.000Z", + "2097-02-05T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2-30 2 MON", + "currentDate": "2096-02-28T23:59:58Z", + "next": { + "UTC": [ + "2096-02-29T00:00:00.000Z", + "2097-02-02T00:00:00.000Z", + "2097-02-03T00:00:00.000Z", + "2097-02-04T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2097-02-01T16:00:00.000Z", + "2097-02-02T16:00:00.000Z", + "2097-02-03T16:00:00.000Z", + "2097-02-04T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2-30 2 MON#2", + "currentDate": "2024-02-27T23:59:58Z", + "next": { + "UTC": [ + "2025-02-08T00:00:00.000Z", + "2025-02-09T00:00:00.000Z", + "2025-02-10T00:00:00.000Z", + "2025-02-11T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2025-02-07T16:00:00.000Z", + "2025-02-08T16:00:00.000Z", + "2025-02-09T16:00:00.000Z", + "2025-02-10T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2-30 2 5L", + "currentDate": "2026-04-29T23:59:58Z", + "next": { + "UTC": [ + "2027-02-02T00:00:00.000Z", + "2027-02-03T00:00:00.000Z", + "2027-02-04T00:00:00.000Z", + "2027-02-05T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-02-01T16:00:00.000Z", + "2027-02-02T16:00:00.000Z", + "2027-02-03T16:00:00.000Z", + "2027-02-04T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2-31,L 2 0-6", + "currentDate": "2026-12-31T23:59:58Z", + "next": { + "UTC": [ + "2027-02-01T00:00:00.000Z", + "2027-02-02T00:00:00.000Z", + "2027-02-03T00:00:00.000Z", + "2027-02-04T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-01-31T16:00:00.000Z", + "2027-02-01T16:00:00.000Z", + "2027-02-02T16:00:00.000Z", + "2027-02-03T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2-31,L 4 0-6", + "currentDate": "2024-02-27T23:59:58Z", + "next": { + "UTC": [ + "2024-04-01T00:00:00.000Z", + "2024-04-02T00:00:00.000Z", + "2024-04-03T00:00:00.000Z", + "2024-04-04T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2024-03-31T16:00:00.000Z", + "2024-04-01T16:00:00.000Z", + "2024-04-02T16:00:00.000Z", + "2024-04-03T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2-31,L 2,4 0-6", + "currentDate": "2026-12-31T23:59:58Z", + "next": { + "UTC": [ + "2027-02-01T00:00:00.000Z", + "2027-02-02T00:00:00.000Z", + "2027-02-03T00:00:00.000Z", + "2027-02-04T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-01-31T16:00:00.000Z", + "2027-02-01T16:00:00.000Z", + "2027-02-02T16:00:00.000Z", + "2027-02-03T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2-31,L 2,4,6,9,11 MON", + "currentDate": "2026-04-29T23:59:58Z", + "next": { + "UTC": [ + "2026-06-01T00:00:00.000Z", + "2026-06-08T00:00:00.000Z", + "2026-06-15T00:00:00.000Z", + "2026-06-22T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2026-05-31T16:00:00.000Z", + "2026-06-07T16:00:00.000Z", + "2026-06-14T16:00:00.000Z", + "2026-06-21T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2-31,L 2,4,6,9,11 0-6", + "currentDate": "2024-02-27T23:59:58Z", + "next": { + "UTC": [ + "2024-02-28T00:00:00.000Z", + "2024-02-29T00:00:00.000Z", + "2024-04-01T00:00:00.000Z", + "2024-04-02T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2024-02-28T16:00:00.000Z", + "2024-03-31T16:00:00.000Z", + "2024-04-01T16:00:00.000Z", + "2024-04-02T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2-31,L APR 0-6", + "currentDate": "2026-12-31T23:59:58Z", + "next": { + "UTC": [ + "2027-04-01T00:00:00.000Z", + "2027-04-02T00:00:00.000Z", + "2027-04-03T00:00:00.000Z", + "2027-04-04T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-03-31T16:00:00.000Z", + "2027-04-01T16:00:00.000Z", + "2027-04-02T16:00:00.000Z", + "2027-04-03T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2-31,L 2,3 MON", + "currentDate": "2026-04-29T23:59:58Z", + "next": { + "UTC": [ + "2027-02-01T00:00:00.000Z", + "2027-02-08T00:00:00.000Z", + "2027-02-15T00:00:00.000Z", + "2027-02-22T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-01-31T16:00:00.000Z", + "2027-02-07T16:00:00.000Z", + "2027-02-14T16:00:00.000Z", + "2027-02-21T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2-31,L 2,3 0-6", + "currentDate": "2024-02-27T23:59:58Z", + "next": { + "UTC": [ + "2024-02-28T00:00:00.000Z", + "2024-02-29T00:00:00.000Z", + "2024-03-01T00:00:00.000Z", + "2024-03-02T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2024-02-28T16:00:00.000Z", + "2024-02-29T16:00:00.000Z", + "2024-03-01T16:00:00.000Z", + "2024-03-02T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 2-31,L * 0-6", + "currentDate": "2026-12-31T23:59:58Z", + "next": { + "UTC": [ + "2027-01-01T00:00:00.000Z", + "2027-01-02T00:00:00.000Z", + "2027-01-03T00:00:00.000Z", + "2027-01-04T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-01-01T16:00:00.000Z", + "2027-01-02T16:00:00.000Z", + "2027-01-03T16:00:00.000Z", + "2027-01-04T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 1 * 0-7,0", + "currentDate": "2026-12-31T23:59:58Z", + "next": { + "UTC": [ + "2027-01-01T00:00:00.000Z", + "2027-01-02T00:00:00.000Z", + "2027-01-03T00:00:00.000Z", + "2027-01-04T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-01-01T16:00:00.000Z", + "2027-01-02T16:00:00.000Z", + "2027-01-03T16:00:00.000Z", + "2027-01-04T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 1,15 * 0-7,0", + "currentDate": "2096-02-28T23:59:58Z", + "next": { + "UTC": [ + "2096-02-29T00:00:00.000Z", + "2096-03-01T00:00:00.000Z", + "2096-03-02T00:00:00.000Z", + "2096-03-03T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2096-02-29T16:00:00.000Z", + "2096-03-01T16:00:00.000Z", + "2096-03-02T16:00:00.000Z", + "2096-03-03T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 1 * 0,1,2,3,4,5,6,7,0", + "currentDate": "2026-12-31T23:59:58Z", + "next": { + "UTC": [ + "2027-01-01T00:00:00.000Z", + "2027-01-02T00:00:00.000Z", + "2027-01-03T00:00:00.000Z", + "2027-01-04T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-01-01T16:00:00.000Z", + "2027-01-02T16:00:00.000Z", + "2027-01-03T16:00:00.000Z", + "2027-01-04T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 1,15 * 0,1,2,3,4,5,6,7,0", + "currentDate": "2096-02-28T23:59:58Z", + "next": { + "UTC": [ + "2096-02-29T00:00:00.000Z", + "2096-03-01T00:00:00.000Z", + "2096-03-02T00:00:00.000Z", + "2096-03-03T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2096-02-29T16:00:00.000Z", + "2096-03-01T16:00:00.000Z", + "2096-03-02T16:00:00.000Z", + "2096-03-03T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 31 2,4 MON", + "currentDate": "2026-04-29T23:59:58Z", + "next": { + "UTC": [ + "2027-02-01T00:00:00.000Z", + "2027-02-08T00:00:00.000Z", + "2027-02-15T00:00:00.000Z", + "2027-02-22T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-01-31T16:00:00.000Z", + "2027-02-07T16:00:00.000Z", + "2027-02-14T16:00:00.000Z", + "2027-02-21T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 31 2,4 MON#2", + "currentDate": "2026-12-31T23:59:58Z", + "next": { + "UTC": [ + "2027-02-08T00:00:00.000Z", + "2027-04-12T00:00:00.000Z", + "2028-02-14T00:00:00.000Z", + "2028-04-10T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-02-07T16:00:00.000Z", + "2027-04-11T16:00:00.000Z", + "2028-02-13T16:00:00.000Z", + "2028-04-09T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 31 2,4 5L", + "currentDate": "2096-02-28T23:59:58Z", + "next": { + "UTC": [ + "2096-04-27T00:00:00.000Z", + "2097-02-22T00:00:00.000Z", + "2097-04-26T00:00:00.000Z", + "2098-02-28T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2096-04-26T16:00:00.000Z", + "2097-02-21T16:00:00.000Z", + "2097-04-25T16:00:00.000Z", + "2098-02-27T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 31 2,4 0-6", + "currentDate": "2024-02-27T23:59:58Z", + "next": { + "UTC": [ + "2024-02-28T00:00:00.000Z", + "2024-02-29T00:00:00.000Z", + "2024-04-01T00:00:00.000Z", + "2024-04-02T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2024-02-28T16:00:00.000Z", + "2024-03-31T16:00:00.000Z", + "2024-04-01T16:00:00.000Z", + "2024-04-02T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 31 2,4,6,9,11 MON", + "currentDate": "2096-02-28T23:59:58Z", + "next": { + "UTC": [ + "2096-04-02T00:00:00.000Z", + "2096-04-09T00:00:00.000Z", + "2096-04-16T00:00:00.000Z", + "2096-04-23T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2096-04-01T16:00:00.000Z", + "2096-04-08T16:00:00.000Z", + "2096-04-15T16:00:00.000Z", + "2096-04-22T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 31 2,4,6,9,11 MON#2", + "currentDate": "2024-02-27T23:59:58Z", + "next": { + "UTC": [ + "2024-04-08T00:00:00.000Z", + "2024-06-10T00:00:00.000Z", + "2024-09-09T00:00:00.000Z", + "2024-11-11T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2024-04-07T16:00:00.000Z", + "2024-06-09T16:00:00.000Z", + "2024-09-08T16:00:00.000Z", + "2024-11-10T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 31 2,4,6,9,11 5L", + "currentDate": "2026-04-29T23:59:58Z", + "next": { + "UTC": [ + "2026-06-26T00:00:00.000Z", + "2026-09-25T00:00:00.000Z", + "2026-11-27T00:00:00.000Z", + "2027-02-26T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2026-06-25T16:00:00.000Z", + "2026-09-24T16:00:00.000Z", + "2026-11-26T16:00:00.000Z", + "2027-02-25T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 31 2,4,6,9,11 0-6", + "currentDate": "2026-12-31T23:59:58Z", + "next": { + "UTC": [ + "2027-02-01T00:00:00.000Z", + "2027-02-02T00:00:00.000Z", + "2027-02-03T00:00:00.000Z", + "2027-02-04T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-01-31T16:00:00.000Z", + "2027-02-01T16:00:00.000Z", + "2027-02-02T16:00:00.000Z", + "2027-02-03T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 * * MON-7/4", + "currentDate": "2026-12-31T23:59:58Z", + "next": { + "UTC": [ + "2027-01-01T00:00:00.000Z", + "2027-01-03T00:00:00.000Z", + "2027-01-04T00:00:00.000Z", + "2027-01-08T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-01-02T16:00:00.000Z", + "2027-01-03T16:00:00.000Z", + "2027-01-07T16:00:00.000Z", + "2027-01-09T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 * * 02-007/04", + "currentDate": "2026-12-31T23:59:58Z", + "next": { + "UTC": [ + "2027-01-02T00:00:00.000Z", + "2027-01-03T00:00:00.000Z", + "2027-01-05T00:00:00.000Z", + "2027-01-09T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-01-01T16:00:00.000Z", + "2027-01-02T16:00:00.000Z", + "2027-01-04T16:00:00.000Z", + "2027-01-08T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 1 * 0,0,0,0,0,0,0,0", + "currentDate": "2026-12-31T23:59:58Z", + "next": { + "UTC": [ + "2027-01-01T00:00:00.000Z", + "2027-02-01T00:00:00.000Z", + "2027-03-01T00:00:00.000Z", + "2027-04-01T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2027-01-31T16:00:00.000Z", + "2027-02-28T16:00:00.000Z", + "2027-03-31T16:00:00.000Z", + "2027-04-30T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 29 2 *", + "currentDate": "2099-12-31T23:59:58Z", + "next": { + "UTC": [ + "2104-02-29T00:00:00.000Z", + "2108-02-29T00:00:00.000Z", + "2112-02-29T00:00:00.000Z", + "2116-02-29T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2104-02-28T16:00:00.000Z", + "2108-02-28T16:00:00.000Z", + "2112-02-28T16:00:00.000Z", + "2116-02-28T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 31 2,4 MON-7/4", + "currentDate": "2026-01-01T00:00:00Z", + "next": { + "UTC": [ + "2026-02-01T00:00:00.000Z", + "2026-02-02T00:00:00.000Z", + "2026-02-06T00:00:00.000Z", + "2026-02-08T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2026-01-31T16:00:00.000Z", + "2026-02-01T16:00:00.000Z", + "2026-02-05T16:00:00.000Z", + "2026-02-07T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 2-31,L 4 MON-7/4", + "currentDate": "2026-01-01T00:00:00Z", + "next": { + "UTC": [ + "2026-04-03T00:00:00.000Z", + "2026-04-05T00:00:00.000Z", + "2026-04-06T00:00:00.000Z", + "2026-04-10T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2026-04-02T16:00:00.000Z", + "2026-04-04T16:00:00.000Z", + "2026-04-05T16:00:00.000Z", + "2026-04-09T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 * 2 7#5", + "currentDate": "2024-02-27T23:59:58Z", + "oracle": "Gregorian calendar: February 29 on Sunday; legacy iterator exceeds its search limit", + "next": { + "UTC": [ + "2032-02-29T00:00:00.000Z", + "2060-02-29T00:00:00.000Z", + "2088-02-29T00:00:00.000Z", + "2128-02-29T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2032-02-28T16:00:00.000Z", + "2060-02-28T16:00:00.000Z", + "2088-02-28T16:00:00.000Z", + "2128-02-28T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 ? 2 7#5", + "currentDate": "2026-04-29T23:59:58Z", + "oracle": "Gregorian calendar: February 29 on Sunday; legacy iterator exceeds its search limit", + "next": { + "UTC": [ + "2032-02-29T00:00:00.000Z", + "2060-02-29T00:00:00.000Z", + "2088-02-29T00:00:00.000Z", + "2128-02-29T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2032-02-28T16:00:00.000Z", + "2060-02-28T16:00:00.000Z", + "2088-02-28T16:00:00.000Z", + "2128-02-28T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 1 2 7#5", + "currentDate": "2026-12-31T23:59:58Z", + "oracle": "Gregorian calendar: February 29 on Sunday; legacy iterator exceeds its search limit", + "next": { + "UTC": [ + "2032-02-29T00:00:00.000Z", + "2060-02-29T00:00:00.000Z", + "2088-02-29T00:00:00.000Z", + "2128-02-29T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2032-02-28T16:00:00.000Z", + "2060-02-28T16:00:00.000Z", + "2088-02-28T16:00:00.000Z", + "2128-02-28T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 15 2 7#5", + "currentDate": "2096-02-28T23:59:58Z", + "oracle": "Gregorian calendar: February 29 on Sunday; legacy iterator exceeds its search limit", + "next": { + "UTC": [ + "2128-02-29T00:00:00.000Z", + "2156-02-29T00:00:00.000Z", + "2184-02-29T00:00:00.000Z", + "2224-02-29T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2128-02-28T16:00:00.000Z", + "2156-02-28T16:00:00.000Z", + "2184-02-28T16:00:00.000Z", + "2224-02-28T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 28 2 7#5", + "currentDate": "2024-02-27T23:59:58Z", + "oracle": "Gregorian calendar: February 29 on Sunday; legacy iterator exceeds its search limit", + "next": { + "UTC": [ + "2032-02-29T00:00:00.000Z", + "2060-02-29T00:00:00.000Z", + "2088-02-29T00:00:00.000Z", + "2128-02-29T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2032-02-28T16:00:00.000Z", + "2060-02-28T16:00:00.000Z", + "2088-02-28T16:00:00.000Z", + "2128-02-28T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 1-28 2 7#5", + "currentDate": "2096-02-28T23:59:58Z", + "oracle": "Gregorian calendar: February 29 on Sunday; legacy iterator exceeds its search limit", + "next": { + "UTC": [ + "2128-02-29T00:00:00.000Z", + "2156-02-29T00:00:00.000Z", + "2184-02-29T00:00:00.000Z", + "2224-02-29T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2128-02-28T16:00:00.000Z", + "2156-02-28T16:00:00.000Z", + "2184-02-28T16:00:00.000Z", + "2224-02-28T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 1-29 2 7#5", + "currentDate": "2026-04-29T23:59:58Z", + "oracle": "Gregorian calendar: February 29 on Sunday; legacy iterator exceeds its search limit", + "next": { + "UTC": [ + "2032-02-29T00:00:00.000Z", + "2060-02-29T00:00:00.000Z", + "2088-02-29T00:00:00.000Z", + "2128-02-29T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2032-02-28T16:00:00.000Z", + "2060-02-28T16:00:00.000Z", + "2088-02-28T16:00:00.000Z", + "2128-02-28T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 1-30 2 7#5", + "currentDate": "2096-02-28T23:59:58Z", + "oracle": "Gregorian calendar: February 29 on Sunday; legacy iterator exceeds its search limit", + "next": { + "UTC": [ + "2128-02-29T00:00:00.000Z", + "2156-02-29T00:00:00.000Z", + "2184-02-29T00:00:00.000Z", + "2224-02-29T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2128-02-28T16:00:00.000Z", + "2156-02-28T16:00:00.000Z", + "2184-02-28T16:00:00.000Z", + "2224-02-28T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 1-31 2 7#5", + "currentDate": "2026-04-29T23:59:58Z", + "oracle": "Gregorian calendar: February 29 on Sunday; legacy iterator exceeds its search limit", + "next": { + "UTC": [ + "2032-02-29T00:00:00.000Z", + "2060-02-29T00:00:00.000Z", + "2088-02-29T00:00:00.000Z", + "2128-02-29T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2032-02-28T16:00:00.000Z", + "2060-02-28T16:00:00.000Z", + "2088-02-28T16:00:00.000Z", + "2128-02-28T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 1/3 2 7#5", + "currentDate": "2024-02-27T23:59:58Z", + "oracle": "Gregorian calendar: February 29 on Sunday; legacy iterator exceeds its search limit", + "next": { + "UTC": [ + "2032-02-29T00:00:00.000Z", + "2060-02-29T00:00:00.000Z", + "2088-02-29T00:00:00.000Z", + "2128-02-29T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2032-02-28T16:00:00.000Z", + "2060-02-28T16:00:00.000Z", + "2088-02-28T16:00:00.000Z", + "2128-02-28T16:00:00.000Z" + ] + } + }, + { + "schedule": "0 0 0 1,15 2 7#5", + "currentDate": "2026-12-31T23:59:58Z", + "oracle": "Gregorian calendar: February 29 on Sunday; legacy iterator exceeds its search limit", + "next": { + "UTC": [ + "2032-02-29T00:00:00.000Z", + "2060-02-29T00:00:00.000Z", + "2088-02-29T00:00:00.000Z", + "2128-02-29T00:00:00.000Z" + ], + "Asia/Shanghai": [ + "2032-02-28T16:00:00.000Z", + "2060-02-28T16:00:00.000Z", + "2088-02-28T16:00:00.000Z", + "2128-02-28T16:00:00.000Z" + ] + } + } + ] +}