icu_calendar/
date.rs

1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5use crate::any_calendar::{AnyCalendar, IntoAnyCalendar};
6use crate::error::{DateError, DateFromFieldsError};
7use crate::options::DateFromFieldsOptions;
8use crate::options::{DateAddOptions, DateDifferenceOptions};
9use crate::types::{CyclicYear, EraYear, IsoWeekOfYear};
10use crate::week::{RelativeUnit, WeekCalculator, WeekOf};
11use crate::{types, Calendar, Iso};
12#[cfg(feature = "alloc")]
13use alloc::rc::Rc;
14#[cfg(feature = "alloc")]
15use alloc::sync::Arc;
16use calendrical_calculations::rata_die::RataDie;
17use core::fmt;
18use core::ops::Deref;
19
20/// Types that contain a calendar
21///
22/// This allows one to use [`Date`] with wrappers around calendars,
23/// e.g. reference counted calendars.
24pub trait AsCalendar {
25    /// The calendar being wrapped
26    type Calendar: Calendar;
27    /// Obtain the inner calendar
28    fn as_calendar(&self) -> &Self::Calendar;
29}
30
31impl<C: Calendar> AsCalendar for C {
32    type Calendar = C;
33    #[inline]
34    fn as_calendar(&self) -> &Self {
35        self
36    }
37}
38
39#[cfg(feature = "alloc")]
40/// ✨ *Enabled with the `alloc` Cargo feature.*
41impl<C: AsCalendar> AsCalendar for Rc<C> {
42    type Calendar = C::Calendar;
43    #[inline]
44    fn as_calendar(&self) -> &Self::Calendar {
45        self.as_ref().as_calendar()
46    }
47}
48
49#[cfg(feature = "alloc")]
50/// ✨ *Enabled with the `alloc` Cargo feature.*
51impl<C: AsCalendar> AsCalendar for Arc<C> {
52    type Calendar = C::Calendar;
53    #[inline]
54    fn as_calendar(&self) -> &Self::Calendar {
55        self.as_ref().as_calendar()
56    }
57}
58
59/// This exists as a wrapper around `&'a T` so that
60/// `Date<&'a C>` is possible for calendar `C`.
61///
62/// Unfortunately,
63/// [`AsCalendar`] cannot be implemented on `&'a T` directly because
64/// `&'a T` is `#[fundamental]` and the impl would clash with the one above with
65/// `AsCalendar` for `C: Calendar`.
66///
67/// Use `Date<Ref<'a, C>>` where you would use `Date<&'a C>`
68#[allow(clippy::exhaustive_structs)] // newtype
69#[derive(PartialEq, Eq, Debug)]
70pub struct Ref<'a, C>(pub &'a C);
71
72impl<C> Copy for Ref<'_, C> {}
73
74impl<C> Clone for Ref<'_, C> {
75    fn clone(&self) -> Self {
76        *self
77    }
78}
79
80impl<C: AsCalendar> AsCalendar for Ref<'_, C> {
81    type Calendar = C::Calendar;
82    #[inline]
83    fn as_calendar(&self) -> &Self::Calendar {
84        self.0.as_calendar()
85    }
86}
87
88impl<C> Deref for Ref<'_, C> {
89    type Target = C;
90    fn deref(&self) -> &C {
91        self.0
92    }
93}
94
95/// A date for a given calendar.
96///
97/// **The primary definition of this type is in the [`icu_calendar`](https://docs.rs/icu_calendar) crate. Other ICU4X crates re-export it for convenience.**
98///
99/// This can work with wrappers around [`Calendar`] types,
100/// e.g. `Rc<C>`, via the [`AsCalendar`] trait.
101///
102/// This can be constructed  constructed
103/// from its fields via [`Self::try_new_from_codes()`], or can be constructed with one of the
104/// `new_<calendar>_date()` per-calendar methods (and then freely converted between calendars).
105///
106/// ```rust
107/// use icu::calendar::Date;
108///
109/// // Example: creation of ISO date from integers.
110/// let date_iso = Date::try_new_iso(1970, 1, 2)
111///     .expect("Failed to initialize ISO Date instance.");
112///
113/// assert_eq!(date_iso.era_year().year, 1970);
114/// assert_eq!(date_iso.month().ordinal, 1);
115/// assert_eq!(date_iso.day_of_month().0, 2);
116/// ```
117pub struct Date<A: AsCalendar> {
118    pub(crate) inner: <A::Calendar as Calendar>::DateInner,
119    pub(crate) calendar: A,
120}
121
122impl<A: AsCalendar> Date<A> {
123    /// Construct a date from from era/month codes and fields, and some calendar representation
124    ///
125    /// The year is `extended_year` if no era is provided.
126    ///
127    /// This function will not accept year/extended_year values that are outside of the range `[-2²⁷, 2²⁷]`,
128    /// regardless of the calendar, instead returning a [`DateError::Range`]. See [`Date::try_from_fields()`] for more
129    /// information.
130    #[inline]
131    pub fn try_new_from_codes(
132        era: Option<&str>,
133        year: i32,
134        month_code: types::MonthCode,
135        day: u8,
136        calendar: A,
137    ) -> Result<Self, DateError> {
138        let inner = calendar
139            .as_calendar()
140            .from_codes(era, year, month_code, day)?;
141        Ok(Date { inner, calendar })
142    }
143
144    /// Construct a date from from a bag of fields.
145    ///
146    /// This function allows specifying the year as either extended year or era + era year,
147    /// and the month as either ordinal or month code. It can constrain out-of-bounds values
148    /// and fill in missing fields. See [`DateFromFieldsOptions`] for more information.
149    ///
150    /// This function will not accept year/extended_year values that are outside of the range `[-2²⁷, 2²⁷]`,
151    /// regardless of the calendar, instead returning a [`DateFromFieldsError::Range`]. This allows us to to keep
152    /// all operations on [`Date`]s infallible by staying clear of integer limits.
153    /// Currently, calendar-specific `Date::try_new_calendarname()` constructors
154    /// do not do this, and it is possible to obtain such extreme dates via calendar conversion or arithmetic,
155    /// though [we may change that behavior in the future](https://github.com/unicode-org/icu4x/issues/7076).
156    ///
157    /// # Examples
158    ///
159    /// ```
160    /// use icu::calendar::cal::Gregorian;
161    /// use icu::calendar::types::DateFields;
162    /// use icu::calendar::Date;
163    ///
164    /// let mut fields = DateFields::default();
165    /// fields.extended_year = Some(2000);
166    /// fields.ordinal_month = Some(1);
167    /// fields.day = Some(1);
168    ///
169    /// let d1 = Date::try_from_fields(fields, Default::default(), Gregorian)
170    ///     .expect("Jan 1 in year 2000");
171    ///
172    /// let d2 = Date::try_new_gregorian(2000, 1, 1).unwrap();
173    /// assert_eq!(d1, d2);
174    /// ```
175    ///
176    /// See [`DateFromFieldsError`] for examples of error conditions.
177    ///
178    /// <div class="stab unstable">
179    /// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
180    /// including in SemVer minor releases. Do not use this type unless you are prepared for things to occasionally break.
181    ///
182    /// Graduation tracking issue: [issue #7161](https://github.com/unicode-org/icu4x/issues/7161).
183    /// </div>
184    ///
185    /// ✨ *Enabled with the `unstable` Cargo feature.*
186    #[cfg(feature = "unstable")]
187    #[inline]
188    pub fn try_from_fields(
189        fields: types::DateFields,
190        options: DateFromFieldsOptions,
191        calendar: A,
192    ) -> Result<Self, DateFromFieldsError> {
193        let inner = calendar.as_calendar().from_fields(fields, options)?;
194        Ok(Date { inner, calendar })
195    }
196
197    /// Construct a date from a [`RataDie`] and some calendar representation
198    #[inline]
199    pub fn from_rata_die(rd: RataDie, calendar: A) -> Self {
200        Date {
201            inner: calendar.as_calendar().from_rata_die(rd),
202            calendar,
203        }
204    }
205
206    /// Convert the date to a [`RataDie`]
207    #[inline]
208    pub fn to_rata_die(&self) -> RataDie {
209        self.calendar.as_calendar().to_rata_die(self.inner())
210    }
211
212    /// Construct a date from an ISO date and some calendar representation
213    #[inline]
214    pub fn new_from_iso(iso: Date<Iso>, calendar: A) -> Self {
215        iso.to_calendar(calendar)
216    }
217
218    /// Convert the Date to an ISO Date
219    #[inline]
220    pub fn to_iso(&self) -> Date<Iso> {
221        self.to_calendar(Iso)
222    }
223
224    /// Convert the Date to a date in a different calendar
225    #[inline]
226    pub fn to_calendar<A2: AsCalendar>(&self, calendar: A2) -> Date<A2> {
227        let c1 = self.calendar.as_calendar();
228        let c2 = calendar.as_calendar();
229        let inner = if c1.has_cheap_iso_conversion() && c2.has_cheap_iso_conversion() {
230            c2.from_iso(c1.to_iso(self.inner()))
231        } else {
232            c2.from_rata_die(c1.to_rata_die(self.inner()))
233        };
234        Date { inner, calendar }
235    }
236
237    /// The number of months in the year of this date
238    #[inline]
239    pub fn months_in_year(&self) -> u8 {
240        self.calendar.as_calendar().months_in_year(self.inner())
241    }
242
243    /// The number of days in the year of this date
244    #[inline]
245    pub fn days_in_year(&self) -> u16 {
246        self.calendar.as_calendar().days_in_year(self.inner())
247    }
248
249    /// The number of days in the month of this date
250    #[inline]
251    pub fn days_in_month(&self) -> u8 {
252        self.calendar.as_calendar().days_in_month(self.inner())
253    }
254
255    /// The day of the week for this date
256    #[inline]
257    pub fn day_of_week(&self) -> types::Weekday {
258        self.to_rata_die().into()
259    }
260
261    /// Add a `duration` to this date, mutating it
262    ///
263    /// <div class="stab unstable">
264    /// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
265    /// including in SemVer minor releases. Do not use this type unless you are prepared for things to occasionally break.
266    ///
267    /// Graduation tracking issue: [issue #3964](https://github.com/unicode-org/icu4x/issues/3964).
268    /// </div>
269    ///
270    /// ✨ *Enabled with the `unstable` Cargo feature.*
271    #[cfg(feature = "unstable")]
272    #[inline]
273    pub fn try_add_with_options(
274        &mut self,
275        duration: types::DateDuration,
276        options: DateAddOptions,
277    ) -> Result<(), DateError> {
278        let inner = self
279            .calendar
280            .as_calendar()
281            .add(&self.inner, duration, options)?;
282        self.inner = inner;
283        Ok(())
284    }
285
286    /// Add a `duration` to this date, returning the new one
287    ///
288    /// <div class="stab unstable">
289    /// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
290    /// including in SemVer minor releases. Do not use this type unless you are prepared for things to occasionally break.
291    ///
292    /// Graduation tracking issue: [issue #3964](https://github.com/unicode-org/icu4x/issues/3964).
293    /// </div>
294    ///
295    /// ✨ *Enabled with the `unstable` Cargo feature.*
296    #[cfg(feature = "unstable")]
297    #[inline]
298    pub fn try_added_with_options(
299        mut self,
300        duration: types::DateDuration,
301        options: DateAddOptions,
302    ) -> Result<Self, DateError> {
303        self.try_add_with_options(duration, options)?;
304        Ok(self)
305    }
306
307    /// Calculating the duration between `other - self`
308    ///
309    /// Although this returns a [`Result`], with most fixed calendars, this operation can't fail.
310    /// In such cases, the error type is [`Infallible`], and the inner value can be safely
311    /// unwrapped using [`Result::into_ok()`], which is available in nightly Rust as of this
312    /// writing. In stable Rust, the value can be unwrapped using [pattern matching].
313    ///
314    /// # Examples
315    ///
316    /// ```
317    /// use icu::calendar::types::DateDuration;
318    /// use icu::calendar::Date;
319    ///
320    /// let d1 = Date::try_new_iso(2020, 1, 1).unwrap();
321    /// let d2 = Date::try_new_iso(2025, 10, 2).unwrap();
322    /// let options = Default::default();
323    ///
324    /// // The value can be unwrapped with destructuring syntax:
325    /// let Ok(duration) = d1.try_until_with_options(&d2, options);
326    ///
327    /// assert_eq!(duration, DateDuration::for_days(2101));
328    /// ```
329    ///
330    /// [`Infallible`]: core::convert::Infallible
331    /// [pattern matching]: https://doc.rust-lang.org/book/ch19-03-pattern-syntax.html
332    ///
333    /// <div class="stab unstable">
334    /// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
335    /// including in SemVer minor releases. Do not use this type unless you are prepared for things to occasionally break.
336    ///
337    /// Graduation tracking issue: [issue #3964](https://github.com/unicode-org/icu4x/issues/3964).
338    /// </div>
339    ///
340    /// ✨ *Enabled with the `unstable` Cargo feature.*
341    #[cfg(feature = "unstable")]
342    #[inline]
343    pub fn try_until_with_options<B: AsCalendar<Calendar = A::Calendar>>(
344        &self,
345        other: &Date<B>,
346        options: DateDifferenceOptions,
347    ) -> Result<types::DateDuration, <A::Calendar as Calendar>::DifferenceError> {
348        self.calendar
349            .as_calendar()
350            .until(self.inner(), other.inner(), options)
351    }
352
353    /// The calendar-specific year-info.
354    ///
355    /// This returns an enum, see [`Date::era_year()`] and [`Date::cyclic_year()`] which are available
356    /// for concrete calendar types and return concrete types.
357    #[inline]
358    pub fn year(&self) -> types::YearInfo {
359        self.calendar.as_calendar().year_info(&self.inner).into()
360    }
361
362    /// The "extended year".
363    ///
364    /// This year number can be used when you need a simple numeric representation
365    /// of the year, and can be meaningfully compared with extended years from other
366    /// eras or used in arithmetic.
367    ///
368    /// For calendars defined in Temporal, this will match the "arithmetic year"
369    /// as defined in <https://tc39.es/proposal-intl-era-monthcode/>.
370    /// This is typically anchored with year 1 as the year 1 of either the most modern or
371    /// otherwise some "major" era for the calendar.
372    ///
373    /// See [`Self::year()`] for more information about the year.
374    #[inline]
375    pub fn extended_year(&self) -> i32 {
376        self.year().extended_year()
377    }
378
379    /// Returns whether `self` is in a calendar-specific leap year
380    #[inline]
381    pub fn is_in_leap_year(&self) -> bool {
382        self.calendar.as_calendar().is_in_leap_year(&self.inner)
383    }
384
385    /// The calendar-specific month represented by `self`
386    #[inline]
387    pub fn month(&self) -> types::MonthInfo {
388        self.calendar.as_calendar().month(&self.inner)
389    }
390
391    /// The calendar-specific day-of-month represented by `self`
392    #[inline]
393    pub fn day_of_month(&self) -> types::DayOfMonth {
394        self.calendar.as_calendar().day_of_month(&self.inner)
395    }
396
397    /// The calendar-specific day-of-month represented by `self`
398    #[inline]
399    pub fn day_of_year(&self) -> types::DayOfYear {
400        self.calendar.as_calendar().day_of_year(&self.inner)
401    }
402
403    /// Construct a date from raw values for a given calendar. This does not check any
404    /// invariants for the date and calendar, and should only be called by calendar implementations.
405    ///
406    /// Calling this outside of calendar implementations is sound, but calendar implementations are not
407    /// expected to do anything sensible with such invalid dates.
408    ///
409    /// AnyCalendar *will* panic if AnyCalendar [`Date`] objects with mismatching
410    /// date and calendar types are constructed
411    #[inline]
412    pub fn from_raw(inner: <A::Calendar as Calendar>::DateInner, calendar: A) -> Self {
413        Self { inner, calendar }
414    }
415
416    /// Get the inner date implementation. Should not be called outside of calendar implementations
417    #[inline]
418    pub fn inner(&self) -> &<A::Calendar as Calendar>::DateInner {
419        &self.inner
420    }
421
422    /// Get a reference to the contained calendar
423    #[inline]
424    pub fn calendar(&self) -> &A::Calendar {
425        self.calendar.as_calendar()
426    }
427
428    /// Get a reference to the contained calendar wrapper
429    ///
430    /// (Useful in case the user wishes to e.g. clone an Rc)
431    #[inline]
432    pub fn calendar_wrapper(&self) -> &A {
433        &self.calendar
434    }
435}
436
437impl<A: AsCalendar<Calendar = C>, C: Calendar<Year = EraYear>> Date<A> {
438    /// Returns information about the era for calendars using eras.
439    pub fn era_year(&self) -> EraYear {
440        self.calendar.as_calendar().year_info(self.inner())
441    }
442}
443
444impl<A: AsCalendar<Calendar = C>, C: Calendar<Year = CyclicYear>> Date<A> {
445    /// Returns information about the year cycle, for cyclic calendars.
446    pub fn cyclic_year(&self) -> CyclicYear {
447        self.calendar.as_calendar().year_info(self.inner())
448    }
449}
450
451impl Date<Iso> {
452    /// The ISO week of the year containing this date.
453    ///
454    /// # Examples
455    ///
456    /// ```
457    /// use icu::calendar::types::IsoWeekOfYear;
458    /// use icu::calendar::Date;
459    ///
460    /// let date = Date::try_new_iso(2022, 8, 26).unwrap();
461    ///
462    /// assert_eq!(
463    ///     date.week_of_year(),
464    ///     IsoWeekOfYear {
465    ///         week_number: 34,
466    ///         iso_year: 2022,
467    ///     }
468    /// );
469    /// ```
470    pub fn week_of_year(&self) -> IsoWeekOfYear {
471        let week_of = WeekCalculator::ISO
472            .week_of(
473                365 + calendrical_calculations::gregorian::is_leap_year(self.inner.0.year - 1)
474                    as u16,
475                self.days_in_year(),
476                self.day_of_year().0,
477                self.day_of_week(),
478            )
479            .unwrap_or_else(|_| {
480                // ISO calendar has more than 14 days per year
481                debug_assert!(false);
482                WeekOf {
483                    week: 1,
484                    unit: crate::week::RelativeUnit::Current,
485                }
486            });
487
488        IsoWeekOfYear {
489            week_number: week_of.week,
490            iso_year: match week_of.unit {
491                RelativeUnit::Current => self.inner.0.year,
492                RelativeUnit::Next => self.inner.0.year + 1,
493                RelativeUnit::Previous => self.inner.0.year - 1,
494            },
495        }
496    }
497}
498
499impl<C: IntoAnyCalendar> Date<C> {
500    /// Type-erase the date, converting it to a date for [`AnyCalendar`]
501    pub fn to_any(self) -> Date<AnyCalendar> {
502        Date::from_raw(
503            self.calendar.date_to_any(&self.inner),
504            self.calendar.to_any(),
505        )
506    }
507}
508
509impl<A: AsCalendar> Date<A> {
510    /// Wrap the contained calendar type in `Rc<T>`, making it cheaper to clone.
511    ///
512    /// Useful when paired with [`Self::to_any()`] to obtain a `Date<Rc<AnyCalendar>>`
513    ///
514    /// ✨ *Enabled with the `alloc` Cargo feature.*
515    #[cfg(feature = "alloc")]
516    pub fn into_ref_counted(self) -> Date<Rc<A>> {
517        Date::from_raw(self.inner, Rc::new(self.calendar))
518    }
519
520    /// Wrap the contained calendar type in `Arc<T>`, making it cheaper to clone in a thread-safe manner.
521    ///
522    /// Useful when paired with [`Self::to_any()`] to obtain a `Date<Arc<AnyCalendar>>`
523    ///
524    /// ✨ *Enabled with the `alloc` Cargo feature.*
525    #[cfg(feature = "alloc")]
526    pub fn into_atomic_ref_counted(self) -> Date<Arc<A>> {
527        Date::from_raw(self.inner, Arc::new(self.calendar))
528    }
529
530    /// Wrap the calendar type in `Ref<T>`, making it cheaper to clone (by introducing a borrow)
531    ///
532    /// Useful for converting a `&Date<C>` into an equivalent `Date<D>` without cloning
533    /// the calendar.
534    pub fn as_borrowed(&self) -> Date<Ref<'_, A>> {
535        Date::from_raw(self.inner, Ref(&self.calendar))
536    }
537}
538
539impl<C, A, B> PartialEq<Date<B>> for Date<A>
540where
541    C: Calendar,
542    A: AsCalendar<Calendar = C>,
543    B: AsCalendar<Calendar = C>,
544{
545    fn eq(&self, other: &Date<B>) -> bool {
546        self.inner.eq(&other.inner)
547    }
548}
549
550impl<A: AsCalendar> Eq for Date<A> {}
551
552impl<C, A, B> PartialOrd<Date<B>> for Date<A>
553where
554    C: Calendar,
555    A: AsCalendar<Calendar = C>,
556    B: AsCalendar<Calendar = C>,
557{
558    fn partial_cmp(&self, other: &Date<B>) -> Option<core::cmp::Ordering> {
559        self.inner.partial_cmp(&other.inner)
560    }
561}
562
563impl<C, A> Ord for Date<A>
564where
565    C: Calendar,
566    C::DateInner: Ord,
567    A: AsCalendar<Calendar = C>,
568{
569    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
570        self.inner.cmp(&other.inner)
571    }
572}
573
574impl<A: AsCalendar> fmt::Debug for Date<A> {
575    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
576        let month = self.month().ordinal;
577        let day = self.day_of_month().0;
578        let calendar = self.calendar.as_calendar().debug_name();
579        match self.year() {
580            types::YearInfo::Era(EraYear { year, era, .. }) => {
581                write!(
582                    f,
583                    "Date({year}-{month}-{day}, {era} era, for calendar {calendar})"
584                )
585            }
586            types::YearInfo::Cyclic(CyclicYear { year, related_iso }) => {
587                write!(
588                    f,
589                    "Date({year}-{month}-{day}, ISO year {related_iso}, for calendar {calendar})"
590                )
591            }
592        }
593    }
594}
595
596impl<A: AsCalendar + Clone> Clone for Date<A> {
597    fn clone(&self) -> Self {
598        Self {
599            inner: self.inner,
600            calendar: self.calendar.clone(),
601        }
602    }
603}
604
605impl<A> Copy for Date<A> where A: AsCalendar + Copy {}
606
607#[cfg(test)]
608mod tests {
609    use super::*;
610    use crate::types::Weekday;
611
612    #[test]
613    fn test_ord() {
614        let dates_in_order = [
615            Date::try_new_iso(-10, 1, 1).unwrap(),
616            Date::try_new_iso(-10, 1, 2).unwrap(),
617            Date::try_new_iso(-10, 2, 1).unwrap(),
618            Date::try_new_iso(-1, 1, 1).unwrap(),
619            Date::try_new_iso(-1, 1, 2).unwrap(),
620            Date::try_new_iso(-1, 2, 1).unwrap(),
621            Date::try_new_iso(0, 1, 1).unwrap(),
622            Date::try_new_iso(0, 1, 2).unwrap(),
623            Date::try_new_iso(0, 2, 1).unwrap(),
624            Date::try_new_iso(1, 1, 1).unwrap(),
625            Date::try_new_iso(1, 1, 2).unwrap(),
626            Date::try_new_iso(1, 2, 1).unwrap(),
627            Date::try_new_iso(10, 1, 1).unwrap(),
628            Date::try_new_iso(10, 1, 2).unwrap(),
629            Date::try_new_iso(10, 2, 1).unwrap(),
630        ];
631        for (i, i_date) in dates_in_order.iter().enumerate() {
632            for (j, j_date) in dates_in_order.iter().enumerate() {
633                let result1 = i_date.cmp(j_date);
634                let result2 = j_date.cmp(i_date);
635                assert_eq!(result1.reverse(), result2);
636                assert_eq!(i.cmp(&j), i_date.cmp(j_date));
637            }
638        }
639    }
640
641    #[test]
642    fn test_day_of_week() {
643        // June 23, 2021 is a Wednesday
644        assert_eq!(
645            Date::try_new_iso(2021, 6, 23).unwrap().day_of_week(),
646            Weekday::Wednesday,
647        );
648        // Feb 2, 1983 was a Wednesday
649        assert_eq!(
650            Date::try_new_iso(1983, 2, 2).unwrap().day_of_week(),
651            Weekday::Wednesday,
652        );
653        // Jan 21, 2021 was a Tuesday
654        assert_eq!(
655            Date::try_new_iso(2020, 1, 21).unwrap().day_of_week(),
656            Weekday::Tuesday,
657        );
658    }
659}