Search by

yunadmin / cron-schedule

yunadmin

PHP 的 CRON 表达式解析与运行时间计算库:支持秒级精度、不可达检测与表达式规范化

Package info

github.com/haoziliao/cron-schedule

pkg:composer/yunadmin/cron-schedule

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-08-27 15:26 UTC

This package is auto-updated.

Last update: 2026-08-27 16:21:05 UTC


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