Cronsmith - 終極 Cron 表達(dá)式生成器和解析器

Cronsmith - 終極 Cron 表達(dá)式生成器和解析器

Cronsmith 是一個(gè)功能強(qiáng)大且多功能的 Java 工具庫(kù),旨在以直觀、面向?qū)ο蟮姆绞教幚?cron 表達(dá)式。它提供了一個(gè)高度靈活且用戶友好的 API,輕松生成、解析和調(diào)度基于 cron 的任務(wù)。

Cronsmith 為無(wú)縫集成而構(gòu)建,全面支持 Spring 和 Quartz 的 cron 表達(dá)式,確保與廣泛使用的調(diào)度框架兼容。此外,它通過(guò)引入高級(jí)模式(例如與 'L'(最后一天)和 'W'(工作日)組合的多個(gè)數(shù)字)擴(kuò)展了傳統(tǒng)的 cron 語(yǔ)法,提供了更高的調(diào)度精度和靈活性。

功能特性

1. 面向?qū)ο蟮?CronExpression構(gòu)建器

Cronsmith 允許開(kāi)發(fā)者以直觀、面向?qū)ο蟮姆绞綐?gòu)建復(fù)雜的 cron 表達(dá)式。這種方法便于自定義,避免了手動(dòng)拼接字符串的麻煩。

Example:

 
@Test
public void test1() {
        CronExpression cronExpression = new CronBuilder()
            .everyMinute(5)
            .second(5)
            .andSecond(10)
            .toSecond(30);
        
        System.out.println(cronExpression.toString());
        assertEquals("5,10-30 */5 * * * ?", cronExpression.toString());
    }

@Test
public void test2() {
        CronExpression cronExpression = new CronBuilder()
            .everyMonth()
            .lastWeekday()
            .hour(10)
            .minute(1)
            .toMinute(15);
        
        System.out.println(cronExpression.toString());
        assertEquals("0 1-15 10 LW * ?", cronExpression.toString());
    }

@Test
public void test3() {
        CronExpression cronExpression = new CronBuilder()
            .year(2025)
            .toYear(2028)
            .everyMonth(2)
            .lastDay()
            .hour(12);
        
        System.out.println(cronExpression.toString());
        assertEquals("0 0 12 L */2 ? 2025-2028", cronExpression.toString());
}

@Test
public void test4() {
    CronExpression cronExpression = new CronBuilder()
        .everyYear(2026, 4)
        .everyMonth(5, 7, 1)
        .day(10).andDay(15).andDay(20).andLastDay(2)
        .everyHour(10, 15, 1)
        .at(10, 0)
        .andSecond(15).andSecond(30).andSecond(45);
    
    System.out.println(cronExpression.toString());
    assertEquals("0,15,30,45 10 10-15 10,15,20,L-2 MAY-JUL ? 2026/4",
            cronExpression.toString());
}

@Test
public void testK() {
        CronExpression cronExpression = new CronBuilder()
                .year(2025).toYear(2030).andYear(2035).toEnd(2)
                .everyMonth(2, 12, 2)
                .dayOfWeek(2, DayOfWeek.TUESDAY)
                .and(3, DayOfWeek.WEDNESDAY).andLastFri()
                .hour(2).andHour(3).andHour(4).toHour(17, 2)
                .minute(0).toMinute(12, 3).andMinute(15).toMinute(40, 2).andMinute(46)
                .andMinute(48).andMinute(50)
                .everySecond(5);
    
        System.out.println(cronExpression.toString());
        assertEquals("*/5 0-12/3,15-40/2,46,48,50 2,3,4-17/2 ? 2/2 TUE#2,WED#3,5L 2025-2030,2035/2",
                cronExpression.toString());
 }

2. Cron 表達(dá)式字符串解析與反向生成 CronExpression

Cronsmith 內(nèi)置了一個(gè)強(qiáng)大的解析器,基于 ANTLR 構(gòu)建,支持將已有的 cron 表達(dá)式字符串解析回結(jié)構(gòu)化的 CronExpression 對(duì)象。

Example:

@Test
public void test1() {
        String cron = "0 15 10 ? * MON-FRI";
        CronExpression cronExpression = CRON.parse(cron);
        System.out.println(cronExpression);
        assertEquals(cron, cronExpression.toString());
}

@Test
public void test2() {
        String cron = "0 10,20,30 9-17 L * ?";
        CronExpression cronExpression = CRON.parse(cron);
        System.out.println(cronExpression);
        assertEquals(cron, cronExpression.toString());
}

@Test
public void test3() {
        String cron = "1,3,5,7,9 3-30/3 12-16 ? * TUE#1";
        CronExpression cronExpression = CRON.parse(cron);
        System.out.println(cronExpression);
        assertEquals(cron, cronExpression.toString());
}

@Test
public void test4() {
    String cron = "5-30/7 0-12/3,15-45/2 2,3,4-17/2 ? JAN-JUL MON-THU/2 2025-2033";
    CronExpression cronExpression = CRON.parse(cron);
    System.out.println(cronExpression);
    assertEquals(cron, cronExpression.toString());
}

Parse Tree:

