Class CronExpression

java.lang.Object
cloud.opencode.base.cron.CronExpression
All Implemented Interfaces:
Serializable, TemporalAdjuster

public final class CronExpression extends Object implements Serializable, TemporalAdjuster
Cron Expression - Full-Featured Cron Expression Parser and Evaluator Cron表达式 - 功能完整的Cron表达式解析器和评估器

Parses and evaluates cron expressions with full standard Unix cron semantics, including OR logic for day-of-month/day-of-week, special characters (L, W, #), name aliases (MON-FRI, JAN-DEC), and macro support (@daily, @yearly, etc.).

解析和评估具有完整标准Unix Cron语义的Cron表达式, 包括月中日/星期几的OR逻辑、特殊字符(L、W、#)、 名称别名(MON-FRI、JAN-DEC)和宏支持(@daily、@yearly等)。

Features | 主要功能:

Usage Examples | 使用示例:

// Every day at 10:30 | 每天10:30
CronExpression.parse("30 10 * * *")

// Every weekday at 9:00 | 工作日9:00
CronExpression.parse("0 9 * * MON-FRI")

// 15th of month OR every Monday at noon | 15号或周一中午
CronExpression.parse("0 12 15 * MON")

// Last day of every month at 18:00 | 每月最后一天18:00
CronExpression.parse("0 18 L * *")

// 3rd Friday of every month at 10:00 | 每月第三个周五10:00
CronExpression.parse("0 10 * * FRI#3")

// Next 5 executions | 下5次执行时间
expr.nextExecutions(ZonedDateTime.now(), 5)

// Human-readable | 人类可读描述
expr.describe()  // "At 09:00, Monday through Friday"

Performance | 性能特性:

  • Parse: O(fields) - one-time cost - 解析: O(字段数) - 一次性开销
  • nextExecution: O(1) amortized via field-jumping - 下次执行: 均摊O(1),字段跳跃优化

Security | 安全性:

  • Thread-safe: Yes (immutable after construction) - 线程安全: 是(构造后不可变)
  • Null-safe: Yes (rejects null inputs) - 空值安全: 是
Since:
JDK 25, opencode-base-cron V1.0.0
Author:
Leon Soo www.LeonSoo.com
See Also:
  • Method Details

    • parse

      public static CronExpression parse(String expression)
      Parse a cron expression 解析Cron表达式

      Supports 5-field, 6-field formats and macros (@yearly, @daily, etc.).

      支持5字段、6字段格式和宏(@yearly、@daily 等)。

      Parameters:
      expression - the cron expression | Cron表达式
      Returns:
      the parsed expression | 解析后的表达式
      Throws:
      OpenCronException - if the expression is invalid | 如果表达式无效
    • matches

      public boolean matches(ZonedDateTime time)
      Check if a time matches this cron expression 检查时间是否匹配此Cron表达式
      Parameters:
      time - the time to check | 要检查的时间
      Returns:
      true if the time matches | 如果匹配返回true
    • nextExecution

      public ZonedDateTime nextExecution(ZonedDateTime from)
      Gets the next execution time after the given time 获取给定时间之后的下次执行时间
      Parameters:
      from - the start time | 开始时间
      Returns:
      the next execution time, or null if none within 4 years | 下次执行时间,如果4年内没有则返回null
    • nextExecution

      public ZonedDateTime nextExecution(ZonedDateTime from, int maxYears)
      Gets the next execution time with configurable search window 获取可配置搜索窗口的下次执行时间
      Parameters:
      from - the start time | 开始时间
      maxYears - the maximum years to search (1-100) | 最大搜索年数(1-100)
      Returns:
      the next execution time, or null if none within maxYears | 下次执行时间
      Throws:
      IllegalArgumentException - if maxYears is not in range 1-100 | 如果maxYears不在1-100范围内
    • nextExecutions

      public List<ZonedDateTime> nextExecutions(ZonedDateTime from, int count)
      Gets the next N execution times after the given time 获取给定时间之后的下N次执行时间
      Parameters:
      from - the start time | 开始时间
      count - the number of executions | 执行次数
      Returns:
      the list of execution times | 执行时间列表
      Throws:
      IllegalArgumentException - if count is not positive | 如果count不是正数
    • previousExecution

      public ZonedDateTime previousExecution(ZonedDateTime from)
      Gets the previous execution time before the given time 获取给定时间之前的上次执行时间
      Parameters:
      from - the reference time | 参考时间
      Returns:
      the previous execution time, or null if none within 4 years | 上次执行时间,如果4年内没有则返回null
    • previousExecution

      public ZonedDateTime previousExecution(ZonedDateTime from, int maxYears)
      Gets the previous execution time with configurable search window 获取可配置搜索窗口的上次执行时间
      Parameters:
      from - the reference time | 参考时间
      maxYears - the maximum years to search (1-100) | 最大搜索年数(1-100)
      Returns:
      the previous execution time, or null if none within maxYears | 上次执行时间
      Throws:
      IllegalArgumentException - if maxYears is not in range 1-100 | 如果maxYears不在1-100范围内
    • previousExecutions

      public List<ZonedDateTime> previousExecutions(ZonedDateTime from, int count)
      Gets the previous N execution times before the given time 获取给定时间之前的前N次执行时间
      Parameters:
      from - the reference time | 参考时间
      count - the number of executions | 执行次数
      Returns:
      the list of execution times (newest first) | 执行时间列表(最新在前)
      Throws:
      IllegalArgumentException - if count is not positive | 如果count不是正数
    • timeToNextExecution

      public Duration timeToNextExecution(ZonedDateTime from)
      Gets the duration until the next execution 获取距下次执行的时间间隔
      Parameters:
      from - the reference time | 参考时间
      Returns:
      the duration to next execution, or null if none within search window | 距下次执行的Duration
    • timeFromLastExecution

      public Duration timeFromLastExecution(ZonedDateTime from)
      Gets the duration since the last execution 获取距上次执行的时间间隔
      Parameters:
      from - the reference time | 参考时间
      Returns:
      the duration from last execution, or null if none within search window | 距上次执行的Duration
    • countExecutionsBetween

      public long countExecutionsBetween(ZonedDateTime from, ZonedDateTime to)
      Counts executions between two times 计算两个时间点之间的执行次数

      The maximum count is capped at 1,000,000 to prevent excessive computation. If the count reaches this limit, 1,000,000 is returned (which may be an undercount).

      最大计数上限为1,000,000以防止过度计算。如果达到此限制,返回1,000,000(可能是不完整的计数)。

      Parameters:
      from - the start time (exclusive) | 开始时间(不包含)
      to - the end time (inclusive) | 结束时间(包含)
      Returns:
      the count of executions (capped at 1,000,000) | 执行次数(上限1,000,000)
      Throws:
      IllegalArgumentException - if from is not before to | 如果from不在to之前
    • executionsBetween

      public List<ZonedDateTime> executionsBetween(ZonedDateTime from, ZonedDateTime to)
      Lists all executions between two times 列出两个时间点之间的所有执行时间
      Parameters:
      from - the start time (exclusive) | 开始时间(不包含)
      to - the end time (inclusive) | 结束时间(包含)
      Returns:
      unmodifiable list of execution times | 不可修改的执行时间列表
      Throws:
      IllegalArgumentException - if from is not before to | 如果from不在to之前
    • executionsBetween

      public List<ZonedDateTime> executionsBetween(ZonedDateTime from, ZonedDateTime to, int limit)
      Lists executions between two times with a limit 列出两个时间点之间的执行时间(带限制)
      Parameters:
      from - the start time (exclusive) | 开始时间(不包含)
      to - the end time (inclusive) | 结束时间(包含)
      limit - the maximum number of results (1-1_000_000) | 最大结果数
      Returns:
      unmodifiable list of execution times | 不可修改的执行时间列表
      Throws:
      IllegalArgumentException - if from is not before to, or limit is out of range | 如果from不在to之前或limit超出范围
    • isEquivalentTo

      public boolean isEquivalentTo(CronExpression other)
      Checks if this expression is structurally equivalent to another 检查此表达式是否与另一个结构等价

      Compares internal parsed state (BitSets, special fields, and field count), not the original expression strings. Note: a 5-field expression and a 6-field expression with second=0 produce the same schedule, but are considered structurally different (hasSeconds differs).

      比较内部解析状态(BitSet、特殊字段和字段数),而非原始表达式字符串。 注意:5字段表达式和秒=0的6字段表达式产生相同调度,但被视为结构不同(hasSeconds不同)。

      Parameters:
      other - the other expression | 另一个表达式
      Returns:
      true if equivalent schedules | 如果调度等价返回true
    • explain

      public CronExplanation explain(ZonedDateTime from)
      Gets a comprehensive explanation for debugging 获取用于调试的综合解释信息

      The estimated interval is Duration.ZERO when fewer than 2 upcoming executions can be found within the search window.

      当搜索窗口内找到的即将执行次数少于2次时,预估间隔为 Duration.ZERO

      Parameters:
      from - the reference time for next executions | 用于计算下次执行的参考时间
      Returns:
      the explanation | 解释信息
    • stream

      public Stream<ZonedDateTime> stream(ZonedDateTime from)
      Returns a lazy, ordered stream of future execution times 返回一个惰性的、有序的未来执行时间流

      The stream is computed lazily — each element is calculated on demand. Use .limit(), .takeWhile(), .filter() etc. to control iteration.

      流是惰性计算的——每个元素按需计算。 使用 .limit().takeWhile().filter() 等控制迭代。

      Examples | 示例:

      // Next 10 executions | 下10次执行
      expr.stream(now).limit(10).toList();
      
      // All executions before deadline | deadline之前的所有执行
      expr.stream(now).takeWhile(t -> t.isBefore(deadline)).toList();
      
      Parameters:
      from - the start time (exclusive) | 开始时间(不包含)
      Returns:
      an ordered, sequential stream of execution times | 有序的顺序执行时间流
    • reverseStream

      public Stream<ZonedDateTime> reverseStream(ZonedDateTime from)
      Returns a lazy, ordered stream of past execution times (newest first) 返回一个惰性的、有序的过去执行时间流(最新在前)

      The stream is computed lazily — each element is calculated on demand.

      流是惰性计算的——每个元素按需计算。

      Parameters:
      from - the reference time (exclusive) | 参考时间(不包含)
      Returns:
      an ordered, sequential stream of past execution times | 有序的顺序过去执行时间流
    • nextExecution

      public ZonedDateTime nextExecution(ZonedDateTime from, Predicate<ZonedDateTime> filter)
      Gets the next execution time that satisfies a filter 获取满足过滤条件的下次执行时间

      Skips execution times that do not pass the filter predicate. Stops after 10,000 skipped candidates to prevent infinite loops.

      跳过不满足过滤谓词的执行时间。跳过10,000个候选后停止以防止无限循环。

      Examples | 示例:

      // Skip weekends | 跳过周末
      expr.nextExecution(now, t -> t.getDayOfWeek().getValue() <= 5);
      
      // Skip holidays | 跳过节假日
      Set<LocalDate> holidays = Set.of(LocalDate.of(2026, 1, 1));
      expr.nextExecution(now, t -> !holidays.contains(t.toLocalDate()));
      
      Parameters:
      from - the start time (exclusive) | 开始时间(不包含)
      filter - the filter predicate — returns true to accept, false to skip | 过滤谓词
      Returns:
      the next matching execution time, or null if none found | 下次匹配的执行时间
    • previousExecution

      public ZonedDateTime previousExecution(ZonedDateTime from, Predicate<ZonedDateTime> filter)
      Gets the previous execution time that satisfies a filter 获取满足过滤条件的上次执行时间

      Skips execution times that do not pass the filter predicate. Stops after 10,000 skipped candidates to prevent infinite loops.

      跳过不满足过滤谓词的执行时间。跳过10,000个候选后停止以防止无限循环。

      Parameters:
      from - the reference time (exclusive) | 参考时间(不包含)
      filter - the filter predicate — returns true to accept, false to skip | 过滤谓词
      Returns:
      the previous matching execution time, or null if none found | 上次匹配的执行时间
    • nextOverlap

      public ZonedDateTime nextOverlap(CronExpression other, ZonedDateTime from)
      Finds the next time both this and another expression fire simultaneously 查找此表达式与另一个表达式同时触发的下一个时间

      Iterates forward through both schedules to find the first overlap. Returns null if no overlap is found within 4 years.

      正向遍历两个调度以找到第一个重叠时间。如果4年内找不到重叠则返回null。

      Parameters:
      other - the other cron expression | 另一个Cron表达式
      from - the start time (exclusive) | 开始时间(不包含)
      Returns:
      the next overlapping execution time, or null | 下一个重叠执行时间
    • hasOverlapBetween

      public boolean hasOverlapBetween(CronExpression other, ZonedDateTime from, ZonedDateTime to)
      Checks if this and another expression have overlapping executions in a time range 检查此表达式与另一个表达式在时间范围内是否有重叠执行
      Parameters:
      other - the other cron expression | 另一个Cron表达式
      from - the start time (exclusive) | 开始时间(不包含)
      to - the end time (inclusive) | 结束时间(包含)
      Returns:
      true if there is at least one overlapping execution | 如果存在重叠返回true
    • adjustInto

      public Temporal adjustInto(Temporal temporal)
      Adjusts the temporal to the next execution time of this cron expression 将时间调节为此Cron表达式的下次执行时间

      Enables idiomatic usage with java.time:

      支持 java.time 的惯用写法:

      ZonedDateTime next = ZonedDateTime.now().with(cronExpr);
      
      Specified by:
      adjustInto in interface TemporalAdjuster
      Parameters:
      temporal - the temporal to adjust | 要调节的时间
      Returns:
      the next execution time | 下次执行时间
      Throws:
      DateTimeException - if no execution found within search window | 如果搜索窗口内找不到执行时间
    • describe

      public String describe()
      Gets a human-readable description of this expression 获取此表达式的人类可读描述
      Returns:
      the description in English | 英文描述
    • describe

      public String describe(Locale locale)
      Gets a human-readable description in the specified locale 获取指定语言的人类可读描述
      Parameters:
      locale - the locale (supports CHINESE/SIMPLIFIED_CHINESE for Chinese, others default to English) 语言(支持CHINESE/SIMPLIFIED_CHINESE返回中文,其他默认英文)
      Returns:
      the localized description | 本地化描述
    • getExpression

      public String getExpression()
      Gets the original expression string 获取原始表达式字符串
      Returns:
      the expression | 表达式
    • hasSeconds

      public boolean hasSeconds()
      Checks if this expression uses 6-field format (with seconds) 检查此表达式是否使用6字段格式(含秒)
      Returns:
      true if 6-field format | 如果是6字段格式返回true
    • equals

      public boolean equals(Object o)
      Overrides:
      equals in class Object
    • hashCode

      public int hashCode()
      Overrides:
      hashCode in class Object
    • toString

      public String toString()
      Overrides:
      toString in class Object