Skip to content
This repository has been archived by the owner on Jul 11, 2022. It is now read-only.

Commit

Permalink
Allow changing the default offset that dates are computed in
Browse files Browse the repository at this point in the history
  • Loading branch information
Peter Sebastian Puetz committed Mar 8, 2022
1 parent 635fb1b commit 1503043
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 23 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.4.1] - 2022-03-08
### Added
- Added methods `ScheduleIter::assume_offset`, `ScheduleIter::use_local_offset` and pendants for `MultiScheduleIter` that allow to change the default offset (local) that dates are computed in to another.

## [0.4.0] - 2021-12-04
### Added
- Introduced new WeekdayModifier variant "Last" to select the last occurrence of some weekday, e.g. "at 6 PM (last Monday)".
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cron-lingo"
version = "0.4.0"
version = "0.4.1"
authors = ["Peter Sebastian Pütz <ppuetz@protonmail.com>"]
edition = "2018"
repository = "https://github.com/puetzp/cron-lingo"
Expand Down
11 changes: 10 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
//! This crate allows to parse a cron-like, human-readable expression.
//! The resulting object can be turned into an iterator to compute the next date
//! (as a `time::OffsetDateTime`) one at a time.
//! (as a `time::OffsetDateTime`) one at a time. By default dates are computed
//! in the current local offset, but the iterator can be configured to use other
//! offsets.
//!
//! # Example
//! ```rust
//! use cron_lingo::Schedule;
//! use std::str::FromStr;
//! use time::macros::offset;
//!
//! fn main() -> Result<(), cron_lingo::error::Error> {
//! // Create a schedule from an expression and iterate.
Expand All @@ -23,6 +26,12 @@
//! combination += schedule3;
//! assert!(combination.iter()?.next().is_some());
//!
//! // The examples above assume that the current local offset is to
//! // be used to determine the dates, but dates can also be computed
//! // in different offsets.
//! let expr = "at 6:30 AM on Mondays and Thursdays";
//! let schedule = Schedule::from_str(expr)?;
//! assert!(schedule.iter()?.assume_offset(offset!(+3)).next().is_some());
//! Ok(())
//! }
//! ```
Expand Down
172 changes: 151 additions & 21 deletions src/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::parse::parse;
use crate::types::*;
use std::iter::Iterator;
use std::str::FromStr;
use time::{Duration, OffsetDateTime, PrimitiveDateTime};
use time::{Duration, OffsetDateTime, PrimitiveDateTime, UtcOffset};

/// A schedule that is built from an expression and can be iterated
/// in order to compute the next date(s) that match the specification.
Expand All @@ -18,6 +18,7 @@ impl Schedule {
schedule: schedule.clone(),
current: OffsetDateTime::now_local().map_err(Error::IndeterminateOffset)?,
skip_outdated: true,
offset: None,
};
Ok(iter)
}
Expand Down Expand Up @@ -56,31 +57,55 @@ pub struct ScheduleIter {
schedule: ParsedSchedule,
current: OffsetDateTime,
skip_outdated: bool,
offset: Option<UtcOffset>,
}

impl ScheduleIter {
/// By default the `next` method will not return a date that is
/// in the past but compute the next future date based on the
/// current local time instead. This method allows to change the
/// iterators default behaviour.
/// in the past but compute the next future data. This method
/// allows to change the iterators default behaviour.
pub fn skip_outdated(mut self, skip: bool) -> ScheduleIter {
self.skip_outdated = skip;
self
}

/// By default the iterator returns dates in the current local
/// offset taken from the system. This method allows to change
/// the iteration behaviour to compute dates in another offset.
pub fn assume_offset(mut self, offset: UtcOffset) -> ScheduleIter {
self.offset = Some(offset);
self
}

/// Compute dates in the current local offset. This is also the
/// default behaviour and can be used to revert changes to this
/// behaviour that were made using `assume_offset`.
pub fn use_local_offset(mut self) -> ScheduleIter {
self.offset = None;
self
}
}

impl Iterator for ScheduleIter {
type Item = Result<OffsetDateTime, Error>;

fn next(&mut self) -> Option<Self::Item> {
if let Some(offset) = self.offset {
self.current = self.current.to_offset(offset);
}

if self.skip_outdated {
match OffsetDateTime::now_local().map_err(Error::IndeterminateOffset) {
Ok(now) => {
if now > self.current {
self.current = now;
}
}
let mut now = match OffsetDateTime::now_local().map_err(Error::IndeterminateOffset) {
Ok(n) => n,
Err(e) => return Some(Err(e)),
};

if let Some(offset) = self.offset {
now = now.to_offset(offset);
}

if now > self.current {
self.current = now;
}
}

Expand Down Expand Up @@ -118,6 +143,7 @@ impl MultiSchedule {
schedules: &schedules,
current: OffsetDateTime::now_local().map_err(Error::IndeterminateOffset)?,
skip_outdated: true,
offset: None,
};
Ok(iter)
}
Expand Down Expand Up @@ -167,31 +193,54 @@ pub struct MultiScheduleIter<'a> {
schedules: &'a [ParsedSchedule],
current: OffsetDateTime,
skip_outdated: bool,
offset: Option<UtcOffset>,
}

impl<'a> MultiScheduleIter<'a> {
/// By default the `next` method will not return a date that is
/// in the past but compute the next future date based on the
/// current local time instead. This method allows to change the
/// iterators default behaviour.
/// in the past but compute the next future data. This method
/// allows to change the iterators default behaviour.
pub fn skip_outdated(mut self, skip: bool) -> MultiScheduleIter<'a> {
self.skip_outdated = skip;
self
}
/// By default the iterator returns dates in the current local
/// offset taken from the system. This method allows to change
/// the iteration behaviour to compute dates in another offset.
pub fn assume_offset(mut self, offset: UtcOffset) -> MultiScheduleIter<'a> {
self.offset = Some(offset);
self
}

