fix: 避免 cron 自动侦测无效时区导致定时任务相关异常#1531
Conversation
There was a problem hiding this comment.
Pull request overview
该 PR 针对部分运行环境(如 Intl.DateTimeFormat().resolvedOptions().timeZone 返回无效值 Etc/Unknown)导致 cron 计算下一次触发时间抛出 “ERROR: You specified an invalid date.” 的问题,通过引入“固定 UTC 偏移量时区(fixed offset zone)”的方式绕开 cron 内部的 IANA 时区自动侦测,从而提升定时任务在不同环境下的稳定性。这与 ScriptCat 的沙箱运行时定时任务启动路径直接相关(src/app/service/sandbox/runtime.ts)。
Changes:
- 在
src/pkg/utils/cron.ts新增基于Date#getTimezoneOffset()的本地 UTC offset 计算与 fixed offset zone 生成,并封装createCronJob()统一创建 CronJob(默认补齐 fixed offset zone)。 - 将沙箱运行时定时任务创建从
new CronJob(...)切换为createCronJob(...),避免依赖 cron 内部自动时区探测。 - 新增
cron工具单测(Vitest),覆盖 offset/zone 转换、下一次执行时间计算,以及createCronJob()的默认与参数保留行为。
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/pkg/utils/cron.ts | 新增 UTC offset / fixed offset zone 工具方法与 createCronJob(),并调整 Luxon DateTime 构造来源以规避无效 IANA timezone 自动侦测。 |
| src/pkg/utils/cron.test.ts | 新增单元测试,使用 mock 的 getTimezoneOffset() 覆盖关键逻辑与默认行为,避免依赖真实机器时区。 |
| src/app/service/sandbox/runtime.ts | 定时任务创建改用 createCronJob(),确保未显式传入时区时也能稳定启动。 |
| * 另外,CronJobParams 的类型定义里 timeZone 和 utcOffset 是互斥的。 | ||
| * 所以这里需要把原来的 timeZone / utcOffset 字段解构掉, | ||
| * 再重新组装成只包含 utcOffset 的参数对象。 |
| const cron = createCronJob({ | ||
| cronTime: cronExpr, | ||
| onTick, | ||
| start: false, // 不使用 start: true。下面手動執行。 |
|
这个 PR 方向是对的,但目前还不能合并。补充并修正一下之前的判断: 已确认的问题1.
Intl.DateTimeFormat = function () {
return { resolvedOptions: () => ({ timeZone: "Etc/Unknown" }) };
};
new CronTime("* * * * *").sendAt();这条无显式 timezone 的 cron 路径( 不过要修正之前「管理页/安装页会继续崩」的说法: 2. #1326 的白屏其实已经被这个 PR 修掉了 #1326 的实际现象是 options 页白屏( 3. 无条件替换成 fixed offset 会丢 DST(这是更该阻塞的点) 现在 建议调整
我跑过 |
|
晚點修改一下 |
Checklist / 检查清单
Description / 描述
本 PR 修复了部分运行环境下 cron 定时任务启动失败的问题。
在某些环境中,
cron内部通过Intl.DateTimeFormat().resolvedOptions().timeZone自动侦测到的 IANA timezone 可能是无效值,例如Etc/Unknown。这会导致CronTime#sendAt()/CronTime#getNextDateFrom()在计算下一次执行时间时生成无效日期,并抛出以下错误:本次改动新增了 cron 工具方法,在调用方没有显式传入
timeZone或utcOffset时,自动使用当前运行环境的固定 UTC offset zone,例如UTC+08:00,从而避免进入 cron 内部的 IANA timezone 自动侦测逻辑。主要改动:
getLocalUtcOffset(),用于从 JavaScriptDate#getTimezoneOffset()获取当前运行环境的本地 UTC offset。toUtcOffsetZone(),将 offset 分钟数转换成 Luxon / cron 可识别的 fixed offset zone 字符串,例如UTC+08:00。getLuxonDate(),用于 debug 和稳定获取 cron 内部 Luxon DateTime 构造来源,避免依赖运行环境自动侦测 timezone。createCronJob(),统一创建CronJob,在未显式指定 timezone 时自动补充 fixed offset zone。new CronJob(cronExpr, onTick)改为createCronJob({ cronTime, onTick, start: false })。createCronJob()的默认行为和参数保留逻辑。注意:
fixed offset zone 是固定偏移量,不等同于完整 IANA timezone。它不会自动跟随 DST / 夏令时变化。本 PR 的目标是避免无效 IANA timezone 自动侦测导致定时任务异常。
Screenshots / 截图
无 UI 变更。