date_differencer/add_diff/
mod.rs1#[cfg(feature = "chrono")]
2mod chrono_support;
3#[cfg(feature = "jiff")]
4mod jiff_support;
5#[cfg(feature = "time")]
6mod time_support;
7
8#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
9use super::constants::*;
10#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
11use super::diff::DateTimeFields;
12use super::{DateTimeDiff, DateTimeParts};
13
14#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
15#[inline]
16fn month_add(year: &mut i32, month: &mut i32, n: i32) -> Option<()> {
17 let total = i64::from(*month) + i64::from(n);
19
20 if (0..12).contains(&total) {
21 *month = total as i32;
22 } else {
23 *year = year.checked_add(total.div_euclid(12) as i32)?;
24 *month = total.rem_euclid(12) as i32;
25 }
26
27 Some(())
28}
29
30#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
31#[inline]
32fn date_add(year: &mut i32, month: &mut i32, date: &mut i32, n: i32) -> Option<()> {
33 *date = date.checked_add(n)?;
34
35 if *date == 0 {
36 month_add(year, month, -1)?;
37
38 *date = year_helper::get_days_in_month(*year, (*month + 1) as u8).unwrap() as i32;
39 } else if *date > 28 {
40 loop {
41 let days_in_month =
42 year_helper::get_days_in_month(*year, (*month + 1) as u8).unwrap() as i32;
43
44 if *date <= days_in_month {
45 break;
46 }
47
48 month_add(year, month, 1)?;
49
50 *date -= days_in_month;
51 }
52 } else if *date < 0 {
53 loop {
54 month_add(year, month, -1)?;
55
56 let days_in_month =
57 year_helper::get_days_in_month(*year, (*month + 1) as u8).unwrap() as i32;
58
59 if -*date < days_in_month {
60 *date += days_in_month;
61 break;
62 }
63
64 *date += days_in_month;
65 }
66 }
67
68 Some(())
69}
70
71#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
72#[inline]
73fn hour_add(year: &mut i32, month: &mut i32, date: &mut i32, hour: &mut i32, n: i32) -> Option<()> {
74 let total = i64::from(*hour) + i64::from(n);
75
76 if (0..24).contains(&total) {
77 *hour = total as i32;
78 } else {
79 date_add(year, month, date, total.div_euclid(24) as i32)?;
80 *hour = total.rem_euclid(24) as i32;
81 }
82
83 Some(())
84}
85
86#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
87#[inline]
88fn minute_add(
89 year: &mut i32,
90 month: &mut i32,
91 date: &mut i32,
92 hour: &mut i32,
93 minute: &mut i32,
94 n: i32,
95) -> Option<()> {
96 let total = i64::from(*minute) + i64::from(n);
97
98 if (0..60).contains(&total) {
99 *minute = total as i32;
100 } else {
101 hour_add(year, month, date, hour, total.div_euclid(60) as i32)?;
102 *minute = total.rem_euclid(60) as i32;
103 }
104
105 Some(())
106}
107
108#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
109#[inline]
110fn second_add(
111 year: &mut i32,
112 month: &mut i32,
113 date: &mut i32,
114 hour: &mut i32,
115 minute: &mut i32,
116 second: &mut i32,
117 n: i32,
118) -> Option<()> {
119 let total = i64::from(*second) + i64::from(n);
120
121 if (0..60).contains(&total) {
122 *second = total as i32;
123 } else {
124 minute_add(year, month, date, hour, minute, total.div_euclid(60) as i32)?;
125 *second = total.rem_euclid(60) as i32;
126 }
127
128 Some(())
129}
130
131#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
132#[allow(clippy::too_many_arguments)]
133#[inline]
134fn nanosecond_add(
135 year: &mut i32,
136 month: &mut i32,
137 date: &mut i32,
138 hour: &mut i32,
139 minute: &mut i32,
140 second: &mut i32,
141 nanosecond: &mut i32,
142 n: i32,
143) -> Option<()> {
144 const SECOND_NANOSECONDS_I64: i64 = SECOND_NANOSECONDS as i64;
145
146 let total = i64::from(*nanosecond) + i64::from(n);
147
148 if (0..SECOND_NANOSECONDS_I64).contains(&total) {
149 *nanosecond = total as i32;
150 } else {
151 second_add(
152 year,
153 month,
154 date,
155 hour,
156 minute,
157 second,
158 total.div_euclid(SECOND_NANOSECONDS_I64) as i32,
159 )?;
160 *nanosecond = total.rem_euclid(SECOND_NANOSECONDS_I64) as i32;
161 }
162
163 Some(())
164}
165
166#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
167fn add_date_time_parts(
168 from: &impl DateTimeParts,
169 date_time_diff: &impl DateTimeDiff,
170) -> Option<DateTimeFields> {
171 let from = DateTimeFields::from_parts(from);
172
173 let mut year = from.year.checked_add(date_time_diff.years())?;
174 let mut month = from.month as i32 - 1;
175
176 month_add(&mut year, &mut month, date_time_diff.months())?;
177
178 let mut date = from.day as i32;
179
180 let days_in_month = year_helper::get_days_in_month(year, (month + 1) as u8).unwrap() as i32;
181
182 if date > days_in_month {
183 date = days_in_month;
184 }
185
186 date_add(&mut year, &mut month, &mut date, date_time_diff.days())?;
187
188 let mut hour = from.hour as i32;
189
190 hour_add(&mut year, &mut month, &mut date, &mut hour, date_time_diff.hours())?;
191
192 let mut minute = from.minute as i32;
193
194 minute_add(&mut year, &mut month, &mut date, &mut hour, &mut minute, date_time_diff.minutes())?;
195
196 let mut second = from.second as i32;
197
198 second_add(
199 &mut year,
200 &mut month,
201 &mut date,
202 &mut hour,
203 &mut minute,
204 &mut second,
205 date_time_diff.seconds(),
206 )?;
207
208 let mut nanosecond = from.nanosecond as i32;
209
210 nanosecond_add(
211 &mut year,
212 &mut month,
213 &mut date,
214 &mut hour,
215 &mut minute,
216 &mut second,
217 &mut nanosecond,
218 date_time_diff.nanoseconds(),
219 )?;
220
221 Some(DateTimeFields {
222 year,
223 month: (month + 1) as u8,
224 day: date as u8,
225 hour: hour as u8,
226 minute: minute as u8,
227 second: second as u8,
228 nanosecond: nanosecond as u32,
229 })
230}
231
232pub trait AddDateTimeDiff: DateTimeParts {
241 type Output;
242
243 fn add_date_time_diff(self, date_time_diff: &impl DateTimeDiff) -> Self::Output;
244}
245
246#[inline]
276pub fn add_date_time_diff<DT: AddDateTimeDiff>(
277 from: DT,
278 date_time_diff: &impl DateTimeDiff,
279) -> DT::Output {
280 from.add_date_time_diff(date_time_diff)
281}