cron
    second
        secondField
            rangeWithStep
                5
                -
                30
                /
                7
    <missing SPACE>
    minute
        minuteField
            rangeWithStep
                0
                -
                12
                /
                3
        ,
        minuteField
            rangeWithStep
                15
                -
                45
                /
                2
    <missing SPACE>
    hour
        hourField
            2
        ,
        hourField
            3
        ,
        hourField
            rangeWithStep
                4
                -
                17
                /
                2
    <missing SPACE>
    dayOfMonth
        dayOfMonthField
            ?
    <missing SPACE>
    month
        monthField
            monthRange
                monthName
                    JAN
                -
                monthName
                    JUL
    <missing SPACE>
    dayOfWeek
        dayOfWeekField
            weekdayRangeWithStep
                dayOfWeekName
                    MON
                -
                dayOfWeekName
                    THU
                /
                2
    year
        yearField
            yearRange
                2025
                -
                2030
    <EOF>

Retrieve next date and times from <code>CronExpression</code>

public static void main(String[] args) {
   String cron = "0 1,3,5-20 12-16 1,3,20,LW MAR-SEP ? 2026/1";
   CronExpression cronExpression = CRON.parse(cron);
   int N = 1000; // Retrieve 1000 items
   DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
   cronExpression.consume(ldt -> {
      System.out.println(ldt.format(dtf));
   }, N);
}

// Console:
// 2026-03-01 12:01:00
// 2026-03-01 12:03:00
// 2026-03-01 12:05:00
// 2026-03-01 12:06:00
// 2026-03-01 12:07:00
// 2026-03-01 12:08:00
// 2026-03-01 12:09:00
// 2026-03-01 12:10:00
// 2026-03-01 12:11:00
// 2026-03-01 12:12:00
// 2026-03-01 12:13:00
// 2026-03-01 12:14:00
// 2026-03-01 12:15:00
// 2026-03-01 12:16:00
// 2026-03-01 12:17:00
// 2026-03-01 12:18:00
// 2026-03-01 12:19:00
// 2026-03-01 12:20:00
// 2026-03-01 13:01:00
// 2026-03-01 13:03:00
// 2026-03-01 13:05:00
// ...

3. 內(nèi)置調(diào)度器

Cronsmith 提供了內(nèi)置調(diào)度器,支持基于 cron 表達(dá)式執(zhí)行指定間隔的任務(wù),輕松實(shí)現(xiàn)高效的重復(fù)任務(wù)調(diào)度。

Example: 每 5 秒調(diào)度一次任務(wù)

private ScheduledExecutorService scheduledExecutorService;

@Before
public void start() {
    scheduledExecutorService =
                Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors() * 2);
}

@Test
public void testSchedulerAndRunTenTimes() {
    int N = 10; // Run 10 times
    final CountDownLatch latch = new CountDownLatch(N);
    final AtomicInteger counter = new AtomicInteger();
    
    CronFuture future = new CronBuilder()
        .everySecond(5)
        .scheduler(scheduledExecutorService)
        .setDebuged(false)
        .runTask(() -> {
            System.out.println("Run task_" + counter.incrementAndGet());
            latch.countDown();
        }, N);
    
    try {
        latch.await();
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
    
    future.cancel(true);
    assertTrue(future.isDone() && counter.get() == N);
}

@After
public void release() {
    scheduledExecutorService.shutdown();
}

4. 高級(jí)功能

支持指定年份的某一天

public static void main(String[] args) {
   int N = 1000;
   DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
   new CronBuilder().setZoneId(ZoneId.of("UTC")).year(2025).day(208).andDay(330).toLastDay()
                .at(12, 0).consume(ldt -> {
                    System.out.println(ldt.format(dtf));
                }, N);
}
// Console: 
// 2025-07-27 12:00:00
// 2025-11-26 12:00:00
// 2025-11-27 12:00:00
// 2025-11-28 12:00:00
// 2025-11-29 12:00:00
// 2025-11-30 12:00:00
// 2025-12-01 12:00:00
// 2025-12-02 12:00:00
// 2025-12-03 12:00:00
// 2025-12-04 12:00:00
// 2025-12-05 12:00:00
// 2025-12-06 12:00:00
// ...

支持指定年份的某周

public static void main(String[] args) {
    int N = 1000;
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    new CronBuilder().setZoneId(ZoneId.of("UTC")).everyYear().week(40).andWeek(45).Mon().toFri()
                .at(12, 0).consume(ldt -> {
                    System.out.println(ldt.format(dtf));
                }, N);
}
// Console: 
// 2025-09-29 12:00:00
// 2025-09-30 12:00:00
// 2025-10-01 12:00:00
// 2025-10-02 12:00:00
// 2025-10-03 12:00:00
// 2025-11-03 12:00:00
// 2025-11-04 12:00:00
// 2025-11-05 12:00:00
// 2025-11-06 12:00:00
// 2025-11-07 12:00:00
// 2026-09-28 12:00:00
// 2026-09-29 12:00:00
// 2026-09-30 12:00:00
// 2026-10-01 12:00:00
// 2026-10-02 12:00:00
// ...

安裝

支持 Jdk1.8 或更高版本

在 Maven 項(xiàng)目中添加以下依賴:

<dependency>
    <groupId>com.github.paganini2008</groupId>
    <artifactId>cronsmith</artifactId>
    <version>1.0.0-beta</version>
</dependency>

Gradle 項(xiàng)目中添加:

dependencies {
    implementation 'com.github.paganini2008:cronsmith:1.0.0'
}

快速開(kāi)始

  1. 在項(xiàng)目中添加 Cronsmith 依賴。
  2. 使用 CronBuilder 創(chuàng)建復(fù)雜的 cron 表達(dá)式。
  3. 通過(guò) CRON.parse() 解析已有的 cron 表達(dá)式。
  4. 使用內(nèi)置調(diào)度器調(diào)度任務(wù)。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容