1use std::time::{SystemTime, Duration, UNIX_EPOCH};
35
36#[cfg(feature = "datetime")]
37pub use chrono;
38
39pub fn is_after_systemtime(time: &SystemTime, reference: &SystemTime) -> bool {
41 time > reference
42}
43
44pub fn is_before_systemtime(time: &SystemTime, reference: &SystemTime) -> bool {
46 time < reference
47}
48
49pub fn is_between_systemtime(time: &SystemTime, start: &SystemTime, end: &SystemTime) -> bool {
51 time >= start && time <= end
52}
53
54pub fn is_within_duration_systemtime(time: &SystemTime, duration: Duration) -> bool {
56 match SystemTime::now().duration_since(UNIX_EPOCH) {
57 Ok(now) => {
58 if let Ok(time_dur) = time.duration_since(UNIX_EPOCH) {
59 let diff = if now > time_dur {
60 now - time_dur
61 } else {
62 time_dur - now
63 };
64 diff <= duration
65 } else {
66 false
67 }
68 }
69 Err(_) => false,
70 }
71}
72
73pub fn add_duration_systemtime(time: &SystemTime, duration: Duration) -> SystemTime {
75 *time + duration
76}
77
78pub fn subtract_duration_systemtime(time: &SystemTime, duration: Duration) -> SystemTime {
80 *time - duration
81}
82
83#[cfg(feature = "datetime")]
85pub mod chrono_ops {
86 use chrono::{DateTime, TimeZone, Datelike, Timelike, Duration};
87
88 pub fn is_after<Tz: TimeZone>(time: &DateTime<Tz>, reference: &DateTime<Tz>) -> bool
90 where
91 Tz::Offset: std::fmt::Display,
92 {
93 time > reference
94 }
95
96 pub fn is_before<Tz: TimeZone>(time: &DateTime<Tz>, reference: &DateTime<Tz>) -> bool
98 where
99 Tz::Offset: std::fmt::Display,
100 {
101 time < reference
102 }
103
104 pub fn is_between<Tz: TimeZone>(
106 time: &DateTime<Tz>,
107 start: &DateTime<Tz>,
108 end: &DateTime<Tz>,
109 ) -> bool
110 where
111 Tz::Offset: std::fmt::Display,
112 {
113 time >= start && time <= end
114 }
115
116 pub fn is_today<Tz: TimeZone>(time: &DateTime<Tz>, now: &DateTime<Tz>) -> bool
118 where
119 Tz::Offset: std::fmt::Display,
120 {
121 time.date_naive() == now.date_naive()
122 }
123
124 pub fn is_within_duration<Tz: TimeZone>(
126 time: &DateTime<Tz>,
127 now: &DateTime<Tz>,
128 duration: Duration,
129 ) -> bool
130 where
131 Tz::Offset: std::fmt::Display,
132 {
133 let diff = if time > now {
134 time.clone() - now.clone()
135 } else {
136 now.clone() - time.clone()
137 };
138 diff <= duration
139 }
140
141 pub fn is_same_day<Tz: TimeZone>(time1: &DateTime<Tz>, time2: &DateTime<Tz>) -> bool
143 where
144 Tz::Offset: std::fmt::Display,
145 {
146 time1.date_naive() == time2.date_naive()
147 }
148
149 pub fn is_past<Tz: TimeZone>(time: &DateTime<Tz>, now: &DateTime<Tz>) -> bool
151 where
152 Tz::Offset: std::fmt::Display,
153 {
154 time < now
155 }
156
157 pub fn is_future<Tz: TimeZone>(time: &DateTime<Tz>, now: &DateTime<Tz>) -> bool
159 where
160 Tz::Offset: std::fmt::Display,
161 {
162 time > now
163 }
164
165 pub fn extract_year<Tz: TimeZone>(time: &DateTime<Tz>) -> i32
167 where
168 Tz::Offset: std::fmt::Display,
169 {
170 time.year()
171 }
172
173 pub fn extract_month<Tz: TimeZone>(time: &DateTime<Tz>) -> u32
175 where
176 Tz::Offset: std::fmt::Display,
177 {
178 time.month()
179 }
180
181 pub fn extract_day<Tz: TimeZone>(time: &DateTime<Tz>) -> u32
183 where
184 Tz::Offset: std::fmt::Display,
185 {
186 time.day()
187 }
188
189 pub fn extract_hour<Tz: TimeZone>(time: &DateTime<Tz>) -> u32
191 where
192 Tz::Offset: std::fmt::Display,
193 {
194 time.hour()
195 }
196
197 pub fn extract_minute<Tz: TimeZone>(time: &DateTime<Tz>) -> u32
199 where
200 Tz::Offset: std::fmt::Display,
201 {
202 time.minute()
203 }
204
205 pub fn extract_second<Tz: TimeZone>(time: &DateTime<Tz>) -> u32
207 where
208 Tz::Offset: std::fmt::Display,
209 {
210 time.second()
211 }
212
213 pub fn day_of_week<Tz: TimeZone>(time: &DateTime<Tz>) -> u32
215 where
216 Tz::Offset: std::fmt::Display,
217 {
218 time.weekday().num_days_from_monday()
219 }
220
221 pub fn is_weekend<Tz: TimeZone>(time: &DateTime<Tz>) -> bool
223 where
224 Tz::Offset: std::fmt::Display,
225 {
226 let weekday = time.weekday().num_days_from_monday();
227 weekday >= 5 }
229
230 pub fn is_weekday<Tz: TimeZone>(time: &DateTime<Tz>) -> bool
232 where
233 Tz::Offset: std::fmt::Display,
234 {
235 !is_weekend(time)
236 }
237
238 pub fn add_days<Tz: TimeZone>(time: &DateTime<Tz>, days: i64) -> DateTime<Tz>
240 where
241 Tz::Offset: std::fmt::Display,
242 {
243 time.clone() + Duration::days(days)
244 }
245
246 pub fn add_hours<Tz: TimeZone>(time: &DateTime<Tz>, hours: i64) -> DateTime<Tz>
248 where
249 Tz::Offset: std::fmt::Display,
250 {
251 time.clone() + Duration::hours(hours)
252 }
253
254 pub fn add_minutes<Tz: TimeZone>(time: &DateTime<Tz>, minutes: i64) -> DateTime<Tz>
256 where
257 Tz::Offset: std::fmt::Display,
258 {
259 time.clone() + Duration::minutes(minutes)
260 }
261
262 pub fn subtract_days<Tz: TimeZone>(time: &DateTime<Tz>, days: i64) -> DateTime<Tz>
264 where
265 Tz::Offset: std::fmt::Display,
266 {
267 time.clone() - Duration::days(days)
268 }
269
270 pub fn subtract_hours<Tz: TimeZone>(time: &DateTime<Tz>, hours: i64) -> DateTime<Tz>
272 where
273 Tz::Offset: std::fmt::Display,
274 {
275 time.clone() - Duration::hours(hours)
276 }
277
278 pub fn subtract_minutes<Tz: TimeZone>(time: &DateTime<Tz>, minutes: i64) -> DateTime<Tz>
280 where
281 Tz::Offset: std::fmt::Display,
282 {
283 time.clone() - Duration::minutes(minutes)
284 }
285
286 pub fn start_of_day<Tz: TimeZone + Clone>(time: &DateTime<Tz>) -> Option<DateTime<Tz>>
288 where
289 Tz::Offset: std::fmt::Display,
290 {
291 time.date_naive()
292 .and_hms_opt(0, 0, 0)
293 .and_then(|naive| time.timezone().from_local_datetime(&naive).single())
294 }
295
296 pub fn end_of_day<Tz: TimeZone + Clone>(time: &DateTime<Tz>) -> Option<DateTime<Tz>>
298 where
299 Tz::Offset: std::fmt::Display,
300 {
301 time.date_naive()
302 .and_hms_opt(23, 59, 59)
303 .and_then(|naive| time.timezone().from_local_datetime(&naive).single())
304 }
305
306 pub fn is_business_hours<Tz: TimeZone>(time: &DateTime<Tz>) -> bool
308 where
309 Tz::Offset: std::fmt::Display,
310 {
311 let hour = time.hour();
312 hour >= 9 && hour < 17
313 }
314
315 pub fn days_between<Tz: TimeZone>(time1: &DateTime<Tz>, time2: &DateTime<Tz>) -> i64
317 where
318 Tz::Offset: std::fmt::Display,
319 {
320 let diff = if time1 > time2 {
321 time1.clone() - time2.clone()
322 } else {
323 time2.clone() - time1.clone()
324 };
325 diff.num_days()
326 }
327
328 pub fn hours_between<Tz: TimeZone>(time1: &DateTime<Tz>, time2: &DateTime<Tz>) -> i64
330 where
331 Tz::Offset: std::fmt::Display,
332 {
333 let diff = if time1 > time2 {
334 time1.clone() - time2.clone()
335 } else {
336 time2.clone() - time1.clone()
337 };
338 diff.num_hours()
339 }
340}
341
342#[cfg(test)]
343#[cfg(feature = "datetime")]
344mod tests {
345 use super::*;
346 use chrono::{Utc, Duration, TimeZone};
347
348 #[test]
349 fn test_is_after() {
350 let now = Utc::now();
351 let future = now + Duration::hours(1);
352 let past = now - Duration::hours(1);
353
354 assert!(chrono_ops::is_after(&future, &now));
355 assert!(!chrono_ops::is_after(&past, &now));
356 }
357
358 #[test]
359 fn test_is_between() {
360 let start = Utc.with_ymd_and_hms(2024, 1, 1, 0, 0, 0).unwrap();
361 let end = Utc.with_ymd_and_hms(2024, 12, 31, 23, 59, 59).unwrap();
362 let middle = Utc.with_ymd_and_hms(2024, 6, 15, 12, 0, 0).unwrap();
363 let before = Utc.with_ymd_and_hms(2023, 1, 1, 0, 0, 0).unwrap();
364
365 assert!(chrono_ops::is_between(&middle, &start, &end));
366 assert!(!chrono_ops::is_between(&before, &start, &end));
367 }
368
369 #[test]
370 fn test_date_extraction() {
371 let dt = Utc.with_ymd_and_hms(2024, 3, 15, 14, 30, 45).unwrap();
372
373 assert_eq!(chrono_ops::extract_year(&dt), 2024);
374 assert_eq!(chrono_ops::extract_month(&dt), 3);
375 assert_eq!(chrono_ops::extract_day(&dt), 15);
376 assert_eq!(chrono_ops::extract_hour(&dt), 14);
377 assert_eq!(chrono_ops::extract_minute(&dt), 30);
378 assert_eq!(chrono_ops::extract_second(&dt), 45);
379 }
380
381 #[test]
382 fn test_date_arithmetic() {
383 let dt = Utc.with_ymd_and_hms(2024, 3, 15, 12, 0, 0).unwrap();
384 let future = chrono_ops::add_days(&dt, 10);
385 let past = chrono_ops::subtract_days(&dt, 5);
386
387 assert_eq!(chrono_ops::extract_day(&future), 25);
388 assert_eq!(chrono_ops::extract_day(&past), 10);
389 }
390
391 #[test]
392 fn test_is_weekend() {
393 let saturday = Utc.with_ymd_and_hms(2024, 3, 16, 12, 0, 0).unwrap();
395 let monday = Utc.with_ymd_and_hms(2024, 3, 18, 12, 0, 0).unwrap();
397
398 assert!(chrono_ops::is_weekend(&saturday));
399 assert!(!chrono_ops::is_weekend(&monday));
400 assert!(chrono_ops::is_weekday(&monday));
401 }
402
403 #[test]
404 fn test_is_business_hours() {
405 let morning = Utc.with_ymd_and_hms(2024, 3, 15, 10, 0, 0).unwrap();
406 let evening = Utc.with_ymd_and_hms(2024, 3, 15, 18, 0, 0).unwrap();
407
408 assert!(chrono_ops::is_business_hours(&morning));
409 assert!(!chrono_ops::is_business_hours(&evening));
410 }
411}
412