/// Compute dates in the current local offset. This is also the
/// default behaviour and can be used to revert changes to this
/// behaviour that were made using `assume_offset`.
pub fn use_local_offset(mut self) -> MultiScheduleIter<'a> {
self.offset = None;
self
}
}

impl<'a> Iterator for MultiScheduleIter<'a> {
type Item = Result<OffsetDateTime, Error>;

fn next(&mut self) -> Option<Self::Item> {
if let Some(offset) = self.offset {
self.current = self.current.to_offset(offset);
}

if self.skip_outdated {
match OffsetDateTime::now_local().map_err(Error::IndeterminateOffset) {
Ok(now) => {
if now > self.current {
self.current = now;
}
}
let mut now = match OffsetDateTime::now_local().map_err(Error::IndeterminateOffset) {
Ok(n) => n,
Err(e) => return Some(Err(e)),
};

if let Some(offset) = self.offset {
now = now.to_offset(offset);
}

if now > self.current {
self.current = now;
}
}

Expand Down Expand Up @@ -303,7 +352,7 @@ fn check_date_validity(
#[cfg(test)]
mod tests {
use super::*;
use time::macros::{datetime, time};
use time::macros::{datetime, offset, time};
use time::Weekday;

#[test]
Expand Down Expand Up @@ -423,6 +472,7 @@ mod tests {
weeks: None,
},
skip_outdated: false,
offset: None,
};

let result = vec![
Expand All @@ -449,6 +499,7 @@ mod tests {
weeks: None,
},
skip_outdated: false,
offset: None,
};

let result = vec![
Expand Down Expand Up @@ -478,6 +529,7 @@ mod tests {
weeks: None,
},
skip_outdated: false,
offset: None,
};

let result = vec![
Expand All @@ -501,6 +553,41 @@ mod tests {

#[test]
fn test_schedule_iteration_4() {
let iterator = ScheduleIter {
current: datetime!(2021-06-09 13:00:00 UTC),
schedule: ParsedSchedule {
times: vec![time!(06:00:00), time!(13:00:00)],
days: Some(vec![
(Weekday::Monday, Some(WeekdayModifier::Third)),
(Weekday::Thursday, None),
]),
weeks: None,
},
skip_outdated: false,
offset: Some(offset!(+3)),
};

let result = vec![
Ok(datetime!(2021-06-10 06:00:00 +3)),
Ok(datetime!(2021-06-10 13:00:00 +3)),
Ok(datetime!(2021-06-17 06:00:00 +3)),
Ok(datetime!(2021-06-17 13:00:00 +3)),
Ok(datetime!(2021-06-21 06:00:00 +3)),
Ok(datetime!(2021-06-21 13:00:00 +3)),
Ok(datetime!(2021-06-24 06:00:00 +3)),
Ok(datetime!(2021-06-24 13:00:00 +3)),
];

assert_eq!(
iterator
.take(8)
.collect::<Vec<Result<OffsetDateTime, Error>>>(),
result
);
}

#[test]
fn test_schedule_iteration_5() {
let iterator = MultiScheduleIter {
current: datetime!(2021-06-09 13:00:00 UTC),
schedules: &vec![
Expand All @@ -519,6 +606,7 @@ mod tests {
},
],
skip_outdated: false,
offset: None,
};

let result = vec![
Expand All @@ -544,7 +632,7 @@ mod tests {
}

#[test]
fn test_schedule_iteration_5() {
fn test_schedule_iteration_6() {
let iterator = MultiScheduleIter {
current: datetime!(2021-06-18 13:00:00 UTC),
schedules: &vec![
Expand All @@ -563,6 +651,7 @@ mod tests {
},
],
skip_outdated: false,
offset: None,
};

let result = vec![
Expand Down Expand Up @@ -594,6 +683,47 @@ mod tests {
);
}

#[test]
fn test_schedule_iteration_7() {
let iterator = MultiScheduleIter {
current: datetime!(2021-06-18 13:00:00 UTC),
schedules: &vec![
ParsedSchedule {
times: vec![time!(06:00:00), time!(18:00:00)],
days: Some(vec![
(Weekday::Monday, Some(WeekdayModifier::Last)),
(Weekday::Thursday, None),
]),
weeks: None,
},
ParsedSchedule {
times: vec![time!(18:00:00)],
days: Some(vec![(Weekday::Saturday, Some(WeekdayModifier::Fourth))]),
weeks: None,
},
],
skip_outdated: false,
offset: Some(offset!(+2:30)),
};

let result = vec![
Ok(datetime!(2021-06-24 06:00:00 +2:30)),
Ok(datetime!(2021-06-24 18:00:00 +2:30)),
Ok(datetime!(2021-06-26 18:00:00 +2:30)),
Ok(datetime!(2021-06-28 06:00:00 +2:30)),
Ok(datetime!(2021-06-28 18:00:00 +2:30)),
Ok(datetime!(2021-07-01 06:00:00 +2:30)),
Ok(datetime!(2021-07-01 18:00:00 +2:30)),
];

assert_eq!(
iterator
.take(7)
.collect::<Vec<Result<OffsetDateTime, Error>>>(),
result
);
}

#[test]
fn test_add_two_schedules() {
let sched1 = Schedule(ParsedSchedule {
Expand Down

0 comments on commit 1503043

Please sign in to comment.