use crate::any_calendar::{AnyCalendar, IntoAnyCalendar};
use crate::calendar_arithmetic::CalendarArithmetic;
use crate::error::DateError;
use crate::types::{CyclicYear, EraYear, IsoWeekOfYear};
use crate::week::{RelativeUnit, WeekCalculator, WeekOf};
use crate::{types, Calendar, DateDuration, DateDurationUnit, Iso};
#[cfg(feature = "alloc")]
use alloc::rc::Rc;
#[cfg(feature = "alloc")]
use alloc::sync::Arc;
use calendrical_calculations::rata_die::RataDie;
use core::fmt;
use core::ops::Deref;
pub trait AsCalendar {
type Calendar: Calendar;
fn as_calendar(&self) -> &Self::Calendar;
}
impl<C: Calendar> AsCalendar for C {
type Calendar = C;
#[inline]
fn as_calendar(&self) -> &Self {
self
}
}
#[cfg(feature = "alloc")]
impl<C: AsCalendar> AsCalendar for Rc<C> {
type Calendar = C::Calendar;
#[inline]
fn as_calendar(&self) -> &Self::Calendar {
self.as_ref().as_calendar()
}
}
#[cfg(feature = "alloc")]
impl<C: AsCalendar> AsCalendar for Arc<C> {
type Calendar = C::Calendar;
#[inline]
fn as_calendar(&self) -> &Self::Calendar {
self.as_ref().as_calendar()
}
}
#[allow(clippy::exhaustive_structs)] #[derive(PartialEq, Eq, Debug)]
pub struct Ref<'a, C>(pub &'a C);
impl<C> Copy for Ref<'_, C> {}
impl<C> Clone for Ref<'_, C> {
fn clone(&self) -> Self {
*self
}
}
impl<C: AsCalendar> AsCalendar for Ref<'_, C> {
type Calendar = C::Calendar;
#[inline]
fn as_calendar(&self) -> &Self::Calendar {
self.0.as_calendar()
}
}
impl<C> Deref for Ref<'_, C> {
type Target = C;
fn deref(&self) -> &C {
self.0
}
}
pub struct Date<A: AsCalendar> {
pub(crate) inner: <A::Calendar as Calendar>::DateInner,
pub(crate) calendar: A,
}
impl<A: AsCalendar> Date<A> {
#[inline]
pub fn try_new_from_codes(
era: Option<&str>,
year: i32,
month_code: types::MonthCode,
day: u8,
calendar: A,
) -> Result<Self, DateError> {
let inner = calendar
.as_calendar()
.from_codes(era, year, month_code, day)?;
Ok(Date { inner, calendar })
}
#[inline]
pub fn from_rata_die(rd: RataDie, calendar: A) -> Self {
Date {
inner: calendar.as_calendar().from_rata_die(rd),
calendar,
}
}
#[inline]
pub fn to_rata_die(&self) -> RataDie {
self.calendar.as_calendar().to_rata_die(self.inner())
}
#[inline]
pub fn new_from_iso(iso: Date<Iso>, calendar: A) -> Self {
let inner = calendar.as_calendar().from_iso(iso.inner);
Date { inner, calendar }
}
#[inline]
pub fn to_iso(&self) -> Date<Iso> {
Date::from_raw(self.calendar.as_calendar().to_iso(self.inner()), Iso)
}
#[inline]
pub fn to_calendar<A2: AsCalendar>(&self, calendar: A2) -> Date<A2> {
Date::new_from_iso(self.to_iso(), calendar)
}
#[inline]
pub fn months_in_year(&self) -> u8 {
self.calendar.as_calendar().months_in_year(self.inner())
}
#[inline]
pub fn days_in_year(&self) -> u16 {
self.calendar.as_calendar().days_in_year(self.inner())
}
#[inline]
pub fn days_in_month(&self) -> u8 {
self.calendar.as_calendar().days_in_month(self.inner())
}
#[inline]
pub fn day_of_week(&self) -> types::Weekday {
self.to_rata_die().into()
}
#[doc(hidden)] #[inline]
pub fn add(&mut self, duration: DateDuration<A::Calendar>) {
self.calendar
.as_calendar()
.offset_date(&mut self.inner, duration)
}
#[doc(hidden)] #[inline]
pub fn added(mut self, duration: DateDuration<A::Calendar>) -> Self {
self.add(duration);
self
}
#[doc(hidden)] #[inline]
pub fn until<B: AsCalendar<Calendar = A::Calendar>>(
&self,
other: &Date<B>,
largest_unit: DateDurationUnit,
smallest_unit: DateDurationUnit,
) -> DateDuration<A::Calendar> {
self.calendar.as_calendar().until(
self.inner(),
other.inner(),
other.calendar.as_calendar(),
largest_unit,
smallest_unit,
)
}
#[inline]
pub fn year(&self) -> types::YearInfo {
self.calendar.as_calendar().year_info(&self.inner).into()
}
#[inline]
pub fn extended_year(&self) -> i32 {
self.calendar.as_calendar().extended_year(&self.inner)
}
#[inline]
pub fn is_in_leap_year(&self) -> bool {
self.calendar.as_calendar().is_in_leap_year(&self.inner)
}
#[inline]
pub fn month(&self) -> types::MonthInfo {
self.calendar.as_calendar().month(&self.inner)
}
#[inline]
pub fn day_of_month(&self) -> types::DayOfMonth {
self.calendar.as_calendar().day_of_month(&self.inner)
}
#[inline]
pub fn day_of_year(&self) -> types::DayOfYear {
self.calendar.as_calendar().day_of_year(&self.inner)
}
#[inline]
pub fn from_raw(inner: <A::Calendar as Calendar>::DateInner, calendar: A) -> Self {
Self { inner, calendar }
}
#[inline]
pub fn inner(&self) -> &<A::Calendar as Calendar>::DateInner {
&self.inner
}
#[inline]
pub fn calendar(&self) -> &A::Calendar {
self.calendar.as_calendar()
}
#[inline]
pub fn calendar_wrapper(&self) -> &A {
&self.calendar
}
}
impl<A: AsCalendar<Calendar = C>, C: Calendar<Year = EraYear>> Date<A> {
pub fn era_year(&self) -> EraYear {
self.calendar.as_calendar().year_info(self.inner())
}
}
impl<A: AsCalendar<Calendar = C>, C: Calendar<Year = CyclicYear>> Date<A> {
pub fn cyclic_year(&self) -> CyclicYear {
self.calendar.as_calendar().year_info(self.inner())
}
}
impl Date<Iso> {
pub fn week_of_year(&self) -> IsoWeekOfYear {
let week_of = WeekCalculator::ISO
.week_of(
Iso::days_in_provided_year(self.inner.0.year.saturating_sub(1)),
self.days_in_year(),
self.day_of_year().0,
self.day_of_week(),
)
.unwrap_or_else(|_| {
debug_assert!(false);
WeekOf {
week: 1,
unit: crate::week::RelativeUnit::Current,
}
});
IsoWeekOfYear {
week_number: week_of.week,
iso_year: match week_of.unit {
RelativeUnit::Current => self.inner.0.year,
RelativeUnit::Next => self.inner.0.year.saturating_add(1),
RelativeUnit::Previous => self.inner.0.year.saturating_sub(1),
},
}
}
}
impl<C: IntoAnyCalendar> Date<C> {
pub fn to_any(self) -> Date<AnyCalendar> {
Date::from_raw(
self.calendar.date_to_any(&self.inner),
self.calendar.to_any(),
)
}
}
impl<A: AsCalendar> Date<A> {
#[cfg(feature = "alloc")]
pub fn into_ref_counted(self) -> Date<Rc<A>> {
Date::from_raw(self.inner, Rc::new(self.calendar))
}
#[cfg(feature = "alloc")]
pub fn into_atomic_ref_counted(self) -> Date<Arc<A>> {
Date::from_raw(self.inner, Arc::new(self.calendar))
}
pub fn as_borrowed(&self) -> Date<Ref<A>> {
Date::from_raw(self.inner, Ref(&self.calendar))
}
}
impl<C, A, B> PartialEq<Date<B>> for Date<A>
where
C: Calendar,
A: AsCalendar<Calendar = C>,
B: AsCalendar<Calendar = C>,
{
fn eq(&self, other: &Date<B>) -> bool {
self.inner.eq(&other.inner)
}
}
impl<A: AsCalendar> Eq for Date<A> {}
impl<C, A, B> PartialOrd<Date<B>> for Date<A>
where
C: Calendar,
C::DateInner: PartialOrd,
A: AsCalendar<Calendar = C>,
B: AsCalendar<Calendar = C>,
{
fn partial_cmp(&self, other: &Date<B>) -> Option<core::cmp::Ordering> {
self.inner.partial_cmp(&other.inner)
}
}
impl<C, A> Ord for Date<A>
where
C: Calendar,
C::DateInner: Ord,
A: AsCalendar<Calendar = C>,
{
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.inner.cmp(&other.inner)
}
}
impl<A: AsCalendar> fmt::Debug for Date<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
let month = self.month().ordinal;
let day = self.day_of_month().0;
let calendar = self.calendar.as_calendar().debug_name();
match self.year() {
types::YearInfo::Era(EraYear { year, era, .. }) => {
write!(
f,
"Date({year}-{month}-{day}, {era} era, for calendar {calendar})"
)
}
types::YearInfo::Cyclic(CyclicYear { year, related_iso }) => {
write!(
f,
"Date({year}-{month}-{day}, ISO year {related_iso}, for calendar {calendar})"
)
}
}
}
}
impl<A: AsCalendar + Clone> Clone for Date<A> {
fn clone(&self) -> Self {
Self {
inner: self.inner,
calendar: self.calendar.clone(),
}
}
}
impl<A> Copy for Date<A> where A: AsCalendar + Copy {}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::Weekday;
#[test]
fn test_ord() {
let dates_in_order = [
Date::try_new_iso(-10, 1, 1).unwrap(),
Date::try_new_iso(-10, 1, 2).unwrap(),
Date::try_new_iso(-10, 2, 1).unwrap(),
Date::try_new_iso(-1, 1, 1).unwrap(),
Date::try_new_iso(-1, 1, 2).unwrap(),
Date::try_new_iso(-1, 2, 1).unwrap(),
Date::try_new_iso(0, 1, 1).unwrap(),
Date::try_new_iso(0, 1, 2).unwrap(),
Date::try_new_iso(0, 2, 1).unwrap(),
Date::try_new_iso(1, 1, 1).unwrap(),
Date::try_new_iso(1, 1, 2).unwrap(),
Date::try_new_iso(1, 2, 1).unwrap(),
Date::try_new_iso(10, 1, 1).unwrap(),
Date::try_new_iso(10, 1, 2).unwrap(),
Date::try_new_iso(10, 2, 1).unwrap(),
];
for (i, i_date) in dates_in_order.iter().enumerate() {
for (j, j_date) in dates_in_order.iter().enumerate() {
let result1 = i_date.cmp(j_date);
let result2 = j_date.cmp(i_date);
assert_eq!(result1.reverse(), result2);
assert_eq!(i.cmp(&j), i_date.cmp(j_date));
}
}
}
#[test]
fn test_day_of_week() {
assert_eq!(
Date::try_new_iso(2021, 6, 23).unwrap().day_of_week(),
Weekday::Wednesday,
);
assert_eq!(
Date::try_new_iso(1983, 2, 2).unwrap().day_of_week(),
Weekday::Wednesday,
);
assert_eq!(
Date::try_new_iso(2020, 1, 21).unwrap().day_of_week(),
Weekday::Tuesday,
);
}
}