1#[cfg(feature = "chrono")]
2mod chrono_support;
3#[cfg(feature = "jiff")]
4mod jiff_support;
5#[cfg(feature = "time")]
6mod time_support;
7
8use core::cmp::Ordering;
9
10use super::constants::*;
11
12#[derive(Debug)]
13struct TimeDiffResult {
14 pub(crate) hours: i32,
15 pub(crate) minutes: i32,
16 pub(crate) seconds: i32,
17 pub(crate) nanoseconds: i32,
18}
19
20#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
22pub struct DateDiffResult {
23 pub years: i32,
24 pub months: i32,
25 pub days: i32,
26}
27
28impl DateDiffResult {
29 #[doc(hidden)]
30 #[inline]
31 pub fn into_neg(mut self) -> Self {
32 self.years *= -1;
33 self.months *= -1;
34 self.days *= -1;
35
36 self
37 }
38}
39
40#[derive(Debug, Clone, Copy)]
42pub(crate) struct DateTimeFields {
43 pub(crate) year: i32,
44 pub(crate) month: u8,
45 pub(crate) day: u8,
46 pub(crate) hour: u8,
47 pub(crate) minute: u8,
48 pub(crate) second: u8,
49 pub(crate) nanosecond: u32,
50}
51
52impl DateTimeFields {
53 #[inline]
54 pub(crate) fn from_parts(date_time: &impl DateTimeParts) -> Self {
55 let (year, month, day, hour, minute, second, nanosecond) = date_time.all_parts();
56
57 Self {
58 year,
59 month,
60 day,
61 hour,
62 minute,
63 second,
64 nanosecond,
65 }
66 }
67}
68
69#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
72struct WallClock {
73 year: i32,
74 month: u8,
75 day: u8,
76 nanoseconds_of_day: u64,
77}
78
79impl WallClock {
80 #[inline]
81 fn from_parts(date_time: &impl DateTimeParts) -> Self {
82 let fields = DateTimeFields::from_parts(date_time);
83
84 Self {
85 year: fields.year,
86 month: fields.month,
87 day: fields.day,
88 nanoseconds_of_day: (fields.hour as u64 * HOUR_NANOSECONDS)
89 + (fields.minute as u64 * MINUTE_NANOSECONDS)
90 + (fields.second as u64 * SECOND_NANOSECONDS)
91 + fields.nanosecond as u64,
92 }
93 }
94}
95
96#[derive(Debug)]
97struct _DateDiffResult {
98 pub(crate) earlier_nanoseconds_of_day: u64,
99 pub(crate) later_nanoseconds_of_day: u64,
100 pub(crate) result: DateDiffResult,
101}
102
103#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
105pub struct DateTimeDiffResult {
106 pub years: i32,
107 pub months: i32,
108 pub days: i32,
109 pub hours: i32,
110 pub minutes: i32,
111 pub seconds: i32,
112 pub nanoseconds: i32,
113}
114
115impl DateTimeDiffResult {
116 #[doc(hidden)]
117 #[inline]
118 pub fn into_neg(mut self) -> Self {
119 self.years *= -1;
120 self.months *= -1;
121 self.days *= -1;
122 self.hours *= -1;
123 self.minutes *= -1;
124 self.seconds *= -1;
125 self.nanoseconds *= -1;
126
127 self
128 }
129}
130
131impl From<DateDiffResult> for DateTimeDiffResult {
132 #[inline]
133 fn from(value: DateDiffResult) -> Self {
134 DateTimeDiffResult {
135 years: value.years,
136 months: value.months,
137 days: value.days,
138 ..DateTimeDiffResult::default()
139 }
140 }
141}
142
143impl From<DateTimeDiffResult> for DateDiffResult {
144 #[inline]
145 fn from(value: DateTimeDiffResult) -> Self {
146 DateDiffResult {
147 years: value.years, months: value.months, days: value.days
148 }
149 }
150}
151
152pub trait DateTimeDiff {
154 #[inline]
155 fn years(&self) -> i32 {
156 0
157 }
158
159 #[inline]
160 fn months(&self) -> i32 {
161 0
162 }
163
164 #[inline]
165 fn days(&self) -> i32 {
166 0
167 }
168
169 #[inline]
170 fn hours(&self) -> i32 {
171 0
172 }
173
174 #[inline]
175 fn minutes(&self) -> i32 {
176 0
177 }
178
179 #[inline]
180 fn seconds(&self) -> i32 {
181 0
182 }
183
184 #[inline]
185 fn nanoseconds(&self) -> i32 {
186 0
187 }
188}
189
190pub trait DateTimeParts: Ord {
199 fn year(&self) -> i32;
201
202 fn month(&self) -> u8;
204
205 fn day(&self) -> u8;
207
208 fn hour(&self) -> u8;
210
211 fn minute(&self) -> u8;
213
214 fn second(&self) -> u8;
216
217 fn nanosecond(&self) -> u32;
219
220 #[doc(hidden)]
222 #[inline]
223 fn all_parts(&self) -> (i32, u8, u8, u8, u8, u8, u32) {
224 (
225 self.year(),
226 self.month(),
227 self.day(),
228 self.hour(),
229 self.minute(),
230 self.second(),
231 self.nanosecond(),
232 )
233 }
234
235 #[doc(hidden)]
237 #[inline]
238 fn to_same_time_zone(&self, other: Self) -> Self
239 where
240 Self: Sized, {
241 other
242 }
243}
244
245impl DateTimeDiff for DateDiffResult {
246 #[inline]
247 fn years(&self) -> i32 {
248 self.years
249 }
250
251 #[inline]
252 fn months(&self) -> i32 {
253 self.months
254 }
255
256 #[inline]
257 fn days(&self) -> i32 {
258 self.days
259 }
260}
261
262impl DateTimeDiff for DateTimeDiffResult {
263 #[inline]
264 fn years(&self) -> i32 {
265 self.years
266 }
267
268 #[inline]
269 fn months(&self) -> i32 {
270 self.months
271 }
272
273 #[inline]
274 fn days(&self) -> i32 {
275 self.days
276 }
277
278 #[inline]
279 fn hours(&self) -> i32 {
280 self.hours
281 }
282
283 #[inline]
284 fn minutes(&self) -> i32 {
285 self.minutes
286 }
287
288 #[inline]
289 fn seconds(&self) -> i32 {
290 self.seconds
291 }
292
293 #[inline]
294 fn nanoseconds(&self) -> i32 {
295 self.nanoseconds
296 }
297}
298
299#[inline]
300const fn _nanoseconds_to_units(mut nanoseconds: u64) -> TimeDiffResult {
301 let h = nanoseconds / HOUR_NANOSECONDS;
302 nanoseconds -= h * HOUR_NANOSECONDS;
303
304 let m = nanoseconds / MINUTE_NANOSECONDS;
305 nanoseconds -= m * MINUTE_NANOSECONDS;
306
307 let s = nanoseconds / SECOND_NANOSECONDS;
308 nanoseconds -= s * SECOND_NANOSECONDS;
309
310 TimeDiffResult {
311 hours: h as i32,
312 minutes: m as i32,
313 seconds: s as i32,
314 nanoseconds: nanoseconds as i32,
315 }
316}
317
318#[inline]
319const fn _time_diff(
320 earlier_nanoseconds_of_day: u64,
321 later_nanoseconds_of_day: u64,
322) -> TimeDiffResult {
323 let nanoseconds = if later_nanoseconds_of_day >= earlier_nanoseconds_of_day {
324 later_nanoseconds_of_day - earlier_nanoseconds_of_day
325 } else {
326 DAY_NANOSECONDS + later_nanoseconds_of_day - earlier_nanoseconds_of_day
327 };
328
329 _nanoseconds_to_units(nanoseconds)
330}
331
332fn _date_diff(earlier: &WallClock, later: &WallClock, start_from_later: bool) -> _DateDiffResult {
333 let mut earlier_year = earlier.year;
334 let mut earlier_month = earlier.month;
335 let mut earlier_date = earlier.day;
336
337 let mut later_year = later.year;
338 let mut later_month = later.month;
339 let mut later_date = later.day;
340
341 let later_nanoseconds_of_day = later.nanoseconds_of_day;
342 let earlier_nanoseconds_of_day = earlier.nanoseconds_of_day;
343
344 let years: i32;
345 let months: i32;
346 let days: i32;
347
348 if later_nanoseconds_of_day < earlier_nanoseconds_of_day {
349 if start_from_later {
352 if earlier_date < year_helper::get_days_in_month(earlier_year, earlier_month).unwrap() {
355 earlier_date += 1;
358 } else if earlier_month < 12 {
359 earlier_month += 1;
362 earlier_date = 1;
363 } else {
364 earlier_year += 1;
367 earlier_month = 1;
368 earlier_date = 1;
369 }
370 } else {
371 if later_date > 1 {
374 later_date -= 1;
377 } else if later_month > 1 {
378 later_month -= 1;
381 later_date = year_helper::get_days_in_month(later_year, later_month).unwrap();
382 } else {
383 later_year -= 1;
386 later_month = 12;
387 later_date = 31;
388 }
389 }
390 }
391
392 let year_diff = later_year - earlier_year;
393 let month_diff = later_month as i32 - earlier_month as i32;
394
395 match month_diff.cmp(&0) {
396 Ordering::Greater => {
397 years = year_diff;
400
401 if later_date >= earlier_date {
402 months = month_diff;
405 } else {
406 months = month_diff - 1;
409 }
410 },
411 Ordering::Less => {
412 years = year_diff - 1;
415
416 if later_date >= earlier_date {
417 months = month_diff + 12;
420 } else {
421 months = month_diff + 11;
424 }
425 },
426 Ordering::Equal => {
427 if later_date >= earlier_date {
430 years = year_diff;
433 months = 0;
434 } else {
435 years = year_diff - 1;
438 months = 11;
439 }
440 },
441 }
442
443 if later_date >= earlier_date {
444 if start_from_later {
447 days = later_date
448 .min(year_helper::get_days_in_month(earlier_year, earlier_month).unwrap())
449 as i32
450 - earlier_date as i32;
451 } else {
452 days = later_date as i32 - earlier_date as i32;
453 }
454 } else {
455 if start_from_later {
458 if earlier_month < 12 {
459 later_date = later_date
460 .min(year_helper::get_days_in_month(earlier_year, earlier_month + 1).unwrap())
461 } else {
462 }
464
465 days = (later_date
466 + (year_helper::get_days_in_month(earlier_year, earlier_month).unwrap()
467 - earlier_date)) as i32;
468 } else {
469 let days_in_month = if later_month > 1 {
470 year_helper::get_days_in_month(later_year, later_month - 1).unwrap()
471 } else {
472 31 };
474
475 if days_in_month > earlier_date {
476 days = (later_date + (days_in_month - earlier_date)) as i32;
477 } else {
478 days = later_date as i32;
479 }
480 }
481 }
482
483 _DateDiffResult {
484 earlier_nanoseconds_of_day,
485 later_nanoseconds_of_day,
486 result: DateDiffResult {
487 years,
488 months,
489 days,
490 },
491 }
492}
493
494#[inline]
524pub fn date_diff<DT: DateTimeParts>(from: DT, to: DT) -> DateDiffResult {
525 let to = WallClock::from_parts(&from.to_same_time_zone(to));
526 let from = WallClock::from_parts(&from);
527
528 match to.cmp(&from) {
529 Ordering::Greater => _date_diff(&from, &to, false).result,
530 Ordering::Less => _date_diff(&to, &from, true).result.into_neg(),
531 Ordering::Equal => DateDiffResult::default(),
532 }
533}
534
535#[inline]
565pub fn date_time_diff<DT: DateTimeParts>(from: DT, to: DT) -> DateTimeDiffResult {
566 let to = WallClock::from_parts(&from.to_same_time_zone(to));
567 let from = WallClock::from_parts(&from);
568
569 match to.cmp(&from) {
570 Ordering::Greater => {
571 let date_diff = _date_diff(&from, &to, false);
572
573 let time_diff = _time_diff(
574 date_diff.earlier_nanoseconds_of_day,
575 date_diff.later_nanoseconds_of_day,
576 );
577
578 let date_diff = date_diff.result;
579
580 DateTimeDiffResult {
581 years: date_diff.years,
582 months: date_diff.months,
583 days: date_diff.days,
584 hours: time_diff.hours,
585 minutes: time_diff.minutes,
586 seconds: time_diff.seconds,
587 nanoseconds: time_diff.nanoseconds,
588 }
589 },
590 Ordering::Less => {
591 let date_diff = _date_diff(&to, &from, true);
592
593 let time_diff = _time_diff(
594 date_diff.earlier_nanoseconds_of_day,
595 date_diff.later_nanoseconds_of_day,
596 );
597
598 let date_diff = date_diff.result;
599
600 DateTimeDiffResult {
601 years: -date_diff.years,
602 months: -date_diff.months,
603 days: -date_diff.days,
604 hours: -time_diff.hours,
605 minutes: -time_diff.minutes,
606 seconds: -time_diff.seconds,
607 nanoseconds: -time_diff.nanoseconds,
608 }
609 },
610 Ordering::Equal => DateTimeDiffResult::default(),
611 }
612}