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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use super::{Duration, Epoch};
/*

NOTE: This is taken from itertools: https://docs.rs/itertools-num/0.1.3/src/itertools_num/linspace.rs.html#78-93 .

*/

/// An iterator of a sequence of evenly spaced Epochs.
#[derive(Clone, Debug)]
pub struct TimeSeries {
    start: Epoch,
    end: Epoch,
    step: Duration,
    cur: Epoch,
    incl: bool,
}

impl TimeSeries {
    /// Return an iterator of evenly spaced Epochs, **inclusive** on start and **exclusive** on end.
    /// ```
    /// use hifitime::{Epoch, Unit, TimeSeries};
    /// let start = Epoch::from_gregorian_utc_at_midnight(2017, 1, 14);
    /// let end = Epoch::from_gregorian_utc_at_noon(2017, 1, 14);
    /// let step = Unit::Hour * 2;
    /// let time_series = TimeSeries::exclusive(start, end, step);
    /// let mut cnt = 0;
    /// for epoch in time_series {
    ///     println!("{}", epoch);
    ///     cnt += 1
    /// }
    /// assert_eq!(cnt, 6)
    /// ```
    #[inline]
    pub fn exclusive(start: Epoch, end: Epoch, step: Duration) -> TimeSeries {
        // Start one step prior to start because next() just moves forward
        Self {
            start,
            end,
            step,
            cur: start - step,
            incl: false,
        }
    }

    /// Return an iterator of evenly spaced Epochs, inclusive on start **and** on end.
    /// ```
    /// use hifitime::{Epoch, Unit, TimeSeries};
    /// let start = Epoch::from_gregorian_utc_at_midnight(2017, 1, 14);
    /// let end = Epoch::from_gregorian_utc_at_noon(2017, 1, 14);
    /// let step = Unit::Hour * 2;
    /// let time_series = TimeSeries::inclusive(start, end, step);
    /// let mut cnt = 0;
    /// for epoch in time_series {
    ///     println!("{}", epoch);
    ///     cnt += 1
    /// }
    /// assert_eq!(cnt, 7)
    /// ```
    #[inline]
    pub fn inclusive(start: Epoch, end: Epoch, step: Duration) -> TimeSeries {
        // Start one step prior to start because next() just moves forward
        Self {
            start,
            end,
            step,
            cur: start - step,
            incl: true,
        }
    }
}

impl Iterator for TimeSeries {
    type Item = Epoch;

    #[inline]
    fn next(&mut self) -> Option<Epoch> {
        let next_item = self.cur + self.step;
        if (!self.incl && next_item >= self.end) || (self.incl && next_item > self.end) {
            None
        } else {
            self.cur = next_item;
            Some(next_item)
        }
    }
}

impl DoubleEndedIterator for TimeSeries {
    #[inline]
    fn next_back(&mut self) -> Option<Epoch> {
        let next_item = self.cur - self.step;
        if next_item < self.start {
            None
        } else {
            Some(next_item)
        }
    }
}

impl ExactSizeIterator for TimeSeries where TimeSeries: Iterator {}

#[cfg(test)]
mod tests {
    use crate::{Epoch, TimeSeries, Unit};

    #[test]
    fn test_timeseries() {
        let start = Epoch::from_gregorian_utc_at_midnight(2017, 1, 14);
        let end = Epoch::from_gregorian_utc_at_noon(2017, 1, 14);
        let step = Unit::Hour * 2;

        let mut count = 0;
        let time_series = TimeSeries::exclusive(start, end, step);
        for epoch in time_series {
            if count == 0 {
                assert_eq!(
                    epoch, start,
                    "Starting epoch of exclusive time series is wrong"
                );
            } else if count == 5 {
                assert_ne!(epoch, end, "Ending epoch of exclusive time series is wrong");
            }
            #[cfg(feature = "std")]
            println!("{}", epoch);
            count += 1;
        }

        assert_eq!(count, 6, "Should have five items in this iterator");

        count = 0;
        let time_series = TimeSeries::inclusive(start, end, step);
        for epoch in time_series {
            if count == 0 {
                assert_eq!(
                    epoch, start,
                    "Starting epoch of inclusive time series is wrong"
                );
            } else if count == 6 {
                assert_eq!(epoch, end, "Ending epoch of inclusive time series is wrong");
            }
            #[cfg(feature = "std")]
            println!("{}", epoch);
            count += 1;
        }

        assert_eq!(count, 7, "Should have six items in this iterator");
    }
}