//! Cron math shared by the scheduler loop and the `routine.schedule` tool. use croner::Cron; use time::OffsetDateTime; #[derive(Debug, thiserror::Error)] pub enum ScheduleError { #[error("invalid cron pattern: {0}")] Pattern(String), } /// The next firing strictly after `after` for a 5-field cron pattern. pub fn next_occurrence( pattern: &str, after: OffsetDateTime, ) -> Result { let cron = Cron::new(pattern) .parse() .map_err(|e| ScheduleError::Pattern(e.to_string()))?; let chrono_after = chrono::DateTime::from_timestamp(after.unix_timestamp(), 0) .ok_or_else(|| ScheduleError::Pattern("timestamp out of range".into()))?; let next = cron .find_next_occurrence(&chrono_after, false) .map_err(|e| ScheduleError::Pattern(e.to_string()))?; OffsetDateTime::from_unix_timestamp(next.timestamp()) .map_err(|e| ScheduleError::Pattern(e.to_string())) }