chrono_kit/iter/
naive_datetime_range_iter.rs1use super::naive_datetime_iter::NaiveDatetimeIterError;
2use super::naive_datetime_iter::NaiveDatetimeIterator;
3use chrono::{Duration, NaiveDateTime};
4
5pub struct NaiveDatetimeRangeIterator {
40 datetime_iter: NaiveDatetimeIterator,
41 current: Option<NaiveDateTime>,
42 asc: bool,
43}
44
45impl NaiveDatetimeRangeIterator {
46 pub fn new(
58 start: NaiveDateTime,
59 end: NaiveDateTime,
60 step: Duration,
61 ) -> Result<Self, NaiveDatetimeIterError> {
62 let datetime_iter = NaiveDatetimeIterator::new(start, end, step)?;
63
64 Ok(NaiveDatetimeRangeIterator {
65 datetime_iter,
66 current: None,
67 asc: step > Duration::zero(),
68 })
69 }
70}
71
72impl Iterator for NaiveDatetimeRangeIterator {
73 type Item = (NaiveDateTime, NaiveDateTime);
74
75 fn next(&mut self) -> Option<Self::Item> {
76 let start = match self.current {
77 Some(dt) => dt,
78 None => {
79 let first = self.datetime_iter.next()?;
80 self.current = Some(first);
81 first
82 }
83 };
84
85 let end = self.datetime_iter.next()?;
86 self.current = Some(end);
87 if self.asc {
88 Some((start, end))
89 } else {
90 Some((end, start))
91 }
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use chrono::NaiveDateTime;
99
100 #[test]
101 fn test_ascending_range_iteration() {
102 let start =
103 NaiveDateTime::parse_from_str("2023-01-01 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
104 let end =
105 NaiveDateTime::parse_from_str("2023-01-03 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
106 let step = Duration::days(1);
107
108 let mut iter = NaiveDatetimeRangeIterator::new(start, end, step).unwrap();
109 assert_eq!(iter.next(), Some((start, start + step)));
110 assert_eq!(iter.next(), Some((start + step, end)));
111 assert_eq!(iter.next(), None);
112 }
113
114 #[test]
115 fn test_descending_range_iteration() {
116 let start =
117 NaiveDateTime::parse_from_str("2023-01-01 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
118 let end =
119 NaiveDateTime::parse_from_str("2023-01-03 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
120 let step = Duration::days(-1);
121
122 let mut iter = NaiveDatetimeRangeIterator::new(start, end, step).unwrap();
123 assert_eq!(iter.next(), Some((end + step, end)));
124 assert_eq!(iter.next(), Some((start, end + step)));
125 assert_eq!(iter.next(), None);
126 }
127
128 #[test]
129 fn test_non_integer_period() {
130 let start =
131 NaiveDateTime::parse_from_str("2023-01-01 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
132 let end =
133 NaiveDateTime::parse_from_str("2023-01-01 12:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
134 let step = Duration::hours(9);
135
136 let mut iter = NaiveDatetimeRangeIterator::new(start, end, step).unwrap();
137 assert_eq!(iter.next(), Some((start, start + step)));
138 assert_eq!(iter.next(), Some((start + step, end)));
139 assert_eq!(iter.next(), None);
140 }
141
142 #[test]
143 fn test_single_range() {
144 let start =
145 NaiveDateTime::parse_from_str("2023-01-01 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
146 let end =
147 NaiveDateTime::parse_from_str("2023-01-01 12:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
148 let step = Duration::hours(12);
149
150 let mut iter = NaiveDatetimeRangeIterator::new(start, end, step).unwrap();
151 assert_eq!(iter.next(), Some((start, end)));
152 assert_eq!(iter.next(), None);
153 }
154
155 #[test]
156 fn test_zero_step_error() {
157 let start =
158 NaiveDateTime::parse_from_str("2023-01-01 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
159 let end =
160 NaiveDateTime::parse_from_str("2023-01-03 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
161 let step = Duration::zero();
162
163 let result = NaiveDatetimeRangeIterator::new(start, end, step);
164 assert!(matches!(result, Err(NaiveDatetimeIterError::ZeroStep)));
165 }
166
167 #[test]
168 fn test_invalid_range_error() {
169 let start =
170 NaiveDateTime::parse_from_str("2023-01-03 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
171 let end =
172 NaiveDateTime::parse_from_str("2023-01-01 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
173 let step = Duration::days(1);
174
175 let result = NaiveDatetimeRangeIterator::new(start, end, step);
176 assert!(matches!(
177 result,
178 Err(NaiveDatetimeIterError::InvalidRange { .. })
179 ));
180 }
181}