Skip to main content
Bun 内置支持注册操作系统级别的 cron 任务以及解析 cron 表达式。

快速开始

解析 cron 表达式以查找下一次匹配的时间:
// 下一个工作日上午 9:30 UTC
const next = Bun.cron.parse("30 9 * * MON-FRI");
console.log(next); // => 2025-01-20T09:30:00.000Z
注册一个按计划运行脚本的 cron 任务:
await Bun.cron("./worker.ts", "30 2 * * MON", "weekly-report");

Bun.cron.parse()

解析 cron 表达式并返回下一个匹配的 UTC Date
const next = Bun.cron.parse("*/15 * * * *");
console.log(next); // => 下一个一刻钟边界

参数

参数类型描述
expressionstring5 字段的 cron 表达式或预定义别名
relativeDateDate | number搜索的起始点(默认为 Date.now()

返回值

Date | null — 下一个匹配的 UTC 时间,如果在约 4 年内没有匹配(例如 2 月 30 日)则返回 null

链式调用

重复调用 parse() 以获取即将到来的时间序列:
const from = Date.UTC(2025, 0, 15, 10, 0, 0);

const first = Bun.cron.parse("0 * * * *", from);
console.log(first); // => 2025-01-15T11:00:00.000Z

const second = Bun.cron.parse("0 * * * *", first);
console.log(second); // => 2025-01-15T12:00:00.000Z

Cron 表达式语法

标准 5 字段格式:minute hour day-of-month month day-of-week
字段取值特殊字符
Minute059* , - /
Hour023* , - /
Day of month131* , - /
Month112JANDEC* , - /
Day of week07SUNSAT* , - /

特殊字符

字符说明示例
*所有值* * * * * — 每分钟
,列表1,15 * * * * — 第 1 和第 15 分钟
-范围9-17 * * * * — 第 9 到 17 分钟
/步长*/15 * * * * — 每 15 分钟

命名值

月份和星期字段接受不区分大小写的名称:
// 3 字母缩写
Bun.cron.parse("0 9 * * MON-FRI"); // 工作日
Bun.cron.parse("0 0 1 JAN,JUN *"); // 一月和六月

// 完整名称
Bun.cron.parse("0 9 * * Monday-Friday");
Bun.cron.parse("0 0 1 January *");
在星期字段中,07 都表示星期日。

预定义别名

别名等效表达式说明
@yearly / @annually0 0 1 1 *每年一次(1 月 1 日)
@monthly0 0 1 * *每月一次(每月 1 日)
@weekly0 0 * * 0每周一次(周日)
@daily / @midnight0 0 * * *每天一次(午夜)
@hourly0 * * * *每小时一次
const next = Bun.cron.parse("@daily");
console.log(next); // => 下一个午夜 UTC

日期和星期字段的交互

同时指定了 day-of-month 和 day-of-week(两者都不是 *)时,只要任一条件为真,表达式就会匹配。这遵循 POSIX cron 标准。
// 在每月 15 日或每周五触发
Bun.cron.parse("0 0 15 * FRI");
当只指定了一个(另一个是 *)时,仅使用该字段进行匹配。

Bun.cron()

注册一个操作系统级别的 cron 任务,按计划运行 JavaScript/TypeScript 模块。
await Bun.cron("./worker.ts", "30 2 * * MON", "weekly-report");

参数

参数类型描述
pathstring脚本路径(相对于调用者解析)
schedulestringCron 表达式或别名
titlestring唯一任务标识符(字母数字、连字符、下划线)
使用相同的 title 重新注册会就地覆盖现有任务 —— 旧的计划被替换,而非重复添加。
await Bun.cron("./worker.ts", "0 * * * *", "my-job"); // 每小时
await Bun.cron("./worker.ts", "*/15 * * * *", "my-job"); // 替换为:每 15 分钟

scheduled() 处理程序

注册的脚本必须导出一个带有 scheduled() 方法的默认对象,遵循 Cloudflare Workers Cron Triggers API
worker.ts
export default {
  scheduled(controller: Bun.CronController) {
    console.log(controller.cron); // "30 2 * * 1"
    console.log(controller.type); // "scheduled"
    console.log(controller.scheduledTime); // 1737340201847(调用时的 Date.now())
  },
};
处理程序可以是 async。Bun 会等待返回的 promise 完成后再退出。

各平台的工作原理

Linux

Bun 使用 crontab 注册任务。每个任务作为一行存储在您用户的 crontab 中,上方带有 # bun-cron: <title> 标记注释。 crontab 条目如下所示:
<schedule> '<bun-path>' run --cron-title=<title> --cron-period='<schedule>' '<script-path>'
当 cron 守护进程触发任务时,Bun 会导入您的模块并调用 scheduled() 处理程序。 查看已注册的任务:
crontab -l
日志: 在 Linux 上,cron 输出进入系统日志。使用以下命令查看:
# 基于 systemd 的系统(Ubuntu、Fedora、Arch 等)
journalctl -u cron       # 或在某些发行版上为 crond
journalctl -u cron --since "1 hour ago"

# 基于 syslog 的系统(旧系统)
grep CRON /var/log/syslog
要将 stdout/stderr 捕获到文件,可直接在 crontab 条目中重定向输出,或在 scheduled() 处理程序内添加日志记录。 无需代码手动卸载:
# 编辑您的 crontab 并删除 "# bun-cron: <title>" 注释
# 及其下方的命令行
crontab -e

# 或通过过滤一次性删除所有 bun cron 任务:
crontab -l | grep -v "# bun-cron:" | grep -v "\-\-cron-title=" | crontab -

macOS

Bun 使用 launchd 注册任务。每个任务作为 plist 文件安装在:
~/Library/LaunchAgents/bun.cron.<title>.plist
plist 使用 StartCalendarInterval 定义计划。支持包含范围、列表或步长的复杂模式 —— Bun 通过笛卡尔积将它们展开为多个 StartCalendarInterval 字典。 查看已注册的任务:
launchctl list | grep bun.cron
日志: stdout 和 stderr 写入到:
/tmp/bun.cron.<title>.stdout.log
/tmp/bun.cron.<title>.stderr.log
例如,一个标题为 weekly-report 的任务:
cat /tmp/bun.cron.weekly-report.stdout.log
tail -f /tmp/bun.cron.weekly-report.stderr.log
无需代码手动卸载:
# 从 launchd 卸载任务
launchctl bootout gui/$(id -u)/bun.cron.<title>

# 删除 plist 文件
rm ~/Library/LaunchAgents/bun.cron.<title>.plist

# 示例:标题为 "weekly-report" 的任务:
launchctl bootout gui/$(id -u)/bun.cron.weekly-report
rm ~/Library/LaunchAgents/bun.cron.weekly-report.plist

Windows

Bun 使用基于 XML 任务定义的 Windows Task Scheduler,使用 CalendarTrigger 元素和 Repetition 模式。 大多数 cron 表达式都得到完全支持,包括 @daily@weekly@monthly@yearly、范围 (1-5)、列表 (1,15)、命名日/月和日期模式。

用户上下文

任务使用 S4U (Service-for-User) 登录类型注册,即使用户未登录也会以注册用户的身份运行任务 —— 与 Linux crontab 行为一致。不存储密码。 TCP/IP 网络(fetch()、HTTP、WebSocket、数据库连接)正常工作。唯一的限制是 S4U 任务无法访问 Windows 认证的网络资源(SMB 文件共享、映射驱动器、Kerberos/NTLM 服务)。 在无法解析当前用户 Security Identifier (SID) 的无头服务器和 CI 环境中 —— 例如由 NSSM 或类似工具创建的服务账户 —— Bun.cron() 将失败并返回解释该问题的错误。要解决此问题,可以将 Bun 作为普通用户账户运行,或使用 schtasks /create /xml <file> /tn <name> /ru SYSTEM /f 手动创建计划任务。

触发器限制

Windows Task Scheduler 对每个任务的触发器数量限制为 48 个CalendarTrigger 元素的 maxOccurs="48")。 某些在 Linux 和 macOS 上有效的 cron 表达式在 Windows 上会超出此限制。当模式超出限制时,Bun.cron() 将返回错误消息拒绝该表达式。
在所有平台上都有效的表达式:
模式触发器策略数量
*/5 * * * *使用 Repetition (PT5M) 的单一触发器1
*/15 * * * *使用 Repetition (PT15M) 的单一触发器1
0 9 * * MON-FRI每个工作日一个 CalendarTrigger5
0,30 9-17 * * *2 分钟 × 9 小时18
@daily, @weekly, @monthly, @yearly单一触发器1
在 Windows 上失败的表达式(但在 Linux 和 macOS 上有效):
模式原因触发器数量
*/7 * * * *9 个分钟值 × 24 小时216
*/8 * * * *8 个分钟值 × 24 小时192
*/9 * * * *7 个分钟值 × 24 小时168
*/11 * * * *6 个分钟值 × 24 小时144
*/13 * * * *5 个分钟值 × 24 小时120
*/15 * * 6 *月份限制阻止了 Repetition:4 × 2496
0,30 * 15 * FRIOR 分割使触发器翻倍:2 × 24 × 296
关键因素是表达式是否可以使用 Repetition 间隔(单一触发器),或者必须展开为独立的 CalendarTrigger 元素。能整除 60的分钟步长(*/1*/2*/3*/4*/5*/6*/10*/12*/15*/20*/30)使用 Repetition,无论其他字段如何都能工作。不能整除 60 的步长(*/7*/8*/9*/11*/13 等)必须展开,而在 24 小时全天运行的情况下,数量很快会超过 48。 要解决此问题,请简化表达式或限制小时范围:
// ❌ 在 Windows 上失败:*/7 配合全天 = 216 个触发器
await Bun.cron("./job.ts", "*/7 * * * *", "my-job");

