Rust的cron表达式解析库cron-parser的使用,支持高性能定时任务调度与时间解析

Rust的cron表达式解析库cron-parser的使用,支持高性能定时任务调度与时间解析

cron-parser是一个用于解析cron表达式并支持时区的Rust库。

示例用法

use chrono::{TimeZone, Utc};
use chrono_tz::Europe::Lisbon;
use cron_parser::parse;

fn main() {
   // 解析每5分钟执行一次的cron表达式
   if let Ok(next) = parse("*/5 * * * *", &Utc::now()) {
        println!("when: {}", next);
   }

   // 使用自定义时间戳解析闰年表达式
   if let Ok(next) = parse("0 0 29 2 *", &Utc.timestamp(1893456000, 0)) {
        println!("next leap year: {}", next);
        assert_eq!(next.timestamp(), 1961625600);
   }

   // 测试复杂表达式
   assert!(parse("2-3,9,*/15,1-8,11,9,4,5 * * * *", &Utc::now()).is_ok());
   // 测试不支持的表达式(星期几不支持*/Day格式)
   assert!(parse("* * * * */Fri", &Utc::now()).is_err());

   // 使用自定义时区
   assert!(parse("*/5 * * * *", &Utc::now().with_timezone(&Lisbon)).is_ok());
}

Cron表达式格式

# ┌─────────────────────  分钟 (0 - 59)
# │ ┌───────────────────  小时 (0 - 23)
# │ │ ┌─────────────────  日 (1 - 31)
# │ │ │ ┌───────────────  月 (1 - 12)
# │ │ │ │ ┌─────────────  周几 (0 - 6 或 Sun - Sat)
# │ │ │ │ │
# │ │ │ │ │
# │ │ │ │ │
# * * * * * <要执行的命令>
字段 是否必填 允许值 允许的特殊字符
分钟 0-59 * , - /
小时 0-23 * , - /
1-31 * , - /
1-12 * , - /
周几 0-6或Sun-Sat * , - /

对于周几字段,当使用星期几(Sun-Sat)时,表达式*/Day不支持,应该使用数字。例如*/Wed = */3表示每3天运行一次(周日、周三、周六)。

特殊字符含义:

  • * 任何值
  • , 值列表分隔符
  • - 值范围
  • / 步长值

范围步长

支持带步长的范围表达式,例如:

0 12-18/3 * * *  # 从12点到18点,每3小时一次

或者从1点开始每6小时:

0 1/6 * * *

依赖配置

依赖chrono crate。

Cargo.toml中添加:

[dependencies]
chrono = "^0.4"
cron-parser = "*"

完整示例

获取未来10个闰年的日期:

use chrono::{DateTime, Utc};
use cron_parser::parse;

fn main() {
    // 获取当前UTC时间
    let now = Utc::now();
    let mut crons = Vec::<DateTime<Utc>>::new();
    
    // 解析闰年表达式(2月29日)
    let mut next = parse("0 0 29 2 *", &now).unwrap();
    
    // 获取未来10个闰年日期
    for _ in 0..10 {
        next = parse("0 0 29 2 *", &next).unwrap();
        crons.push(next);
    }
    
    // 打印结果
    for x in crons {
        println!("{} - {}", x, x.timestamp());
    }
}

输出示例:

2024-02-29 00:00:00 UTC - 1709164800
2028-02-29 00:00:00 UTC - 1835395200
2032-02-29 00:00:00 UTC - 1961625600
2036-02-29 00:00:00 UTC - 2087856000
2040-02-29 00:00:00 UTC - 2214086400
2044-02-29 00:00:00 UTC - 2340316800
2048-02-29 00:00:00 UTC - 2466547200
2052-02-29 00:00:00 UTC - 2592777600
2056-02-29 00:00:00 UTC - 2719008000
2060-02-29 00:00:00 UTC - 2845238400

另一个完整示例:定时任务调度器

use chrono::{DateTime, Utc, Duration};
use cron_parser::parse;

fn main() {
    // 定义cron表达式(每小时的第30分钟执行)
    let cron_expr = "30 * * * *";
    let mut next_run = parse(cron_expr, &Utc::now()).unwrap();
    
    println!("Next run at: {}", next_run);
    
    // 模拟调度器运行5次
    for i in 1..=5 {
        // 计算当前时间和下次运行时间的时间差
        let now = Utc::now();
        let wait_time = if next_run > now {
            next_run - now
        } else {
            Duration::seconds(0)
        };
        
        println!("[{i}] Waiting {} seconds...", wait_time.num_seconds());
        
        // 这里可以添加实际要执行的任务代码
        
        // 计算下一次运行时间
        next_run = parse(cron_expr, &next_run).unwrap();
        println!("Next run at: {}", next_run);
    }
}

1 回复

