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}