recurdates/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate chrono;

use chrono::{DateTime, Utc, TimeZone};
use chrono::naive::{MAX_DATE, MIN_DATE};

macro_rules! max_datetime {
    () => (Utc.from_utc_datetime(&MAX_DATE.and_hms(23, 59, 59)))
}

macro_rules! min_datetime {
    () => (Utc.from_utc_datetime(&MIN_DATE.and_hms(0, 0, 0)))
}

#[cfg(test)]
mod tests;
pub mod repeat_every;

use repeat_every::RepeatEvery;

/// `ReDateTime` struct represents recurring datetime object
/// starting at `at` and repeating every `repeat` up to `until` (inclusive).
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ReDateTime {
    pub at: DateTime<Utc>,
    pub repeat: RepeatEvery,
    pub until: DateTime<Utc>,
}

#[derive(Debug)]
pub struct ReDateTimeIter<'a> {
    from: &'a ReDateTime,
    adder: Option<RepeatEvery>,
}

impl ReDateTime {
    /// `ReDateTime` with no repeat.
    pub fn at(dt: DateTime<Utc>) -> Self {
        ReDateTime { at: dt, repeat: RepeatEvery::zero(), until: max_datetime!() }
    }

    /// `ReDateTime` with infinite repeat.
    pub fn repeat(dt: DateTime<Utc>, dur: RepeatEvery) -> Self {
        let lim = if dur.is_zero() { min_datetime!() } else { max_datetime!()
        };
        ReDateTime { at: dt, repeat: dur, until: lim }
    }

    /// `ReDateTime` with a finite number of repeats.
    pub fn repeat_until(dt: DateTime<Utc>, dur: RepeatEvery, til: DateTime<Utc>) -> Self {
        ReDateTime { at: dt, repeat: dur, until: til }
    }

    /// True if there is no repeat after or on `dt`.
    pub fn done_before(&self, dt: &DateTime<Utc>) -> bool {
        if self.repeat.is_zero() {
            return self.at < *dt;
        }

        self.until != max_datetime!()
            && (self.until < *dt || self.iter().last().unwrap() < *dt)
    }

    /// True if there is a repeat between `df` (inclusive) and `dt` (inclusive). `df` always comes earlier than `dt`.
    pub fn between(&self, df: &DateTime<Utc>, dt: &DateTime<Utc>) -> bool {
        self.first_after(df).map_or(false, |d| d <= *dt)
    }

    /// First repeat after `df` (inclusive).
    pub fn first_after(&self, df: &DateTime<Utc>) -> Option<DateTime<Utc>> {
        if self.repeat.is_zero() {
            if self.at < *df {
                None
            } else {
                Some(self.at)
            }
        } else {
            if self.until < *df {
                None
            } else {
                self.iter().skip_while(|x| *x < *df).next()
            }
        }
    }

    /// An iterator visiting datetimes in order. If `repeat` is negative, the iterator runs in reverse-chronological order.
    pub fn iter(&self) -> ReDateTimeIter {
        let a = if self.repeat.is_zero() {
            None } else {
            Some(RepeatEvery::zero())
        };
        ReDateTimeIter { from: &self, adder: a }
    }
}

impl<'a> Iterator for ReDateTimeIter<'a> {
    type Item = DateTime<Utc>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.adder.is_none() {
            return None;
        }

        let x = self.adder.clone().unwrap();
        self.adder = x.checked_add(&self.from.repeat);
        if let Some(n) = x.add_to(&self.from.at) {
            if n <= self.from.until {
                return Some(n);
            }
        }
        None
    }
}