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