time_range_ext/time_range/
vec_range.rs

1use time::OffsetDateTime;
2use crate::time_range::{TimeRange};
3use crate::time_range::time_range_ext::TimeRangeExt;
4
5impl TimeRangeExt for Vec<TimeRange> {
6    fn ends(&self) -> Vec<OffsetDateTime> {
7        self.iter().map(|t| t.end).collect()
8    }
9    fn starts(&self) -> Vec<OffsetDateTime> {
10        self.iter().map(|t| t.start).collect()
11    }
12
13    fn contains_ts(&self, ts: OffsetDateTime) -> bool {
14        self.iter().any(|t| t.start <= ts && ts <= t.end)
15    }
16
17    fn range_within_ts(&self, ts: OffsetDateTime) -> Option<&TimeRange> {
18        self.iter().find(|t| t.start <= ts && ts <= t.end)
19    }
20
21    fn overlaps(&self, other: &TimeRange) -> Option<&TimeRange> {
22        self.iter().find(|r| other.overlaps(r))
23    }
24
25    fn get_overlapping_range(&self, tr: TimeRange) -> Option<TimeRange> {
26        let range_start = self.range_within_ts(tr.start);
27        let range_end = self.range_within_ts(tr.end);
28
29        let within_range = self.overlaps(&tr);
30
31        match (range_start, range_end, within_range) {
32            (Some(start), Some(end), _) => {
33                // To cover the last test case if there are gaps in between, but the end of the
34                // passed tr just hits the start of a new time range in the vec
35                if start.end != end.end {
36                    Some(TimeRange {
37                        start: tr.start,
38                        end: start.end,
39                    })
40                } else {
41                    Some(TimeRange {
42                        start: tr.start,
43                        end: tr.end,
44                    })
45                }
46            }
47            (Some(start), None, _) => Some(TimeRange {
48                start: tr.start,
49                end: start.end,
50            }),
51            (None, Some(end), _) => Some(TimeRange {
52                start: end.start,
53                end: tr.end,
54            }),
55            (None, None, Some(within)) => Some(*within),
56            (None, None, None) => None,
57        }
58    }
59
60    fn dedup_overlapping_ranges(mut self) -> Self {
61        if self.is_empty() {
62            return self;
63        }
64
65        self.sort_by(|a, b| a.start.cmp(&b.start));
66
67        let mut result = Vec::new();
68
69        let mut current_range = self[0];
70        for range in self.into_iter().skip(1) {
71            if current_range.overlaps(&range) {
72                current_range = current_range.merge(&range);
73            } else {
74                result.push(current_range);
75                current_range = range;
76            }
77        }
78        result.push(current_range);
79
80        result
81    }
82
83    fn times_between_contents(self, bounds: Option<TimeRange>) -> Self {
84        let mut times_between = vec![];
85        let mut windows = self.windows(2);
86
87        if self.len() == 1 {
88            if let Some(bounds) = bounds {
89                if self[0].end < bounds.end {
90                    times_between.push(TimeRange {
91                        start: self[0].end,
92                        end: bounds.end,
93                    });
94                }
95            }
96
97            return times_between;
98        }
99
100        while let Some(&[current, next]) = windows.next() {
101            if current.end == next.start {
102                continue;
103            }
104
105            let start = current.end;
106            let end = next.start;
107
108            times_between.push(TimeRange { start, end });
109        }
110
111        times_between
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use time::macros::datetime;
118    use crate::time_range::time_range_ext::TimeRangeExt;
119
120    #[test]
121    fn test_overlapping_range() {
122        let ranges = vec![
123            super::TimeRange {
124                start: time::OffsetDateTime::from_unix_timestamp(0).unwrap(),
125                end: time::OffsetDateTime::from_unix_timestamp(10).unwrap(),
126            },
127            super::TimeRange {
128                start: time::OffsetDateTime::from_unix_timestamp(15).unwrap(),
129                end: time::OffsetDateTime::from_unix_timestamp(20).unwrap(),
130            },
131            super::TimeRange {
132                start: time::OffsetDateTime::from_unix_timestamp(20).unwrap(),
133                end: time::OffsetDateTime::from_unix_timestamp(30).unwrap(),
134            },
135        ];
136
137        let tr = super::TimeRange {
138            start: time::OffsetDateTime::from_unix_timestamp(5).unwrap(),
139            end: time::OffsetDateTime::from_unix_timestamp(8).unwrap(),
140        };
141
142        let overlapping_range = ranges.get_overlapping_range(tr);
143
144        assert_eq!(
145            overlapping_range,
146            Some(super::TimeRange {
147                start: time::OffsetDateTime::from_unix_timestamp(5).unwrap(),
148                end: time::OffsetDateTime::from_unix_timestamp(8).unwrap(),
149            })
150        );
151
152        let tr = super::TimeRange {
153            start: time::OffsetDateTime::from_unix_timestamp(5).unwrap(),
154            end: time::OffsetDateTime::from_unix_timestamp(15).unwrap(),
155        };
156
157        let overlapping_range = ranges.get_overlapping_range(tr);
158
159        assert_eq!(
160            overlapping_range,
161            Some(super::TimeRange {
162                start: time::OffsetDateTime::from_unix_timestamp(5).unwrap(),
163                end: time::OffsetDateTime::from_unix_timestamp(10).unwrap(),
164            })
165        );
166
167        let ranges = vec![super::TimeRange {
168            start: datetime!(2024-04-21 23:10:15.502 UTC),
169            end: datetime!(2024-04-22 05:30:00.000 UTC),
170        }];
171
172        let tr = super::TimeRange {
173            start: datetime!(2024-04-20 16:25:25.632 UTC),
174            end: datetime!(2024-04-22 05:45:03.018 UTC),
175        };
176
177        assert_eq!(
178            ranges.get_overlapping_range(tr),
179            Some(super::TimeRange {
180                start: datetime!(2024-04-21 23:10:15.502 UTC),
181                end: datetime!(2024-04-22 05:30:00.000 UTC),
182            })
183        );
184    }
185
186    #[test]
187    fn test_deduping() {
188        let ranges = vec![
189            super::TimeRange {
190                start: time::OffsetDateTime::from_unix_timestamp(0).unwrap(),
191                end: time::OffsetDateTime::from_unix_timestamp(10).unwrap(),
192            },
193            super::TimeRange {
194                start: time::OffsetDateTime::from_unix_timestamp(5).unwrap(),
195                end: time::OffsetDateTime::from_unix_timestamp(15).unwrap(),
196            },
197            super::TimeRange {
198                start: time::OffsetDateTime::from_unix_timestamp(20).unwrap(),
199                end: time::OffsetDateTime::from_unix_timestamp(30).unwrap(),
200            },
201        ];
202
203        let deduped_ranges = ranges.dedup_overlapping_ranges();
204
205        assert_eq!(
206            deduped_ranges,
207            vec![
208                super::TimeRange {
209                    start: time::OffsetDateTime::from_unix_timestamp(0).unwrap(),
210                    end: time::OffsetDateTime::from_unix_timestamp(15).unwrap(),
211                },
212                super::TimeRange {
213                    start: time::OffsetDateTime::from_unix_timestamp(20).unwrap(),
214                    end: time::OffsetDateTime::from_unix_timestamp(30).unwrap(),
215                },
216            ]
217        );
218
219        let ranges = vec![
220            super::TimeRange {
221                start: time::OffsetDateTime::from_unix_timestamp(0).unwrap(),
222                end: time::OffsetDateTime::from_unix_timestamp(10).unwrap(),
223            },
224            super::TimeRange {
225                start: time::OffsetDateTime::from_unix_timestamp(5).unwrap(),
226                end: time::OffsetDateTime::from_unix_timestamp(15).unwrap(),
227            },
228            super::TimeRange {
229                start: time::OffsetDateTime::from_unix_timestamp(0).unwrap(),
230                end: time::OffsetDateTime::from_unix_timestamp(50).unwrap(),
231            },
232            super::TimeRange {
233                start: time::OffsetDateTime::from_unix_timestamp(20).unwrap(),
234                end: time::OffsetDateTime::from_unix_timestamp(30).unwrap(),
235            },
236        ];
237
238        let deduped_ranges = ranges.dedup_overlapping_ranges();
239
240        assert_eq!(
241            deduped_ranges,
242            vec![super::TimeRange {
243                start: time::OffsetDateTime::from_unix_timestamp(0).unwrap(),
244                end: time::OffsetDateTime::from_unix_timestamp(50).unwrap(),
245            },]
246        );
247    }
248}