kronos/
seq_union.rs

1#![deny(warnings)]
2
3use crate::types::{DateTime, Range, TimeSequence};
4
5// Alternates SeqA and SeqB depending on what happens first
6// Union skips over Ranges totally contained by other sequence
7//   |------a------|
8//            |------b------|
9//
10//   |----a----|
11//                |-----b-----|
12//
13//   |---------a---------|
14//        |-----b-----|
15//
16// Exapmles:
17// - Mondays and Fridays
18// - Weekends and Tuesdays
19// - overlapping (2pm to 3pm) and (1pm to 5pm)
20
21#[derive(Clone)]
22pub struct Union<SeqA, SeqB>(pub SeqA, pub SeqB)
23    where SeqA: TimeSequence,
24          SeqB: TimeSequence;
25
26impl<SeqA, SeqB> Union<SeqA, SeqB>
27    where SeqA: TimeSequence,
28          SeqB: TimeSequence
29{
30    fn _base(&self, t0: &DateTime, future: bool) -> Box<dyn Iterator<Item=Range> + '_> {
31        let (mut astream, mut bstream) = if future {
32            (self.0._future_raw(t0), self.1._future_raw(t0))
33        } else {
34            (self.0._past_raw(t0), self.1._past_raw(t0))
35        };
36        let mut anext = astream.next().unwrap();
37        let mut bnext = bstream.next().unwrap();
38        Box::new((0..).map(move |_| {
39            if (anext.start <= bnext.start && future) ||
40               (anext.start > bnext.start && !future) {
41                // advance included bstream until out of shadow of astream
42                while (bnext.end <= anext.end && future) ||
43                      (bnext.start >= anext.start && !future) {
44                    bnext = bstream.next().unwrap();
45                }
46                let unionret = anext.clone();
47                anext = astream.next().unwrap();
48                unionret
49            } else {
50                // advance included astream until out of shadow of bstream
51                while (anext.end <= bnext.end && future) ||
52                      (anext.start >= bnext.start && !future) {
53                    anext = astream.next().unwrap();
54                }
55                let unionret = bnext.clone();
56                bnext = bstream.next().unwrap();
57                unionret
58            }
59        }))
60    }
61}
62
63impl<SeqA, SeqB> TimeSequence for Union<SeqA, SeqB>
64    where SeqA: TimeSequence,
65          SeqB: TimeSequence
66{
67    fn _future_raw(&self, t0: &DateTime) -> Box<dyn Iterator<Item=Range> + '_> {
68        self._base(t0, true)
69    }
70
71    fn _past_raw(&self, t0: &DateTime) -> Box<dyn Iterator<Item=Range> + '_> {
72        self._base(t0, false)
73    }
74}
75
76
77#[cfg(test)]
78mod test {
79    use super::*;
80    use crate::types::Grain;
81
82    fn dt(year: i32, month: u32, day: u32) -> DateTime {
83        use crate::types::Date;
84        Date::from_ymd(year, month, day).and_hms(0, 0, 0)
85    }
86
87    #[test]
88    fn test_union() {
89        use crate::seq_named::Weekday;
90
91        let mut monwed = Union(Weekday(1), Weekday(3)).future(&dt(2015, 2, 27));
92        assert_eq!(monwed.next().unwrap(),
93            Range{start: dt(2015, 3, 2), end: dt(2015, 3, 3), grain: Grain::Day});
94        assert_eq!(monwed.next().unwrap(),
95            Range{start: dt(2015, 3, 4), end: dt(2015, 3, 5), grain: Grain::Day});
96        assert_eq!(monwed.next().unwrap(),
97            Range{start: dt(2015, 3, 9), end: dt(2015, 3, 10), grain: Grain::Day});
98
99        let monwed = Union(Weekday(1), Weekday(3));
100        let monwedfri = Union(monwed, Weekday(5));
101        let mut monwedfri = monwedfri.future(&dt(2015, 2, 27));
102        assert_eq!(monwedfri.next().unwrap(),
103            Range{start: dt(2015, 2, 27), end: dt(2015, 2, 28), grain: Grain::Day});
104        assert_eq!(monwedfri.next().unwrap(),
105            Range{start: dt(2015, 3, 2), end: dt(2015, 3, 3), grain: Grain::Day});
106        assert_eq!(monwedfri.next().unwrap(),
107            Range{start: dt(2015, 3, 4), end: dt(2015, 3, 5), grain: Grain::Day});
108    }
109
110    #[test]
111    fn test_union_past() {
112        use crate::seq_named::Weekday;
113
114        let mut monwed = Union(Weekday(1), Weekday(3)).past(&dt(2015, 2, 27));
115        assert_eq!(monwed.next().unwrap(),
116            Range{start: dt(2015, 2, 25), end: dt(2015, 2, 26), grain: Grain::Day});
117        assert_eq!(monwed.next().unwrap(),
118            Range{start: dt(2015, 2, 23), end: dt(2015, 2, 24), grain: Grain::Day});
119
120        let monwed = Union(Weekday(1), Weekday(3));
121        let monwedfri = Union(monwed, Weekday(5));
122        let mut monwedfri = monwedfri.past(&dt(2015, 2, 27));
123        assert_eq!(monwedfri.next().unwrap(),
124            Range{start: dt(2015, 2, 25), end: dt(2015, 2, 26), grain: Grain::Day});
125        assert_eq!(monwedfri.next().unwrap(),
126            Range{start: dt(2015, 2, 23), end: dt(2015, 2, 24), grain: Grain::Day});
127        assert_eq!(monwedfri.next().unwrap(),
128            Range{start: dt(2015, 2, 20), end: dt(2015, 2, 21), grain: Grain::Day});
129
130        // past-inclusive/raw
131        let monwed = Union(Weekday(1), Weekday(3));
132        let monwedfri = Union(monwed, Weekday(5));
133        let mut monwedfri = monwedfri._past_raw(&dt(2015, 2, 27));
134        assert_eq!(monwedfri.next().unwrap(),
135            Range{start: dt(2015, 2, 27), end: dt(2015, 2, 28), grain: Grain::Day});
136    }
137
138    #[test]
139    fn test_diff_resolution() {
140        use crate::seq_named::{Month, Weekday};
141
142        let mut mon_or_march = Union(Weekday(1), Month(3)).future(&dt(2015, 2, 27));
143        assert_eq!(mon_or_march.next().unwrap(),
144            Range{start: dt(2015, 3, 1), end: dt(2015, 4, 1), grain: Grain::Month});
145        assert_eq!(mon_or_march.next().unwrap(),
146            Range{start: dt(2015, 4, 6), end: dt(2015, 4, 7), grain: Grain::Day});
147        assert_eq!(mon_or_march.next().unwrap(),
148            Range{start: dt(2015, 4, 13), end: dt(2015, 4, 14), grain: Grain::Day});
149        let mut mon_or_march = mon_or_march.skip(46);
150        assert_eq!(mon_or_march.next().unwrap(),
151            Range{start: dt(2016, 3, 1), end: dt(2016, 4, 1), grain: Grain::Month});
152    }
153}