1use crate::any_calendar::{AnyCalendar, IntoAnyCalendar};
6use crate::week::{WeekCalculator, WeekOf};
7use crate::{types, Calendar, CalendarError, DateDuration, DateDurationUnit, Iso};
8use alloc::rc::Rc;
9use alloc::sync::Arc;
10use core::fmt;
11use core::ops::Deref;
12
13pub trait AsCalendar {
18    type Calendar: Calendar;
20    fn as_calendar(&self) -> &Self::Calendar;
22}
23
24impl<C: Calendar> AsCalendar for C {
25    type Calendar = C;
26    #[inline]
27    fn as_calendar(&self) -> &Self {
28        self
29    }
30}
31
32impl<C: Calendar> AsCalendar for Rc<C> {
33    type Calendar = C;
34    #[inline]
35    fn as_calendar(&self) -> &C {
36        self
37    }
38}
39
40impl<C: Calendar> AsCalendar for Arc<C> {
41    type Calendar = C;
42    #[inline]
43    fn as_calendar(&self) -> &C {
44        self
45    }
46}
47
48#[allow(clippy::exhaustive_structs)] #[derive(PartialEq, Eq, Debug)]
59pub struct Ref<'a, C>(pub &'a C);
60
61impl<C> Copy for Ref<'_, C> {}
62
63impl<C> Clone for Ref<'_, C> {
64    fn clone(&self) -> Self {
65        *self
66    }
67}
68
69impl<C: Calendar> AsCalendar for Ref<'_, C> {
70    type Calendar = C;
71    #[inline]
72    fn as_calendar(&self) -> &C {
73        self.0
74    }
75}
76
77impl<'a, C> Deref for Ref<'a, C> {
78    type Target = C;
79    fn deref(&self) -> &C {
80        self.0
81    }
82}
83
84pub struct Date<A: AsCalendar> {
105    pub(crate) inner: <A::Calendar as Calendar>::DateInner,
106    pub(crate) calendar: A,
107}
108
109impl<A: AsCalendar> Date<A> {
110    #[inline]
112    pub fn try_new_from_codes(
113        era: types::Era,
114        year: i32,
115        month_code: types::MonthCode,
116        day: u8,
117        calendar: A,
118    ) -> Result<Self, CalendarError> {
119        let inner = calendar
120            .as_calendar()
121            .date_from_codes(era, year, month_code, day)?;
122        Ok(Date { inner, calendar })
123    }
124
125    #[inline]
127    pub fn new_from_iso(iso: Date<Iso>, calendar: A) -> Self {
128        let inner = calendar.as_calendar().date_from_iso(iso);
129        Date { inner, calendar }
130    }
131
132    #[inline]
134    pub fn to_iso(&self) -> Date<Iso> {
135        self.calendar.as_calendar().date_to_iso(self.inner())
136    }
137
138    #[inline]
140    pub fn to_calendar<A2: AsCalendar>(&self, calendar: A2) -> Date<A2> {
141        Date::new_from_iso(self.to_iso(), calendar)
142    }
143
144    #[inline]
146    pub fn months_in_year(&self) -> u8 {
147        self.calendar.as_calendar().months_in_year(self.inner())
148    }
149
150    #[inline]
152    pub fn days_in_year(&self) -> u16 {
153        self.calendar.as_calendar().days_in_year(self.inner())
154    }
155
156    #[inline]
158    pub fn days_in_month(&self) -> u8 {
159        self.calendar.as_calendar().days_in_month(self.inner())
160    }
161
162    #[inline]
166    pub fn day_of_week(&self) -> types::IsoWeekday {
167        self.calendar.as_calendar().day_of_week(self.inner())
168    }
169
170    #[doc(hidden)]
174    #[inline]
175    pub fn add(&mut self, duration: DateDuration<A::Calendar>) {
176        self.calendar
177            .as_calendar()
178            .offset_date(&mut self.inner, duration)
179    }
180
181    #[doc(hidden)]
185    #[inline]
186    pub fn added(mut self, duration: DateDuration<A::Calendar>) -> Self {
187        self.add(duration);
188        self
189    }
190
191    #[doc(hidden)]
195    #[inline]
196    pub fn until<B: AsCalendar<Calendar = A::Calendar>>(
197        &self,
198        other: &Date<B>,
199        largest_unit: DateDurationUnit,
200        smallest_unit: DateDurationUnit,
201    ) -> DateDuration<A::Calendar> {
202        self.calendar.as_calendar().until(
203            self.inner(),
204            other.inner(),
205            other.calendar.as_calendar(),
206            largest_unit,
207            smallest_unit,
208        )
209    }
210
211    #[inline]
213    pub fn year(&self) -> types::FormattableYear {
214        self.calendar.as_calendar().year(&self.inner)
215    }
216
217    #[inline]
219    pub fn is_in_leap_year(&self) -> bool {
220        self.calendar.as_calendar().is_in_leap_year(&self.inner)
221    }
222
223    #[inline]
225    pub fn month(&self) -> types::FormattableMonth {
226        self.calendar.as_calendar().month(&self.inner)
227    }
228
229    #[inline]
231    pub fn day_of_month(&self) -> types::DayOfMonth {
232        self.calendar.as_calendar().day_of_month(&self.inner)
233    }
234
235    #[inline]
237    pub fn day_of_year_info(&self) -> types::DayOfYearInfo {
238        self.calendar.as_calendar().day_of_year_info(&self.inner)
239    }
240
241    pub fn week_of_month(&self, first_weekday: types::IsoWeekday) -> types::WeekOfMonth {
258        let config = WeekCalculator {
259            first_weekday,
260            min_week_days: 0, weekend: None,
262        };
263        config.week_of_month(self.day_of_month(), self.day_of_week())
264    }
265
266    pub fn week_of_year(&self, config: &WeekCalculator) -> Result<WeekOf, CalendarError> {
290        config.week_of_year(self.day_of_year_info(), self.day_of_week())
291    }
292
293    #[inline]
302    pub fn from_raw(inner: <A::Calendar as Calendar>::DateInner, calendar: A) -> Self {
303        Self { inner, calendar }
304    }
305
306    #[inline]
308    pub fn inner(&self) -> &<A::Calendar as Calendar>::DateInner {
309        &self.inner
310    }
311
312    #[inline]
314    pub fn calendar(&self) -> &A::Calendar {
315        self.calendar.as_calendar()
316    }
317
318    #[inline]
322    pub fn calendar_wrapper(&self) -> &A {
323        &self.calendar
324    }
325
326    #[cfg(test)]
327    pub(crate) fn to_fixed(&self) -> calendrical_calculations::rata_die::RataDie {
328        Iso::fixed_from_iso(self.to_iso().inner)
329    }
330}
331
332impl<C: IntoAnyCalendar, A: AsCalendar<Calendar = C>> Date<A> {
333    pub fn to_any(&self) -> Date<AnyCalendar> {
335        let cal = self.calendar();
336        Date::from_raw(cal.date_to_any(self.inner()), cal.to_any_cloned())
337    }
338}
339
340impl<C: Calendar> Date<C> {
341    pub fn wrap_calendar_in_rc(self) -> Date<Rc<C>> {
345        Date::from_raw(self.inner, Rc::new(self.calendar))
346    }
347
348    pub fn wrap_calendar_in_arc(self) -> Date<Arc<C>> {
352        Date::from_raw(self.inner, Arc::new(self.calendar))
353    }
354}
355
356impl<C, A, B> PartialEq<Date<B>> for Date<A>
357where
358    C: Calendar,
359    A: AsCalendar<Calendar = C>,
360    B: AsCalendar<Calendar = C>,
361{
362    fn eq(&self, other: &Date<B>) -> bool {
363        self.inner.eq(&other.inner)
364    }
365}
366
367impl<A: AsCalendar> Eq for Date<A> {}
368
369impl<C, A, B> PartialOrd<Date<B>> for Date<A>
370where
371    C: Calendar,
372    C::DateInner: PartialOrd,
373    A: AsCalendar<Calendar = C>,
374    B: AsCalendar<Calendar = C>,
375{
376    fn partial_cmp(&self, other: &Date<B>) -> Option<core::cmp::Ordering> {
377        self.inner.partial_cmp(&other.inner)
378    }
379}
380
381impl<C, A> Ord for Date<A>
382where
383    C: Calendar,
384    C::DateInner: Ord,
385    A: AsCalendar<Calendar = C>,
386{
387    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
388        self.inner.cmp(&other.inner)
389    }
390}
391
392impl<A: AsCalendar> fmt::Debug for Date<A> {
393    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
394        write!(
395            f,
396            "Date({}-{}-{}, {} era, for calendar {})",
397            self.year().number,
398            self.month().ordinal,
399            self.day_of_month().0,
400            self.year().era.0,
401            self.calendar.as_calendar().debug_name()
402        )
403    }
404}
405
406impl<A: AsCalendar + Clone> Clone for Date<A> {
407    fn clone(&self) -> Self {
408        Self {
409            inner: self.inner.clone(),
410            calendar: self.calendar.clone(),
411        }
412    }
413}
414
415impl<A> Copy for Date<A>
416where
417    A: AsCalendar + Copy,
418    <<A as AsCalendar>::Calendar as Calendar>::DateInner: Copy,
419{
420}
421
422#[cfg(test)]
423mod tests {
424    use super::*;
425
426    #[test]
427    fn test_ord() {
428        let dates_in_order = [
429            Date::try_new_iso_date(-10, 1, 1).unwrap(),
430            Date::try_new_iso_date(-10, 1, 2).unwrap(),
431            Date::try_new_iso_date(-10, 2, 1).unwrap(),
432            Date::try_new_iso_date(-1, 1, 1).unwrap(),
433            Date::try_new_iso_date(-1, 1, 2).unwrap(),
434            Date::try_new_iso_date(-1, 2, 1).unwrap(),
435            Date::try_new_iso_date(0, 1, 1).unwrap(),
436            Date::try_new_iso_date(0, 1, 2).unwrap(),
437            Date::try_new_iso_date(0, 2, 1).unwrap(),
438            Date::try_new_iso_date(1, 1, 1).unwrap(),
439            Date::try_new_iso_date(1, 1, 2).unwrap(),
440            Date::try_new_iso_date(1, 2, 1).unwrap(),
441            Date::try_new_iso_date(10, 1, 1).unwrap(),
442            Date::try_new_iso_date(10, 1, 2).unwrap(),
443            Date::try_new_iso_date(10, 2, 1).unwrap(),
444        ];
445        for (i, i_date) in dates_in_order.iter().enumerate() {
446            for (j, j_date) in dates_in_order.iter().enumerate() {
447                let result1 = i_date.cmp(j_date);
448                let result2 = j_date.cmp(i_date);
449                assert_eq!(result1.reverse(), result2);
450                assert_eq!(i.cmp(&j), i_date.cmp(j_date));
451            }
452        }
453    }
454}