1use chrono::{prelude::*, Duration, LocalResult};
2
3use super::{constants::*, DateTimeDiff};
4
5#[inline]
6fn month_add(year: &mut i32, month: &mut i32, n: i32) -> Option<()> {
7 *month = month.checked_add(n)?;
8
9 if *month >= 12 {
10 *year += *month / 12;
11 *month %= 12;
12 } else if *month < 0 {
13 *year += *month / 12 - 1;
14
15 *month = 12 - (-*month % 12);
16
17 if *month == 12 {
18 *month = 0;
19 }
20 }
21
22 Some(())
23}
24
25#[inline]
26fn date_add(year: &mut i32, month: &mut i32, date: &mut i32, n: i32) -> Option<()> {
27 *date = date.checked_add(n)?;
28
29 if *date == 0 {
30 month_add(year, month, -1)?;
31
32 *date = year_helper::get_days_in_month(*year, (*month + 1) as u8).unwrap() as i32;
33 } else if *date > 28 {
34 loop {
35 let days_in_month =
36 year_helper::get_days_in_month(*year, (*month + 1) as u8).unwrap() as i32;
37
38 if *date <= days_in_month {
39 break;
40 }
41
42 month_add(year, month, 1)?;
43
44 *date -= days_in_month;
45 }
46 } else if *date < 0 {
47 loop {
48 month_add(year, month, -1)?;
49
50 let days_in_month =
51 year_helper::get_days_in_month(*year, (*month + 1) as u8).unwrap() as i32;
52
53 if -*date < days_in_month {
54 *date += days_in_month;
55 break;
56 }
57
58 *date += days_in_month;
59 }
60 }
61
62 Some(())
63}
64
65#[inline]
66fn hour_add(year: &mut i32, month: &mut i32, date: &mut i32, hour: &mut i32, n: i32) -> Option<()> {
67 *hour = hour.checked_add(n)?;
68
69 if *hour >= 24 {
70 date_add(year, month, date, *hour / 24)?;
71 *hour %= 24;
72 } else if *hour < 0 {
73 date_add(year, month, date, *hour / 24 - 1)?;
74
75 *hour = 24 - (-*hour % 24);
76
77 if *hour == 24 {
78 *hour = 0;
79 }
80 }
81
82 Some(())
83}
84
85#[inline]
86fn minute_add(
87 year: &mut i32,
88 month: &mut i32,
89 date: &mut i32,
90 hour: &mut i32,
91 minute: &mut i32,
92 n: i32,
93) -> Option<()> {
94 *minute = minute.checked_add(n)?;
95
96 if *minute >= 60 {
97 hour_add(year, month, date, hour, *minute / 60)?;
98 *minute %= 60;
99 } else if *minute < 0 {
100 hour_add(year, month, date, hour, *minute / 60 - 1)?;
101
102 *minute = 60 - (-*minute % 60);
103
104 if *minute == 60 {
105 *minute = 0;
106 }
107 }
108
109 Some(())
110}
111
112#[inline]
113fn second_add(
114 year: &mut i32,
115 month: &mut i32,
116 date: &mut i32,
117 hour: &mut i32,
118 minute: &mut i32,
119 second: &mut i32,
120 n: i32,
121) -> Option<()> {
122 *second = second.checked_add(n)?;
123
124 if *second >= 60 {
125 minute_add(year, month, date, hour, minute, *second / 60)?;
126 *second %= 60;
127 } else if *second < 0 {
128 minute_add(year, month, date, hour, minute, *second / 60 - 1)?;
129
130 *second = 60 - (-*second % 60);
131
132 if *second == 60 {
133 *second = 0;
134 }
135 }
136
137 Some(())
138}
139
140#[allow(clippy::too_many_arguments)]
141#[inline]
142fn nanosecond_add(
143 year: &mut i32,
144 month: &mut i32,
145 date: &mut i32,
146 hour: &mut i32,
147 minute: &mut i32,
148 second: &mut i32,
149 nanosecond: &mut i32,
150 n: i32,
151) -> Option<()> {
152 const SECOND_NANOSECONDS_I32: i32 = SECOND_NANOSECONDS as i32;
153
154 *nanosecond = nanosecond.checked_add(n)?;
155
156 if *nanosecond >= SECOND_NANOSECONDS_I32 {
157 second_add(year, month, date, hour, minute, second, *nanosecond / SECOND_NANOSECONDS_I32)?;
158 *nanosecond %= SECOND_NANOSECONDS_I32;
159 } else if *nanosecond < 0 {
160 second_add(
161 year,
162 month,
163 date,
164 hour,
165 minute,
166 second,
167 *nanosecond / SECOND_NANOSECONDS_I32 - 1,
168 )?;
169
170 *nanosecond = SECOND_NANOSECONDS_I32 - (-*nanosecond % SECOND_NANOSECONDS_I32);
171
172 if *nanosecond == 60 {
173 *nanosecond = 0;
174 }
175 }
176
177 Some(())
178}
179
180pub fn add_date_time_diff<Tz: TimeZone>(
203 from: DateTime<Tz>,
204 date_time_diff: &dyn DateTimeDiff,
205) -> LocalResult<DateTime<Tz>> {
206 let mut year = match from.year().checked_add(date_time_diff.years()) {
207 Some(v) => v,
208 None => return LocalResult::None,
209 };
210
211 let mut month = from.month0() as i32;
212
213 if month_add(&mut year, &mut month, date_time_diff.months()).is_none() {
214 return LocalResult::None;
215 }
216
217 let mut date = from.day() as i32;
218
219 let days_in_month = year_helper::get_days_in_month(year, (month + 1) as u8).unwrap() as i32;
220
221 if date > days_in_month {
222 date = days_in_month;
223 }
224
225 if date_add(&mut year, &mut month, &mut date, date_time_diff.days()).is_none() {
226 return LocalResult::None;
227 }
228
229 let mut hour = from.hour() as i32;
230
231 if hour_add(&mut year, &mut month, &mut date, &mut hour, date_time_diff.hours()).is_none() {
232 return LocalResult::None;
233 }
234
235 let mut minute = from.minute() as i32;
236
237 if minute_add(
238 &mut year,
239 &mut month,
240 &mut date,
241 &mut hour,
242 &mut minute,
243 date_time_diff.minutes(),
244 )
245 .is_none()
246 {
247 return LocalResult::None;
248 }
249
250 let mut second = from.second() as i32;
251
252 if second_add(
253 &mut year,
254 &mut month,
255 &mut date,
256 &mut hour,
257 &mut minute,
258 &mut second,
259 date_time_diff.seconds(),
260 )
261 .is_none()
262 {
263 return LocalResult::None;
264 }
265
266 let mut nanosecond = from.nanosecond() as i32;
267
268 if nanosecond_add(
269 &mut year,
270 &mut month,
271 &mut date,
272 &mut hour,
273 &mut minute,
274 &mut second,
275 &mut nanosecond,
276 date_time_diff.nanoseconds(),
277 )
278 .is_none()
279 {
280 return LocalResult::None;
281 }
282
283 match from.timezone().with_ymd_and_hms(
284 year,
285 month as u32 + 1,
286 date as u32,
287 hour as u32,
288 minute as u32,
289 second as u32,
290 ) {
291 LocalResult::Single(v) => {
292 match v.checked_add_signed(Duration::nanoseconds(nanosecond as i64)) {
293 Some(v) => LocalResult::Single(v),
294 None => LocalResult::None,
295 }
296 },
297 LocalResult::Ambiguous(a, b) => {
298 let delta = Duration::nanoseconds(nanosecond as i64);
299 LocalResult::Ambiguous(
300 match a.checked_add_signed(delta) {
301 Some(v) => v,
302 None => return LocalResult::None,
303 },
304 match b.checked_add_signed(delta) {
305 Some(v) => v,
306 None => return LocalResult::None,
307 },
308 )
309 },
310 LocalResult::None => LocalResult::None,
311 }
312}