#[cfg(feature = "chrono")]
mod chrono_support;
#[cfg(feature = "jiff")]
mod jiff_support;
#[cfg(feature = "time")]
mod time_support;
#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
use super::constants::*;
#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
use super::diff::DateTimeFields;
use super::{DateTimeDiff, DateTimeParts};
#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
#[inline]
fn month_add(year: &mut i32, month: &mut i32, n: i32) -> Option<()> {
let total = i64::from(*month) + i64::from(n);
if (0..12).contains(&total) {
*month = total as i32;
} else {
*year = year.checked_add(total.div_euclid(12) as i32)?;
*month = total.rem_euclid(12) as i32;
}
Some(())
}
#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
#[inline]
fn date_add(year: &mut i32, month: &mut i32, date: &mut i32, n: i32) -> Option<()> {
*date = date.checked_add(n)?;
if *date == 0 {
month_add(year, month, -1)?;
*date = year_helper::get_days_in_month(*year, (*month + 1) as u8).unwrap() as i32;
} else if *date > 28 {
loop {
let days_in_month =
year_helper::get_days_in_month(*year, (*month + 1) as u8).unwrap() as i32;
if *date <= days_in_month {
break;
}
month_add(year, month, 1)?;
*date -= days_in_month;
}
} else if *date < 0 {
loop {
month_add(year, month, -1)?;
let days_in_month =
year_helper::get_days_in_month(*year, (*month + 1) as u8).unwrap() as i32;
if -*date < days_in_month {
*date += days_in_month;
break;
}
*date += days_in_month;
}
}
Some(())
}
#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
#[inline]
fn hour_add(year: &mut i32, month: &mut i32, date: &mut i32, hour: &mut i32, n: i32) -> Option<()> {
let total = i64::from(*hour) + i64::from(n);
if (0..24).contains(&total) {
*hour = total as i32;
} else {
date_add(year, month, date, total.div_euclid(24) as i32)?;
*hour = total.rem_euclid(24) as i32;
}
Some(())
}
#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
#[inline]
fn minute_add(
year: &mut i32,
month: &mut i32,
date: &mut i32,
hour: &mut i32,
minute: &mut i32,
n: i32,
) -> Option<()> {
let total = i64::from(*minute) + i64::from(n);
if (0..60).contains(&total) {
*minute = total as i32;
} else {
hour_add(year, month, date, hour, total.div_euclid(60) as i32)?;
*minute = total.rem_euclid(60) as i32;
}
Some(())
}
#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
#[inline]
fn second_add(
year: &mut i32,
month: &mut i32,
date: &mut i32,
hour: &mut i32,
minute: &mut i32,
second: &mut i32,
n: i32,
) -> Option<()> {
let total = i64::from(*second) + i64::from(n);
if (0..60).contains(&total) {
*second = total as i32;
} else {
minute_add(year, month, date, hour, minute, total.div_euclid(60) as i32)?;
*second = total.rem_euclid(60) as i32;
}
Some(())
}
#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
#[allow(clippy::too_many_arguments)]
#[inline]
fn nanosecond_add(
year: &mut i32,
month: &mut i32,
date: &mut i32,
hour: &mut i32,
minute: &mut i32,
second: &mut i32,
nanosecond: &mut i32,
n: i32,
) -> Option<()> {
const SECOND_NANOSECONDS_I64: i64 = SECOND_NANOSECONDS as i64;
let total = i64::from(*nanosecond) + i64::from(n);
if (0..SECOND_NANOSECONDS_I64).contains(&total) {
*nanosecond = total as i32;
} else {
second_add(
year,
month,
date,
hour,
minute,
second,
total.div_euclid(SECOND_NANOSECONDS_I64) as i32,
)?;
*nanosecond = total.rem_euclid(SECOND_NANOSECONDS_I64) as i32;
}
Some(())
}
#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
fn add_date_time_parts(
from: &impl DateTimeParts,
date_time_diff: &impl DateTimeDiff,
) -> Option<DateTimeFields> {
let from = DateTimeFields::from_parts(from);
let mut year = from.year.checked_add(date_time_diff.years())?;
let mut month = from.month as i32 - 1;
month_add(&mut year, &mut month, date_time_diff.months())?;
let mut date = from.day as i32;
let days_in_month = year_helper::get_days_in_month(year, (month + 1) as u8).unwrap() as i32;
if date > days_in_month {
date = days_in_month;
}
date_add(&mut year, &mut month, &mut date, date_time_diff.days())?;
let mut hour = from.hour as i32;
hour_add(&mut year, &mut month, &mut date, &mut hour, date_time_diff.hours())?;
let mut minute = from.minute as i32;
minute_add(&mut year, &mut month, &mut date, &mut hour, &mut minute, date_time_diff.minutes())?;
let mut second = from.second as i32;
second_add(
&mut year,
&mut month,
&mut date,
&mut hour,
&mut minute,
&mut second,
date_time_diff.seconds(),
)?;
let mut nanosecond = from.nanosecond as i32;
nanosecond_add(
&mut year,
&mut month,
&mut date,
&mut hour,
&mut minute,
&mut second,
&mut nanosecond,
date_time_diff.nanoseconds(),
)?;
Some(DateTimeFields {
year,
month: (month + 1) as u8,
day: date as u8,
hour: hour as u8,
minute: minute as u8,
second: second as u8,
nanosecond: nanosecond as u32,
})
}
pub trait AddDateTimeDiff: DateTimeParts {
type Output;
fn add_date_time_diff(self, date_time_diff: &impl DateTimeDiff) -> Self::Output;
}
#[inline]
pub fn add_date_time_diff<DT: AddDateTimeDiff>(
from: DT,
date_time_diff: &impl DateTimeDiff,
) -> DT::Output {
from.add_date_time_diff(date_time_diff)
}