chrono_kit/iter/
naive_datetime_iter.rs1use chrono::{Duration, NaiveDateTime};
2use thiserror::Error;
3
4#[derive(Debug, Error)]
6pub enum NaiveDatetimeIterError {
7 #[error("Step duration cannot be zero")]
9 ZeroStep,
10 #[error("Invalid range: start {start} must be before end {end} for positive step")]
12 InvalidRange {
13 start: NaiveDateTime,
14 end: NaiveDateTime,
15 },
16}
17
18pub struct NaiveDatetimeIterator {
22 start: NaiveDateTime,
23 end: NaiveDateTime,
24 step: Duration,
25}
26
27impl NaiveDatetimeIterator {
28 pub fn new(
40 start: NaiveDateTime,
41 end: NaiveDateTime,
42 step: Duration,
43 ) -> Result<Self, NaiveDatetimeIterError> {
44 if step.is_zero() {
45 return Err(NaiveDatetimeIterError::ZeroStep);
46 }
47 if start > end {
48 return Err(NaiveDatetimeIterError::InvalidRange { start, end });
49 }
50 Ok(NaiveDatetimeIterator { start, end, step })
51 }
52
53 fn next_asc(&mut self) -> Option<NaiveDateTime> {
54 if self.start > self.end {
55 return None;
56 }
57
58 let result = self.start;
59 self.start = if self.start < self.end && self.start + self.step > self.end {
60 self.end
61 } else {
62 self.start + self.step
63 };
64
65 Some(result)
66 }
67
68 fn next_desc(&mut self) -> Option<NaiveDateTime> {
69 if self.end < self.start {
70 return None;
71 }
72
73 let result = self.end;
74 self.end = if self.end > self.start && self.end + self.step < self.start {
75 self.start
76 } else {
77 self.end + self.step
78 };
79
80 Some(result)
81 }
82}
83
84impl Iterator for NaiveDatetimeIterator {
85 type Item = NaiveDateTime;
86
87 fn next(&mut self) -> Option<Self::Item> {
88 if self.step > Duration::zero() {
89 self.next_asc()
90 } else {
91 self.next_desc()
92 }
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn test_ascending_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 = NaiveDatetimeIterator::new(start, end, step).unwrap();
109 assert_eq!(iter.next(), Some(start));
110 assert_eq!(iter.next(), Some(start + step));
111 assert_eq!(iter.next(), Some(end));
112 assert_eq!(iter.next(), None);
113 }
114
115 #[test]
116 fn test_descending_iteration() {
117 let start =
118 NaiveDateTime::parse_from_str("2023-01-01 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
119 let end =
120 NaiveDateTime::parse_from_str("2023-01-03 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
121 let step = Duration::days(-1);
122
123 let mut iter = NaiveDatetimeIterator::new(start, end, step).unwrap();
124 assert_eq!(iter.next(), Some(end));
125 assert_eq!(iter.next(), Some(end + step));
126 assert_eq!(iter.next(), Some(start));
127 assert_eq!(iter.next(), None);
128 }
129
130 #[test]
131 fn test_zero_step_error() {
132 let start =
133 NaiveDateTime::parse_from_str("2023-01-01 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
134 let end =
135 NaiveDateTime::parse_from_str("2023-01-03 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
136 let step = Duration::zero();
137
138 let result = NaiveDatetimeIterator::new(start, end, step);
139 assert!(matches!(result, Err(NaiveDatetimeIterError::ZeroStep)));
140 }
141
142 #[test]
143 fn test_invalid_range_error() {
144 let start =
145 NaiveDateTime::parse_from_str("2023-01-03 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
146 let end =
147 NaiveDateTime::parse_from_str("2023-01-01 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
148 let step = Duration::days(1);
149
150 let result = NaiveDatetimeIterator::new(start, end, step);
151 assert!(matches!(
152 result,
153 Err(NaiveDatetimeIterError::InvalidRange { .. })
154 ));
155 }
156
157 #[test]
158 fn test_non_integer_step() {
159 let start =
160 NaiveDateTime::parse_from_str("2023-01-01 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
161 let end =
162 NaiveDateTime::parse_from_str("2023-01-03 12:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
163 let step = Duration::days(1);
164
165 let mut iter = NaiveDatetimeIterator::new(start, end, step).unwrap();
166 assert_eq!(iter.next(), Some(start));
167 assert_eq!(iter.next(), Some(start + step));
168 assert_eq!(iter.next(), Some(start + step * 2));
169 assert_eq!(iter.next(), Some(end));
170 }
171}