use crate::{
iso::iso_date_to_epoch_days, options::Overflow, Duration, PlainDate, Sign, TemporalError,
TemporalResult,
};
use super::duration_sign;
#[non_exhaustive]
#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)]
pub struct DateDuration {
pub years: i64,
pub months: i64,
pub weeks: i64,
pub days: i64,
}
impl DateDuration {
#[inline]
#[must_use]
pub(crate) const fn new_unchecked(years: i64, months: i64, weeks: i64, days: i64) -> Self {
Self {
years,
months,
weeks,
days,
}
}
}
impl From<Duration> for DateDuration {
#[inline]
fn from(duration: Duration) -> Self {
Self::new_unchecked(
duration.years(),
duration.months(),
duration.weeks(),
duration.days(),
)
}
}
impl From<&Duration> for DateDuration {
#[inline]
fn from(duration: &Duration) -> Self {
Self::new_unchecked(
duration.years(),
duration.months(),
duration.weeks(),
duration.days(),
)
}
}
impl DateDuration {
#[inline]
pub fn new(years: i64, months: i64, weeks: i64, days: i64) -> TemporalResult<Self> {
if !super::is_valid_duration(years, months, weeks, days, 0, 0, 0, 0, 0, 0) {
return Err(TemporalError::range().with_message("Invalid DateDuration."));
}
Ok(Self::new_unchecked(years, months, weeks, days))
}
#[inline]
#[must_use]
pub fn negated(&self) -> Self {
Self {
years: self.years.saturating_neg(),
months: self.months.saturating_neg(),
weeks: self.weeks.saturating_neg(),
days: self.days.saturating_neg(),
}
}
#[inline]
#[must_use]
pub fn abs(&self) -> Self {
Self {
years: self.years.abs(),
months: self.months.abs(),
weeks: self.weeks.abs(),
days: self.days.abs(),
}
}
#[inline]
#[must_use]
pub fn sign(&self) -> Sign {
duration_sign(&[self.years, self.months, self.weeks, self.days])
}
pub(crate) fn days(&self, relative_to: &PlainDate) -> TemporalResult<i64> {
let ymw_duration = self.adjust(0, None, None)?;
if ymw_duration.sign() == Sign::Zero {
return Ok(self.days);
}
let later = relative_to.calendar().date_add(
&relative_to.iso,
&ymw_duration,
Overflow::Constrain,
)?;
let epoch_days_1 = iso_date_to_epoch_days(
relative_to.iso_year(),
i32::from(relative_to.iso_month()), i32::from(relative_to.iso_day()),
);
let epoch_days_2 = iso_date_to_epoch_days(
later.iso_year(),
i32::from(later.iso_month()), i32::from(later.iso_day()),
);
let ymd_in_days = epoch_days_2 - epoch_days_1;
Ok(self.days + ymd_in_days)
}
pub(crate) fn adjust(
&self,
days: i64,
weeks: Option<i64>,
months: Option<i64>,
) -> TemporalResult<Self> {
let weeks = weeks.unwrap_or(self.weeks);
let months = months.unwrap_or(self.months);
Ok(Self {
years: self.years,
months,
weeks,
days,
})
}
}