1#![deny(warnings)]
2
3use crate::utils;
4use crate::types::{DateTime, Range, TimeSequence};
5
6#[derive(Clone)]
9pub struct Interval<SeqA, SeqB>
10 where SeqA: TimeSequence,
11 SeqB: TimeSequence + Clone
12{
13 start: SeqA,
14 end: SeqB,
15 inclusive: bool,
16}
17
18impl<SeqA, SeqB> Interval<SeqA, SeqB>
19 where SeqA: TimeSequence,
20 SeqB: TimeSequence + Clone
21{
22 fn _base(&self, t0: &DateTime, future: bool) -> Box<dyn Iterator<Item=Range> + '_> {
23 let endseq = self.end.clone();
24 let inclusive = self.inclusive;
25
26 let interval = move |istart: Range| {
28 use std::cmp;
29 let iend = endseq._future_raw(&istart.start).next().unwrap();
30 Range{
31 start: istart.start,
32 end: if inclusive { iend.end } else { iend.start },
33 grain: cmp::min(istart.grain, iend.grain)
34 }
35 };
36
37 let probe = interval(self.start._future_raw(t0).next().unwrap());
40 let trunc_grain = utils::enclosing_grain_from_duration(probe.duration());
42 let t0 = utils::truncate(*t0, trunc_grain);
44 let t0 = self.start._future_raw(&t0).next().unwrap().start;
45
46 Box::new(if future {
47 self.start._future_raw(&t0)
48 } else {
49 self.start._past_raw(&t0)
50 }.map(interval))
51 }
52}
53
54impl<SeqA, SeqB> TimeSequence for Interval<SeqA, SeqB>
55 where SeqA: TimeSequence,
56 SeqB: TimeSequence + Clone
57{
58 fn _future_raw(&self, t0: &DateTime) -> Box<dyn Iterator<Item=Range> + '_> {
59 self._base(t0, true)
60 }
61
62 fn _past_raw(&self, t0: &DateTime) -> Box<dyn Iterator<Item=Range> + '_> {
63 self._base(t0, false)
64 }
65}
66
67
68#[cfg(test)]
69mod test {
70 use super::*;
71 use crate::types::{Date, Grain};
72 use crate::seq_named::{Weekday, Month};
73 use crate::seq_nthof::NthOf;
74 use crate::seq_grain::Grains;
75
76 fn dt(year: i32, month: u32, day: u32) -> DateTime {
77 Date::from_ymd(year, month, day).and_hms(0, 0, 0)
78 }
79
80 fn dttm(year: i32, month: u32, day: u32, h: u32, m: u32, s: u32) -> DateTime {
81 Date::from_ymd(year, month, day).and_hms(h, m, s)
82 }
83
84 #[test]
85 fn interval_basic() {
86 let mon2fri = Interval{start: Weekday(1), end: Weekday(5), inclusive: true};
88
89 let mut fut = mon2fri.future(&dt(2016, 2, 25));
90 assert_eq!(fut.next().unwrap(),
91 Range{start: dt(2016, 2, 22), end: dt(2016, 2, 27), grain: Grain::Day});
92 assert_eq!(fut.next().unwrap(),
93 Range{start: dt(2016, 2, 29), end: dt(2016, 3, 5), grain: Grain::Day});
94
95 let mut past = mon2fri.past(&dt(2016, 2, 25));
97 assert_eq!(past.next().unwrap(),
98 Range{start: dt(2016, 2, 15), end: dt(2016, 2, 20), grain: Grain::Day});
99 assert_eq!(past.next().unwrap(),
100 Range{start: dt(2016, 2, 8), end: dt(2016, 2, 13), grain: Grain::Day});
101
102 let mut past = mon2fri._past_raw(&dt(2016, 2, 25));
104 assert_eq!(past.next().unwrap(),
105 Range{start: dt(2016, 2, 22), end: dt(2016, 2, 27), grain: Grain::Day});
106 assert_eq!(past.next().unwrap(),
107 Range{start: dt(2016, 2, 15), end: dt(2016, 2, 20), grain: Grain::Day});
108 assert_eq!(past.next().unwrap(),
109 Range{start: dt(2016, 2, 8), end: dt(2016, 2, 13), grain: Grain::Day});
110 }
111
112 #[test]
113 fn interval_afternoon() {
114 let afternoon = Interval{
115 start: NthOf(13, Grains(Grain::Hour), Grains(Grain::Day)),
116 end: NthOf(19, Grains(Grain::Hour), Grains(Grain::Day)),
117 inclusive: false};
118
119 let mut iter = afternoon.future(&dt(2016, 2, 25));
120 assert_eq!(iter.next().unwrap(),
121 Range{start: dttm(2016, 2, 25, 12, 0, 0),
122 end: dttm(2016, 2, 25, 18, 0, 0), grain: Grain::Hour});
123 assert_eq!(iter.next().unwrap(),
124 Range{start: dttm(2016, 2, 26, 12, 0, 0),
125 end: dttm(2016, 2, 26, 18, 0, 0), grain: Grain::Hour});
126
127 let mut iter = afternoon.past(&dttm(2016, 2, 25, 14, 0, 0));
129 assert_eq!(iter.next().unwrap(),
130 Range{start: dttm(2016, 2, 24, 12, 0, 0),
131 end: dttm(2016, 2, 24, 18, 0, 0), grain: Grain::Hour});
132 assert_eq!(iter.next().unwrap(),
133 Range{start: dttm(2016, 2, 23, 12, 0, 0),
134 end: dttm(2016, 2, 23, 18, 0, 0), grain: Grain::Hour});
135
136 let mut iter = afternoon._past_raw(&dttm(2016, 2, 25, 14, 0, 0));
138 assert_eq!(iter.next().unwrap(),
139 Range{start: dttm(2016, 2, 25, 12, 0, 0),
140 end: dttm(2016, 2, 25, 18, 0, 0), grain: Grain::Hour});
141 assert_eq!(iter.next().unwrap(),
142 Range{start: dttm(2016, 2, 24, 12, 0, 0),
143 end: dttm(2016, 2, 24, 18, 0, 0), grain: Grain::Hour});
144 assert_eq!(iter.next().unwrap(),
145 Range{start: dttm(2016, 2, 23, 12, 0, 0),
146 end: dttm(2016, 2, 23, 18, 0, 0), grain: Grain::Hour});
147 }
148
149 #[test]
150 fn interval_mixed() {
151 let june2ndtileom = Interval{
152 start: NthOf(2, Grains(Grain::Day), Month(6)),
153 end: Month(6), inclusive: true};
154
155 let mut iter = june2ndtileom.future(&dt(2016, 6, 25));
156 assert_eq!(iter.next().unwrap(),
157 Range{start: dt(2016, 6, 2), end: dt(2016, 7, 1), grain: Grain::Day});
158 assert_eq!(iter.next().unwrap(),
159 Range{start: dt(2017, 6, 2), end: dt(2017, 7, 1), grain: Grain::Day});
160
161 let mut iter = june2ndtileom.past(&dt(2016, 6, 25));
163 assert_eq!(iter.next().unwrap(),
164 Range{start: dt(2015, 6, 2), end: dt(2015, 7, 1), grain: Grain::Day});
165 assert_eq!(iter.next().unwrap(),
166 Range{start: dt(2014, 6, 2), end: dt(2014, 7, 1), grain: Grain::Day});
167
168 let mut iter = june2ndtileom._past_raw(&dt(2016, 6, 25));
170 assert_eq!(iter.next().unwrap(),
171 Range{start: dt(2016, 6, 2), end: dt(2016, 7, 1), grain: Grain::Day});
172 assert_eq!(iter.next().unwrap(),
173 Range{start: dt(2015, 6, 2), end: dt(2015, 7, 1), grain: Grain::Day});
174 assert_eq!(iter.next().unwrap(),
175 Range{start: dt(2014, 6, 2), end: dt(2014, 7, 1), grain: Grain::Day});
176 }
177}