#[cfg(feature = "chrono")]
mod chrono_support;
#[cfg(feature = "jiff")]
mod jiff_support;
#[cfg(feature = "time")]
mod time_support;
use core::cmp::Ordering;
use super::constants::*;
#[derive(Debug)]
struct TimeDiffResult {
pub(crate) hours: i32,
pub(crate) minutes: i32,
pub(crate) seconds: i32,
pub(crate) nanoseconds: i32,
}
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
pub struct DateDiffResult {
pub years: i32,
pub months: i32,
pub days: i32,
}
impl DateDiffResult {
#[doc(hidden)]
#[inline]
pub fn into_neg(mut self) -> Self {
self.years *= -1;
self.months *= -1;
self.days *= -1;
self
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct DateTimeFields {
pub(crate) year: i32,
pub(crate) month: u8,
pub(crate) day: u8,
pub(crate) hour: u8,
pub(crate) minute: u8,
pub(crate) second: u8,
pub(crate) nanosecond: u32,
}
impl DateTimeFields {
#[inline]
pub(crate) fn from_parts(date_time: &impl DateTimeParts) -> Self {
let (year, month, day, hour, minute, second, nanosecond) = date_time.all_parts();
Self {
year,
month,
day,
hour,
minute,
second,
nanosecond,
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
struct WallClock {
year: i32,
month: u8,
day: u8,
nanoseconds_of_day: u64,
}
impl WallClock {
#[inline]
fn from_parts(date_time: &impl DateTimeParts) -> Self {
let fields = DateTimeFields::from_parts(date_time);
Self {
year: fields.year,
month: fields.month,
day: fields.day,
nanoseconds_of_day: (fields.hour as u64 * HOUR_NANOSECONDS)
+ (fields.minute as u64 * MINUTE_NANOSECONDS)
+ (fields.second as u64 * SECOND_NANOSECONDS)
+ fields.nanosecond as u64,
}
}
}
#[derive(Debug)]
struct _DateDiffResult {
pub(crate) earlier_nanoseconds_of_day: u64,
pub(crate) later_nanoseconds_of_day: u64,
pub(crate) result: DateDiffResult,
}
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
pub struct DateTimeDiffResult {
pub years: i32,
pub months: i32,
pub days: i32,
pub hours: i32,
pub minutes: i32,
pub seconds: i32,
pub nanoseconds: i32,
}
impl DateTimeDiffResult {
#[doc(hidden)]
#[inline]
pub fn into_neg(mut self) -> Self {
self.years *= -1;
self.months *= -1;
self.days *= -1;
self.hours *= -1;
self.minutes *= -1;
self.seconds *= -1;
self.nanoseconds *= -1;
self
}
}
impl From<DateDiffResult> for DateTimeDiffResult {
#[inline]
fn from(value: DateDiffResult) -> Self {
DateTimeDiffResult {
years: value.years,
months: value.months,
days: value.days,
..DateTimeDiffResult::default()
}
}
}
impl From<DateTimeDiffResult> for DateDiffResult {
#[inline]
fn from(value: DateTimeDiffResult) -> Self {
DateDiffResult {
years: value.years, months: value.months, days: value.days
}
}
}
pub trait DateTimeDiff {
#[inline]
fn years(&self) -> i32 {
0
}
#[inline]
fn months(&self) -> i32 {
0
}
#[inline]
fn days(&self) -> i32 {
0
}
#[inline]
fn hours(&self) -> i32 {
0
}
#[inline]
fn minutes(&self) -> i32 {
0
}
#[inline]
fn seconds(&self) -> i32 {
0
}
#[inline]
fn nanoseconds(&self) -> i32 {
0
}
}
pub trait DateTimeParts: Ord {
fn year(&self) -> i32;
fn month(&self) -> u8;
fn day(&self) -> u8;
fn hour(&self) -> u8;
fn minute(&self) -> u8;
fn second(&self) -> u8;
fn nanosecond(&self) -> u32;
#[doc(hidden)]
#[inline]
fn all_parts(&self) -> (i32, u8, u8, u8, u8, u8, u32) {
(
self.year(),
self.month(),
self.day(),
self.hour(),
self.minute(),
self.second(),
self.nanosecond(),
)
}
#[doc(hidden)]
#[inline]
fn to_same_time_zone(&self, other: Self) -> Self
where
Self: Sized, {
other
}
}
impl DateTimeDiff for DateDiffResult {
#[inline]
fn years(&self) -> i32 {
self.years
}
#[inline]
fn months(&self) -> i32 {
self.months
}
#[inline]
fn days(&self) -> i32 {
self.days
}
}
impl DateTimeDiff for DateTimeDiffResult {
#[inline]
fn years(&self) -> i32 {
self.years
}
#[inline]
fn months(&self) -> i32 {
self.months
}
#[inline]
fn days(&self) -> i32 {
self.days
}
#[inline]
fn hours(&self) -> i32 {
self.hours
}
#[inline]
fn minutes(&self) -> i32 {
self.minutes
}
#[inline]
fn seconds(&self) -> i32 {
self.seconds
}
#[inline]
fn nanoseconds(&self) -> i32 {
self.nanoseconds
}
}
#[inline]
const fn _nanoseconds_to_units(mut nanoseconds: u64) -> TimeDiffResult {
let h = nanoseconds / HOUR_NANOSECONDS;
nanoseconds -= h * HOUR_NANOSECONDS;
let m = nanoseconds / MINUTE_NANOSECONDS;
nanoseconds -= m * MINUTE_NANOSECONDS;
let s = nanoseconds / SECOND_NANOSECONDS;
nanoseconds -= s * SECOND_NANOSECONDS;
TimeDiffResult {
hours: h as i32,
minutes: m as i32,
seconds: s as i32,
nanoseconds: nanoseconds as i32,
}
}
#[inline]
const fn _time_diff(
earlier_nanoseconds_of_day: u64,
later_nanoseconds_of_day: u64,
) -> TimeDiffResult {
let nanoseconds = if later_nanoseconds_of_day >= earlier_nanoseconds_of_day {
later_nanoseconds_of_day - earlier_nanoseconds_of_day
} else {
DAY_NANOSECONDS + later_nanoseconds_of_day - earlier_nanoseconds_of_day
};
_nanoseconds_to_units(nanoseconds)
}
fn _date_diff(earlier: &WallClock, later: &WallClock, start_from_later: bool) -> _DateDiffResult {
let mut earlier_year = earlier.year;
let mut earlier_month = earlier.month;
let mut earlier_date = earlier.day;
let mut later_year = later.year;
let mut later_month = later.month;
let mut later_date = later.day;
let later_nanoseconds_of_day = later.nanoseconds_of_day;
let earlier_nanoseconds_of_day = earlier.nanoseconds_of_day;
let years: i32;
let months: i32;
let days: i32;
if later_nanoseconds_of_day < earlier_nanoseconds_of_day {
if start_from_later {
if earlier_date < year_helper::get_days_in_month(earlier_year, earlier_month).unwrap() {
earlier_date += 1;
} else if earlier_month < 12 {
earlier_month += 1;
earlier_date = 1;
} else {
earlier_year += 1;
earlier_month = 1;
earlier_date = 1;
}
} else {
if later_date > 1 {
later_date -= 1;
} else if later_month > 1 {
later_month -= 1;
later_date = year_helper::get_days_in_month(later_year, later_month).unwrap();
} else {
later_year -= 1;
later_month = 12;
later_date = 31;
}
}
}
let year_diff = later_year - earlier_year;
let month_diff = later_month as i32 - earlier_month as i32;
match month_diff.cmp(&0) {
Ordering::Greater => {
years = year_diff;
if later_date >= earlier_date {
months = month_diff;
} else {
months = month_diff - 1;
}
},
Ordering::Less => {
years = year_diff - 1;
if later_date >= earlier_date {
months = month_diff + 12;
} else {
months = month_diff + 11;
}
},
Ordering::Equal => {
if later_date >= earlier_date {
years = year_diff;
months = 0;
} else {
years = year_diff - 1;
months = 11;
}
},
}
if later_date >= earlier_date {
if start_from_later {
days = later_date
.min(year_helper::get_days_in_month(earlier_year, earlier_month).unwrap())
as i32
- earlier_date as i32;
} else {
days = later_date as i32 - earlier_date as i32;
}
} else {
if start_from_later {
if earlier_month < 12 {
later_date = later_date
.min(year_helper::get_days_in_month(earlier_year, earlier_month + 1).unwrap())
} else {
}
days = (later_date
+ (year_helper::get_days_in_month(earlier_year, earlier_month).unwrap()
- earlier_date)) as i32;
} else {
let days_in_month = if later_month > 1 {
year_helper::get_days_in_month(later_year, later_month - 1).unwrap()
} else {
31 };
if days_in_month > earlier_date {
days = (later_date + (days_in_month - earlier_date)) as i32;
} else {
days = later_date as i32;
}
}
}
_DateDiffResult {
earlier_nanoseconds_of_day,
later_nanoseconds_of_day,
result: DateDiffResult {
years,
months,
days,
},
}
}
#[inline]
pub fn date_diff<DT: DateTimeParts>(from: DT, to: DT) -> DateDiffResult {
let to = WallClock::from_parts(&from.to_same_time_zone(to));
let from = WallClock::from_parts(&from);
match to.cmp(&from) {
Ordering::Greater => _date_diff(&from, &to, false).result,
Ordering::Less => _date_diff(&to, &from, true).result.into_neg(),
Ordering::Equal => DateDiffResult::default(),
}
}
#[inline]
pub fn date_time_diff<DT: DateTimeParts>(from: DT, to: DT) -> DateTimeDiffResult {
let to = WallClock::from_parts(&from.to_same_time_zone(to));
let from = WallClock::from_parts(&from);
match to.cmp(&from) {
Ordering::Greater => {
let date_diff = _date_diff(&from, &to, false);
let time_diff = _time_diff(
date_diff.earlier_nanoseconds_of_day,
date_diff.later_nanoseconds_of_day,
);
let date_diff = date_diff.result;
DateTimeDiffResult {
years: date_diff.years,
months: date_diff.months,
days: date_diff.days,
hours: time_diff.hours,
minutes: time_diff.minutes,
seconds: time_diff.seconds,
nanoseconds: time_diff.nanoseconds,
}
},
Ordering::Less => {
let date_diff = _date_diff(&to, &from, true);
let time_diff = _time_diff(
date_diff.earlier_nanoseconds_of_day,
date_diff.later_nanoseconds_of_day,
);
let date_diff = date_diff.result;
DateTimeDiffResult {
years: -date_diff.years,
months: -date_diff.months,
days: -date_diff.days,
hours: -time_diff.hours,
minutes: -time_diff.minutes,
seconds: -time_diff.seconds,
nanoseconds: -time_diff.nanoseconds,
}
},
Ordering::Equal => DateTimeDiffResult::default(),
}
}