Cron 调度
使用 cron 表达式进行定时任务调度
Ayiou 框架支持解析 cron 表达式,并提供计算下次触发时间等功能。
基本用法
使用 #[arg(cron)] 属性将字段标记为 cron 表达式:
use ayiou::prelude::*;
#[derive(Args)]
pub struct RemindArgs {
#[arg(cron, error = "无效的 cron 表达式")]
pub schedule: CronSchedule,
#[arg(rest)]
pub message: String,
}Cron 表达式格式
支持标准的 6 字段 cron 表达式:
秒 分 时 日 月 周| 字段 | 允许值 | 特殊字符 |
|---|---|---|
| 秒 | 0-59 | * , - / |
| 分 | 0-59 | * , - / |
| 时 | 0-23 | * , - / |
| 日 | 1-31 | * , - / ? |
| 月 | 1-12 | * , - / |
| 周 | 0-6 (0=周日) | * , - / ? |
常用表达式
| 表达式 | 说明 |
|---|---|
0 0 * * * * | 每小时整点 |
0 0 8 * * * | 每天早上8点 |
0 30 9 * * 1-5 | 工作日9:30 |
0 0 0 1 * * | 每月1号0点 |
0 */5 * * * * | 每5分钟 |
0 0 12 * * ? | 每天中午12点 |
CronSchedule 类型
CronSchedule 是 cron 表达式的包装类型,提供以下方法:
parse(expr: &str)
解析 cron 表达式:
use ayiou::prelude::*;
let schedule = CronSchedule::parse("0 0 8 * * *")?;upcoming()
获取从当前时间开始的触发时间迭代器:
let schedule = CronSchedule::parse("0 0 8 * * *")?;
// 获取接下来5次触发时间
for next in schedule.upcoming().take(5) {
println!("下次触发: {}", next);
}next_after(datetime)
获取指定时间之后的下次触发时间:
use chrono::Utc;
let schedule = CronSchedule::parse("0 0 8 * * *")?;
let now = Utc::now();
if let Some(next) = schedule.next_after(&now) {
println!("下次触发: {}", next);
}includes(datetime)
检查指定时间是否匹配调度:
use chrono::Utc;
let schedule = CronSchedule::parse("0 0 8 * * *")?;
let now = Utc::now();
if schedule.includes(now) {
println!("当前时间匹配调度");
}source()
获取原始 cron 表达式字符串:
let schedule = CronSchedule::parse("0 0 8 * * *")?;
println!("表达式: {}", schedule.source()); // "0 0 8 * * *"完整示例
定时提醒命令
use ayiou::prelude::*;
use chrono::Utc;
#[derive(Args)]
#[arg(usage = "/remind <cron表达式> <提醒内容>")]
pub struct RemindArgs {
#[arg(cron, error = "无效的 cron 表达式,格式: 秒 分 时 日 月 周")]
pub schedule: CronSchedule,
#[arg(rest)]
pub message: String,
}
impl RemindArgs {
pub async fn handle(&self, ctx: &Ctx) -> anyhow::Result<()> {
// 计算下次触发时间
let next = self.schedule
.next_after(&Utc::now())
.map(|t| t.format("%Y-%m-%d %H:%M:%S").to_string())
.unwrap_or_else(|| "无".to_string());
ctx.reply_text(format!(
"已设置提醒\n表达式: {}\n下次触发: {}\n内容: {}",
self.schedule.source(),
next,
self.message
)).await?;
Ok(())
}
}
#[derive(Plugin)]
#[plugin(name = "scheduler", prefix = "/")]
pub enum SchedulerCommands {
#[plugin(description = "设置定时提醒")]
Remind(RemindArgs),
}用户输入:
/remind 0 0 8 * * * 起床啦Bot 回复:
已设置提醒
表达式: 0 0 8 * * *
下次触发: 2024-01-02 08:00:00
内容: 起床啦查询调度信息
#[derive(Args)]
pub struct CronInfoArgs {
#[arg(cron, error = "无效的 cron 表达式")]
pub schedule: CronSchedule,
}
impl CronInfoArgs {
pub async fn handle(&self, ctx: &Ctx) -> anyhow::Result<()> {
let mut info = format!("Cron: {}\n\n接下来5次触发:\n", self.schedule.source());
for (i, next) in self.schedule.upcoming().take(5).enumerate() {
info.push_str(&format!(
"{}. {}\n",
i + 1,
next.format("%Y-%m-%d %H:%M:%S")
));
}
ctx.reply_text(info).await?;
Ok(())
}
}注意事项
- 时区:
CronSchedule使用 UTC 时区,如需本地时间请自行转换 - 表达式验证: 无效的 cron 表达式会在解析时返回错误
- 字段数量: 必须是 6 字段格式(包含秒)