From a96e9fb7242481612eb62853c272b8d02d5bff0c Mon Sep 17 00:00:00 2001 From: Dominik Csapak Date: Tue, 30 Nov 2021 13:12:06 +0100 Subject: [PATCH] proxmox-time: calendar-events: make compute_next_event a method and deprecated the standalone function Signed-off-by: Dominik Csapak --- proxmox-time/src/calendar_event.rs | 253 +++++++++++++++-------------- proxmox-time/src/test.rs | 4 +- 2 files changed, 133 insertions(+), 124 deletions(-) diff --git a/proxmox-time/src/calendar_event.rs b/proxmox-time/src/calendar_event.rs index 1c21a84e..a839f9d8 100644 --- a/proxmox-time/src/calendar_event.rs +++ b/proxmox-time/src/calendar_event.rs @@ -35,139 +35,148 @@ pub struct CalendarEvent { pub(crate) year: Vec, } +impl CalendarEvent { + /// Computes the next timestamp after `last`. If `utc` is false, the local + /// timezone will be used for the calculation. + pub fn compute_next_event(&self, last: i64, utc: bool) -> Result, Error> { + let last = last + 1; // at least one second later + + let all_days = self.days.is_empty() || self.days.is_all(); + + let mut t = TmEditor::with_epoch(last, utc)?; + + let mut count = 0; + + loop { + // cancel after 1000 loops + if count > 1000 { + return Ok(None); + } else { + count += 1; + } + + if !self.year.is_empty() { + let year: u32 = t.year().try_into()?; + if !DateTimeValue::list_contains(&self.year, year) { + if let Some(n) = DateTimeValue::find_next(&self.year, year) { + t.add_years((n - year).try_into()?)?; + continue; + } else { + // if we have no valid year, we cannot find a correct timestamp + return Ok(None); + } + } + } + + if !self.month.is_empty() { + let month: u32 = t.month().try_into()?; + if !DateTimeValue::list_contains(&self.month, month) { + if let Some(n) = DateTimeValue::find_next(&self.month, month) { + t.add_months((n - month).try_into()?)?; + } else { + // if we could not find valid month, retry next year + t.add_years(1)?; + } + continue; + } + } + + if !self.day.is_empty() { + let day: u32 = t.day().try_into()?; + if !DateTimeValue::list_contains(&self.day, day) { + if let Some(n) = DateTimeValue::find_next(&self.day, day) { + t.add_days((n - day).try_into()?)?; + } else { + // if we could not find valid mday, retry next month + t.add_months(1)?; + } + continue; + } + } + + if !all_days { + // match day first + let day_num: u32 = t.day_num().try_into()?; + let day = WeekDays::from_bits(1 << day_num).unwrap(); + if !self.days.contains(day) { + if let Some(n) = ((day_num + 1)..7) + .find(|d| self.days.contains(WeekDays::from_bits(1 << d).unwrap())) + { + // try next day + t.add_days((n - day_num).try_into()?)?; + } else { + // try next week + t.add_days((7 - day_num).try_into()?)?; + } + continue; + } + } + + // this day + if !self.hour.is_empty() { + let hour = t.hour().try_into()?; + if !DateTimeValue::list_contains(&self.hour, hour) { + if let Some(n) = DateTimeValue::find_next(&self.hour, hour) { + // test next hour + t.set_time(n.try_into()?, 0, 0)?; + } else { + // test next day + t.add_days(1)?; + } + continue; + } + } + + // this hour + if !self.minute.is_empty() { + let minute = t.min().try_into()?; + if !DateTimeValue::list_contains(&self.minute, minute) { + if let Some(n) = DateTimeValue::find_next(&self.minute, minute) { + // test next minute + t.set_min_sec(n.try_into()?, 0)?; + } else { + // test next hour + t.set_time(t.hour() + 1, 0, 0)?; + } + continue; + } + } + + // this minute + if !self.second.is_empty() { + let second = t.sec().try_into()?; + if !DateTimeValue::list_contains(&self.second, second) { + if let Some(n) = DateTimeValue::find_next(&self.second, second) { + // test next second + t.set_sec(n.try_into()?)?; + } else { + // test next min + t.set_min_sec(t.min() + 1, 0)?; + } + continue; + } + } + + let next = t.into_epoch()?; + return Ok(Some(next)); + } + } +} + /// Verify the format of the [CalendarEvent] pub fn verify_calendar_event(i: &str) -> Result<(), Error> { parse_calendar_event(i)?; Ok(()) } -/// Compute the next event +/// Compute the next event. Use [CalendarEvent::compute_next_event] instead. +#[deprecated="use method 'compute_next_event' of CalendarEvent instead"] pub fn compute_next_event( event: &CalendarEvent, last: i64, utc: bool, ) -> Result, Error> { - - let last = last + 1; // at least one second later - - let all_days = event.days.is_empty() || event.days.is_all(); - - let mut t = TmEditor::with_epoch(last, utc)?; - - let mut count = 0; - - loop { - // cancel after 1000 loops - if count > 1000 { - return Ok(None); - } else { - count += 1; - } - - if !event.year.is_empty() { - let year: u32 = t.year().try_into()?; - if !DateTimeValue::list_contains(&event.year, year) { - if let Some(n) = DateTimeValue::find_next(&event.year, year) { - t.add_years((n - year).try_into()?)?; - continue; - } else { - // if we have no valid year, we cannot find a correct timestamp - return Ok(None); - } - } - } - - if !event.month.is_empty() { - let month: u32 = t.month().try_into()?; - if !DateTimeValue::list_contains(&event.month, month) { - if let Some(n) = DateTimeValue::find_next(&event.month, month) { - t.add_months((n - month).try_into()?)?; - } else { - // if we could not find valid month, retry next year - t.add_years(1)?; - } - continue; - } - } - - if !event.day.is_empty() { - let day: u32 = t.day().try_into()?; - if !DateTimeValue::list_contains(&event.day, day) { - if let Some(n) = DateTimeValue::find_next(&event.day, day) { - t.add_days((n - day).try_into()?)?; - } else { - // if we could not find valid mday, retry next month - t.add_months(1)?; - } - continue; - } - } - - if !all_days { // match day first - let day_num: u32 = t.day_num().try_into()?; - let day = WeekDays::from_bits(1< Result<(), Error> { Err(err) => bail!("parsing '{}' failed - {}", v, err), }; - match compute_next_event(&event, last, true) { + match event.compute_next_event(last, true) { Ok(Some(next)) => { if next == expect { println!("next {:?} => {}", event, next); @@ -49,7 +49,7 @@ fn test_compute_next_event() -> Result<(), Error> { Err(err) => bail!("parsing '{}' failed - {}", v, err), }; - match compute_next_event(&event, last, true)? { + match event.compute_next_event(last, true)? { None => Ok(()), Some(next) => bail!( "compute next for '{}' succeeded, but expected fail - result {}",