1use core::fmt;
2use std::error;
3
4use chrono::{Duration, NaiveDate, NaiveDateTime, Utc};
5
6mod iter;
7pub use iter::DateRangeIterator;
8
9#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
10pub struct DateRange {
11 since_: NaiveDate,
12 until_: NaiveDate,
13}
14
15impl Default for DateRange {
16 fn default() -> Self {
17 Self {
18 since_: NaiveDate::from_ymd_opt(1970, 1, 1).expect(""),
19 until_: NaiveDate::from_ymd_opt(1970, 1, 1).expect(""),
20 }
21 }
22}
23
24impl DateRange {
25 pub fn new(since: NaiveDate, until: NaiveDate) -> Result<Self, Error> {
26 if since > until {
27 return Err(Error::SinceShouldLtUntil);
28 }
29
30 Ok(Self {
31 since_: since,
32 until_: until,
33 })
34 }
35 pub fn from_timestamp(
36 since: i64,
37 mut until: i64,
38 configuration: impl Into<Option<DateRangeFromTimestampConfiguration>>,
39 ) -> Result<Self, Error> {
40 let configuration = configuration.into().unwrap_or_default();
41
42 if since > until {
43 return Err(Error::SinceShouldLtUntil);
44 }
45
46 if configuration.now_gteq_until {
47 let now = Utc::now().timestamp();
48 if until > now {
49 until = now;
50
51 if since > until {
52 return Err(Error::SinceShouldLtUntil);
53 }
54 }
55 }
56
57 let since = NaiveDateTime::from_timestamp_opt(since, 0).expect("");
58 let until = NaiveDateTime::from_timestamp_opt(until, 0).expect("");
59
60 let mut since = since.date();
61 let until = until.date();
62
63 if (until - since).num_days() > configuration.max_interval.num_days() {
64 since = until - configuration.max_interval;
65 }
66
67 Ok(Self {
68 since_: since,
69 until_: until,
70 })
71 }
72
73 pub fn since(&self) -> NaiveDate {
74 self.since_.to_owned()
75 }
76 pub fn until(&self) -> NaiveDate {
77 self.until_.to_owned()
78 }
79
80 pub fn since_datetime(&self) -> NaiveDateTime {
81 self.since().and_hms_opt(0, 0, 0).expect("")
82 }
83 pub fn until_datetime(&self) -> NaiveDateTime {
84 self.until().and_hms_opt(23, 59, 59).expect("")
85 }
86
87 pub fn prev(&self) -> Self {
88 let prev_until = self.since() - Duration::days(1);
89 let len = self.until() - self.since();
90 Self {
91 since_: prev_until - len,
92 until_: prev_until,
93 }
94 }
95 pub fn next(&self) -> Self {
96 let next_since = self.until() + Duration::days(1);
97 let len = self.until() - self.since();
98 Self {
99 since_: next_since,
100 until_: next_since + len,
101 }
102 }
103
104 pub fn num_days(&self) -> usize {
106 (self.until() - self.since()).num_days() as usize
107 }
108}
109
110#[derive(Debug, Copy, Clone)]
111pub struct DateRangeFromTimestampConfiguration {
112 pub now_gteq_until: bool,
113 pub max_interval: Duration,
114}
115impl Default for DateRangeFromTimestampConfiguration {
116 fn default() -> Self {
117 Self {
118 now_gteq_until: true,
119 max_interval: Duration::days(2 * 365), }
121 }
122}
123impl DateRangeFromTimestampConfiguration {
124 pub fn new() -> Self {
125 Self::default()
126 }
127}
128
129#[derive(Debug, Copy, Clone)]
133pub struct DateTimeRange {
134 since_: NaiveDateTime,
135 until_: NaiveDateTime,
136}
137impl DateTimeRange {
138 pub fn new(since: NaiveDateTime, until: NaiveDateTime) -> Result<Self, Error> {
139 if since > until {
140 return Err(Error::SinceShouldLtUntil);
141 }
142
143 Ok(Self {
144 since_: since,
145 until_: until,
146 })
147 }
148
149 pub fn since(&self) -> NaiveDateTime {
150 self.since_.to_owned()
151 }
152 pub fn until(&self) -> NaiveDateTime {
153 self.until_.to_owned()
154 }
155}
156impl From<&DateRange> for DateTimeRange {
157 fn from(dr: &DateRange) -> Self {
158 Self {
159 since_: dr.since_datetime(),
160 until_: dr.until_datetime(),
161 }
162 }
163}
164impl From<DateRange> for DateTimeRange {
165 fn from(dr: DateRange) -> Self {
166 Self {
167 since_: dr.since_datetime(),
168 until_: dr.until_datetime(),
169 }
170 }
171}
172
173#[derive(Debug)]
177pub enum Error {
178 SinceShouldLtUntil,
179}
180impl fmt::Display for Error {
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 write!(f, "{self:?}")
183 }
184}
185impl error::Error for Error {}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 use std::error;
192
193 #[test]
194 fn test_date_range_default() {
195 assert_eq!(
196 DateRange::default(),
197 DateRange::new("1970-01-01".parse().unwrap(), "1970-01-01".parse().unwrap()).unwrap()
198 )
199 }
200
201 #[test]
202 fn test_date_range_new() -> Result<(), Box<dyn error::Error>> {
203 match DateRange::new("2021-08-02".parse().unwrap(), "2021-08-02".parse().unwrap()) {
204 Ok(_) => {}
205 Err(err) => {
206 panic!("{}", err)
207 }
208 }
209 match DateRange::new("2021-08-02".parse().unwrap(), "2021-08-01".parse().unwrap()) {
210 Ok(_) => panic!(),
211 Err(Error::SinceShouldLtUntil) => {}
212 }
213
214 Ok(())
215 }
216
217 #[test]
218 fn test_date_range_from_timestamp() {
219 let now = Utc::now();
220
221 let date_range = DateRange::from_timestamp(
223 (now - Duration::days(7)).timestamp(),
224 (now + Duration::days(3)).timestamp(),
225 None,
226 )
227 .unwrap();
228 assert_eq!(
229 date_range.since(),
230 now.naive_utc().date() - Duration::days(7)
231 );
232 assert_eq!(date_range.until(), now.naive_utc().date());
233
234 let mut configuration = DateRangeFromTimestampConfiguration::new();
236 configuration.now_gteq_until = false;
237 let date_range = DateRange::from_timestamp(
238 (now - Duration::days(7)).timestamp(),
239 (now + Duration::days(3)).timestamp(),
240 configuration,
241 )
242 .unwrap();
243 assert_eq!(
244 date_range.since(),
245 now.naive_utc().date() - Duration::days(7)
246 );
247 assert_eq!(
248 date_range.until(),
249 now.naive_utc().date() + Duration::days(3)
250 );
251
252 let date_range = DateRange::from_timestamp(
254 (now - Duration::days(10)).timestamp(),
255 now.timestamp(),
256 Some(DateRangeFromTimestampConfiguration {
257 now_gteq_until: true,
258 max_interval: Duration::days(7),
259 }),
260 )
261 .unwrap();
262 assert_eq!(
263 date_range.since(),
264 now.naive_utc().date() - Duration::days(7)
265 );
266 assert_eq!(date_range.until(), now.naive_utc().date());
267 }
268
269 #[test]
270 fn test_date_range_since_datetime_and_until_datetime() {
271 let date_range =
272 DateRange::new("2021-08-09".parse().unwrap(), "2021-08-15".parse().unwrap()).unwrap();
273
274 assert_eq!(
275 date_range.since_datetime(),
276 "2021-08-09T00:00:00".parse().unwrap()
277 );
278 assert_eq!(
279 date_range.until_datetime(),
280 "2021-08-15T23:59:59".parse().unwrap()
281 );
282 }
283
284 #[test]
285 fn test_date_range_prev_and_next() {
286 let date_range =
287 DateRange::new("2021-08-09".parse().unwrap(), "2021-08-15".parse().unwrap()).unwrap();
288
289 let prev_date_range = date_range.prev();
290 assert_eq!(prev_date_range.since(), "2021-08-02".parse().unwrap());
291 assert_eq!(prev_date_range.until(), "2021-08-08".parse().unwrap());
292
293 let next_date_range = date_range.next();
294 assert_eq!(next_date_range.since(), "2021-08-16".parse().unwrap());
295 assert_eq!(next_date_range.until(), "2021-08-22".parse().unwrap());
296 }
297
298 #[test]
299 fn test_date_range_num_days() {
300 let date_range =
301 DateRange::new("2022-02-23".parse().unwrap(), "2022-02-23".parse().unwrap()).unwrap();
302 assert_eq!(date_range.num_days(), 0);
303
304 let date_range =
305 DateRange::new("2022-02-21".parse().unwrap(), "2022-02-23".parse().unwrap()).unwrap();
306 assert_eq!(date_range.num_days(), 2);
307
308 let now = Utc::now();
309 let date_range =
310 DateRange::from_timestamp((now - Duration::days(7)).timestamp(), now.timestamp(), None)
311 .unwrap();
312 assert_eq!(date_range.num_days(), 7);
313 }
314}