// ✅ 有效:限制为特定小时(9 个值 × 5 小时 = 45 个触发器)
await Bun.cron("./job.ts", "*/7 9-13 * * *", "my-job");

// ✅ 有效:改用 60 的约数(Repetition,1 个触发器)
await Bun.cron("./job.ts", "*/5 * * * *", "my-job");

Windows 容器

Bun.cron() 在 Windows Docker 容器中不受支持。Task Scheduler 服务不在 servercorenanoserver 镜像中运行。对于容器化工作负载,请使用进程内调度器。
查看已注册的任务:
schtasks /query /tn "bun-cron-<title>"

# 列出所有 bun cron 任务
schtasks /query | findstr "bun-cron-"
无需代码手动卸载:
schtasks /delete /tn "bun-cron-<title>" /f

# 示例:
schtasks /delete /tn "bun-cron-weekly-report" /f
或者打开任务计划程序(taskschd.msc),找到名为 bun-cron-<title> 的任务,右键单击并删除它。

Bun.cron.remove()

通过其标题移除先前注册的定时任务。适用于所有平台。
await Bun.cron.remove("weekly-report");
这会撤销 Bun.cron() 的操作:
平台remove() 的操作
Linux编辑 crontab 以移除该条目及其标记注释
macOS运行 launchctl bootout 并删除 plist 文件
Windows运行 schtasks /delete 以移除计划任务
移除一个不存在的任务会无错误地完成。