yunadmin / cron-schedule
PHP 的 CRON 表达式解析与运行时间计算库:支持秒级精度、不可达检测与表达式规范化
v1.0.0
2026-08-27 15:26 UTC
Requires
- php: >=8.0
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
PHP CRON 表达式解析与运行时间计算库,支持 PHP 8.0+。
功能特性
- 同时支持 5 段(分 时 日 月 周)与 6 段(秒 分 时 日 月 周,含秒级精度)表达式
- 支持所有标准语法:
* , / - ? L W #,以及月份/星期名称字面量(JAN–DEC、MON–SUN) - 支持宏别名:
@yearly/@annually/@monthly/@weekly/@daily/@hourly - 全程使用
DateTimeImmutable,不会改动调用方传入的 DateTime 对象 - 不可达表达式检测:如
0 0 30 2 *(二月没有 30 天)会抛出UnreachableExpressionException - 清晰的异常体系:
InvalidExpressionException(表达式非法)、UnreachableExpressionException(表达式不可达) - 表达式规范化(
normalize())与人类可读描述(describe()) - 对夏令时(DST)切换和时区做了安全处理
安装
composer require yunadmin/cron-schedule
本项目已通过 PSR-4 自动加载(
YunAdmin\CronSchedule\→src/)。 若作为本地包使用,可在主项目的composer.json中配置:{ "repositories": [ { "type": "path", "url": "vendor/yunadmin/cron-schedule" } ], "require": { "yunadmin/cron-schedule": "*" } }然后执行
composer dump-autoload。
快速开始
use YunAdmin\CronSchedule\CronExpression; // 解析一个 5 段表达式 $cron = CronExpression::parse('0 0 * * *'); // 下次运行时间(返回 DateTimeImmutable) $next = $cron->getNextRunDate(); echo $next->format('Y-m-d H:i:s'); // 上一次运行时间 $prev = $cron->getPreviousRunDate(); // 当前时间是否到期(命中) if ($cron->isDue()) { // 执行任务…… }
使用方式详解
1. 解析表达式
use YunAdmin\CronSchedule\CronExpression; // 5 段(默认) $cron = CronExpression::parse('30 8 * * 1-5'); // 工作日 08:30 // 6 段(含秒):第三个参数传 true $sec = CronExpression::parse('*/20 * * * * *', null, true); // 每 20 秒一次 // 也可以在已有表达式上切换为秒级模式(返回新实例,不影响原对象) $cronWithSeconds = $cron->withSeconds();
宏别名会被自动展开:
CronExpression::parse('@daily'); // 等价于 0 0 * * * CronExpression::parse('@hourly'); // 等价于 0 * * * * CronExpression::parse('@weekly'); // 等价于 0 0 * * 0
2. 计算运行时间
// 下一次(默认从当前时间算) $next = $cron->getNextRunDate(); // 从指定时间算下一次 $next = $cron->getNextRunDate('2026-08-24 08:07:00'); // 跳过前 n 次命中:取第 n 个之后的运行时间 $next3rd = $cron->getNextRunDate('now', 2); // 跳过前 2 次,返回第 3 次 // 允许返回与基准时间相同的结果(即基准时间本身即命中) $next = $cron->getNextRunDate('now', 0, true); // 指定时区(不影响传入的 DateTime 对象) $next = $cron->getNextRunDate('now', 0, false, 'Asia/Shanghai');
// 上一次运行时间(参数含义与 getNextRunDate 一致) $prev = $cron->getPreviousRunDate(); $prev = $cron->getPreviousRunDate('2026-08-24 08:07:00');
// 批量获取多个运行时间 $dates = $cron->getMultipleRunDates(5); // 返回包含 5 个 DateTimeImmutable 的数组
3. 判断是否到期
// 判断表达式在「当前时间」是否命中 $cron->isDue(); // 判断指定时间是否命中 $cron->isDue('2026-08-27 00:00:00'); $cron->isDue(new \DateTimeImmutable('2026-08-27 00:00:00'));
4. 校验表达式
// 语法是否合法(返回 bool) CronExpression::isValidExpression('0 0 * * *'); // true CronExpression::isValidExpression('99 0 * * *'); // false CronExpression::isValidExpression('0 0 0 * * *', true);// 6 段需传 true
5. 表达式规范化和可读描述
use YunAdmin\CronSchedule\ExpressionFormatter; $fmt = new ExpressionFormatter(); // 规范化:列表按语义排序、字面量统一大写,便于比较 $fmt->normalize(CronExpression::parse('0 0 * DEC,JAN *')); // 输出 "0 0 * JAN,DEC *" // 人类可读描述 $fmt->describe(CronExpression::parse('@daily')); // "At 0:0 every day" $fmt->describe(CronExpression::parse('0 0 15W * *')); // "At 0:0 on the nearest weekday to day 15 of the month"
6. 异常与错误处理
use YunAdmin\CronSchedule\Exception\InvalidExpressionException; use YunAdmin\CronSchedule\Exception\UnreachableExpressionException; try { CronExpression::parse('0 0 30 2 *')->getNextRunDate(); // 二月没有 30 天 } catch (UnreachableExpressionException $e) { // 表达式永远不可能匹配任何日期 echo $e->getMessage(); } try { CronExpression::parse('99 0 * * *'); } catch (InvalidExpressionException $e) { // 表达式语法或取值非法 echo $e->getMessage(); }
表达式语法速查
| 符号 | 含义 | 示例 |
|---|---|---|
* |
任意值 | * 每分钟/每小时 |
, |
枚举多个值 | 1,15,30 |
- |
范围 | 9-17(9 点到 17 点) |
/ |
步长 | */15(每 15 分钟)、0-30/10 |
? |
不指定(日与周二选一留空) | 0 0 ? * 1 |
L |
最后一天 / 最后一个星期几 | 0 0 L * *、0 0 * * 5L |
W |
最近的工作日 | 0 0 15W * * |
# |
第 n 个星期几(n 取 1–5) | 0 0 * * 2#1(第一个星期二,编号 1=周一 … 7/0=周日) |
| 名称字面量 | 月份/星期 | JAN-DEC、MON-SUN |
字段顺序(5 段):分 时 日 月 周
字段顺序(6 段):秒 分 时 日 月 周
星期中
0与7均表示星期日;「日」与「星期」同时指定具体值时,采用「任一满足即可」的语义。
许可证
MIT