Rust的cron表达式解析库cron-parser使用指南

cron-parser是一个用于解析cron表达式并计算下次执行时间的Rust库,非常适合需要高性能定时任务调度的应用场景。

基本功能

  • 解析标准cron表达式
  • 计算下一次/上一次匹配的时间
  • 支持秒级精度(6段式cron表达式)
  • 支持常见的cron表达式扩展

安装

Cargo.toml中添加依赖:

[dependencies]
cron-parser = "0.3"

基本使用方法

解析cron表达式

use cron_parser::parse;

fn main() {
    // 解析标准的5段式cron表达式
    let schedule = parse("30 4 * * *").unwrap();
    
    // 解析包含秒的6段式cron表达式
    let precise_schedule = parse("0 30 4 * * *").unwrap();
}

计算下次执行时间

use cron_parser::parse;
use chrono::{Local, TimeZone};

fn main() {
    let schedule = parse("30 4 * * *").unwrap();
    let now = Local::now();
    
    // 计算下次执行时间
    if let Some(next_time) = schedule.next_after(&now) {
        println!("下次执行时间: {}", next_time);
    }
    
    // 也可以计算前一次执行时间
    if let Some(prev_time) = schedule.prev_before(&now) {
        println!("上次执行时间: {}", prev_time);
    }
}

高级用法

检查时间是否匹配cron表达式

use cron_parser::parse;
use chrono::Local;

fn main() {
    let schedule = parse("0 */5 * * * *").unwrap(); // 每5分钟
    let now = Local::now();
    
    if schedule.matches(&now) {
        println!("当前时间匹配cron表达式");
    }
}

迭代所有匹配时间

use cron_parser::parse;
use chrono::{Local, Duration};

fn main() {
    let schedule = parse("0 0 * * *").unwrap(); // 每天午夜
    let start = Local::now();
    let end = start + Duration::days(7);
    
    // 获取接下来一周内所有匹配时间
    let occurrences: Vec<_> = schedule.after(&start).until(&end).collect();
    
    for time in occurrences {
        println!("匹配时间: {}", time);
    }
}

注意事项

  1. cron-parser支持标准的5段和6段(带秒)格式:

    • * * * * * (分 时 日 月 周)
    • * * * * * * (秒 分 时 日 月 周)
  2. 支持特殊字符:

    • * - 任意值
    • , - 值列表分隔符
    • - - 范围
    • / - 步长
  3. 月份和周几可以使用名称缩写(JAN, FEB… 或 MON, TUE…)

性能提示

cron-parser经过优化,适合高性能场景。对于频繁调用的场景,建议:

  1. 重复使用已解析的Schedule对象,避免重复解析
  2. 考虑使用lazy_static缓存常用表达式
#[macro_use]
extern crate lazy_static;

use cron_parser::parse;
use chrono::Local;

lazy_static! {
    static ref HOURLY_SCHEDULE: cron_parser::Schedule = {
        parse("0 * * * *").unwrap()
    };
}

fn main() {
    let now = Local::now();
    if let Some(next) = HOURLY_SCHEDULE.next_after(&now) {
        println!("下一个整点: {}", next);
    }
}

完整示例demo

下面是一个完整的示例,展示如何使用cron-parser构建一个简单的定时任务调度器:

use cron_parser::parse;
use chrono::{Local, Duration};
use std::thread;
use std::time::Duration as StdDuration;

fn main() {
    // 解析一个每5分钟执行一次的cron表达式
    let schedule = parse("0 */5 * * * *").expect("无效的cron表达式");

    println!("开始定时任务调度器,每5分钟执行一次...");
    println!("当前时间: {}", Local::now());
    
    loop {
        let now = Local::now();
        
        // 计算下次执行时间
        if let Some(next_time) = schedule.next_after(&now) {
            println!("下次执行时间: {}", next_time);
            
            // 计算需要等待的时间
            let wait_duration = next_time - now;
            let wait_seconds = wait_duration.num_seconds();
            
            // 等待直到下次执行时间
            println!("等待 {} 秒...", wait_seconds);
            thread::sleep(StdDuration::from_secs(wait_seconds as u64));
            
            // 执行任务
            println!("执行任务: {}", Local::now());
            perform_task();
        } else {
            println!("没有下次执行时间,退出");
            break;
        }
    }
}

fn perform_task() {
    // 这里是你的定时任务逻辑
    println!("执行定时任务...");
    // 模拟任务执行时间
    thread::sleep(StdDuration::from_secs(1));
}

这个示例演示了如何:

  1. 解析cron表达式
  2. 计算下次执行时间
  3. 等待直到执行时间到达
  4. 执行定时任务
  5. 循环执行上述过程

你可以根据需要修改cron表达式和任务逻辑来适应不同的定时任务需求。

回到顶部