diff --git a/components/calendar/src/any_calendar.rs b/components/calendar/src/any_calendar.rs index bbdc98ceeb1..4c839f42e4b 100644 --- a/components/calendar/src/any_calendar.rs +++ b/components/calendar/src/any_calendar.rs @@ -92,7 +92,7 @@ pub enum AnyCalendar { } /// The inner date type for [`AnyCalendar`] -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] #[non_exhaustive] pub enum AnyDateInner { /// A date for a [`Gregorian`] calendar diff --git a/components/calendar/src/calendar_arithmetic.rs b/components/calendar/src/calendar_arithmetic.rs index 60a4574b41b..b65e4297c83 100644 --- a/components/calendar/src/calendar_arithmetic.rs +++ b/components/calendar/src/calendar_arithmetic.rs @@ -7,7 +7,7 @@ use core::convert::TryInto; use core::marker::PhantomData; use tinystr::tinystr; -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] #[allow(clippy::exhaustive_structs)] // this type is stable pub struct ArithmeticDate { pub year: i32, @@ -250,3 +250,38 @@ pub fn ordinal_solar_month_from_code(code: types::MonthCode) -> Option { } None } + +#[cfg(test)] +mod tests { + use super::*; + use crate::Iso; + + #[test] + fn test_ord() { + let dates_in_order = [ + ArithmeticDate::::new(-10, 1, 1), + ArithmeticDate::::new(-10, 1, 2), + ArithmeticDate::::new(-10, 2, 1), + ArithmeticDate::::new(-1, 1, 1), + ArithmeticDate::::new(-1, 1, 2), + ArithmeticDate::::new(-1, 2, 1), + ArithmeticDate::::new(0, 1, 1), + ArithmeticDate::::new(0, 1, 2), + ArithmeticDate::::new(0, 2, 1), + ArithmeticDate::::new(1, 1, 1), + ArithmeticDate::::new(1, 1, 2), + ArithmeticDate::::new(1, 2, 1), + ArithmeticDate::::new(10, 1, 1), + ArithmeticDate::::new(10, 1, 2), + ArithmeticDate::::new(10, 2, 1), + ]; + 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)); + } + } + } +} diff --git a/components/calendar/src/coptic.rs b/components/calendar/src/coptic.rs index 6facafae0ee..2d8f76606f0 100644 --- a/components/calendar/src/coptic.rs +++ b/components/calendar/src/coptic.rs @@ -53,12 +53,12 @@ use tinystr::tinystr; /// /// This calendar supports two era codes: `"bd"`, and `"ad"`, corresponding to the Before Diocletian and After Diocletian/Anno Martyrum /// eras. 1 A.M. is equivalent to 284 C.E. -#[derive(Copy, Clone, Debug, Hash, Default, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Hash, Default, Eq, PartialEq, PartialOrd, Ord)] #[allow(clippy::exhaustive_structs)] // this type is stable pub struct Coptic; /// The inner date type used for representing [`Date`]s of [`Coptic`]. See [`Date`] and [`Coptic`] for more details. -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)] pub struct CopticDateInner(pub(crate) ArithmeticDate); impl CalendarArithmetic for Coptic { diff --git a/components/calendar/src/date.rs b/components/calendar/src/date.rs index d5ed77c7635..ead76b9655d 100644 --- a/components/calendar/src/date.rs +++ b/components/calendar/src/date.rs @@ -355,6 +355,29 @@ where impl Eq for Date {} +impl PartialOrd> for Date +where + C: Calendar, + C::DateInner: PartialOrd, + A: AsCalendar, + B: AsCalendar, +{ + fn partial_cmp(&self, other: &Date) -> Option { + self.inner.partial_cmp(&other.inner) + } +} + +impl Ord for Date +where + C: Calendar, + C::DateInner: Ord, + A: AsCalendar, +{ + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.inner.cmp(&other.inner) + } +} + impl fmt::Debug for Date { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { write!( @@ -381,3 +404,37 @@ where <::Calendar as Calendar>::DateInner: Copy, { } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ord() { + let dates_in_order = [ + Date::try_new_iso_date(-10, 1, 1).unwrap(), + Date::try_new_iso_date(-10, 1, 2).unwrap(), + Date::try_new_iso_date(-10, 2, 1).unwrap(), + Date::try_new_iso_date(-1, 1, 1).unwrap(), + Date::try_new_iso_date(-1, 1, 2).unwrap(), + Date::try_new_iso_date(-1, 2, 1).unwrap(), + Date::try_new_iso_date(0, 1, 1).unwrap(), + Date::try_new_iso_date(0, 1, 2).unwrap(), + Date::try_new_iso_date(0, 2, 1).unwrap(), + Date::try_new_iso_date(1, 1, 1).unwrap(), + Date::try_new_iso_date(1, 1, 2).unwrap(), + Date::try_new_iso_date(1, 2, 1).unwrap(), + Date::try_new_iso_date(10, 1, 1).unwrap(), + Date::try_new_iso_date(10, 1, 2).unwrap(), + Date::try_new_iso_date(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)); + } + } + } +} diff --git a/components/calendar/src/datetime.rs b/components/calendar/src/datetime.rs index 3a77ed580cc..a65fcf43f80 100644 --- a/components/calendar/src/datetime.rs +++ b/components/calendar/src/datetime.rs @@ -137,6 +137,32 @@ where // We can do this since DateInner is required to be Eq by the Calendar trait impl Eq for DateTime {} +impl PartialOrd> for DateTime +where + C: Calendar, + C::DateInner: PartialOrd, + A: AsCalendar, + B: AsCalendar, +{ + fn partial_cmp(&self, other: &DateTime) -> Option { + match self.date.partial_cmp(&other.date) { + Some(core::cmp::Ordering::Equal) => self.time.partial_cmp(&other.time), + other => other, + } + } +} + +impl Ord for DateTime +where + C: Calendar, + C::DateInner: Ord, + A: AsCalendar, +{ + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + (&self.date, &self.time).cmp(&(&other.date, &other.time)) + } +} + impl Clone for DateTime { fn clone(&self) -> Self { Self { @@ -152,3 +178,29 @@ where <::Calendar as Calendar>::DateInner: Copy, { } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ord() { + let dates_in_order = [ + DateTime::try_new_iso_datetime(0, 1, 1, 0, 0, 0).unwrap(), + DateTime::try_new_iso_datetime(0, 1, 1, 0, 0, 1).unwrap(), + DateTime::try_new_iso_datetime(0, 1, 1, 0, 1, 0).unwrap(), + DateTime::try_new_iso_datetime(0, 1, 1, 1, 0, 0).unwrap(), + DateTime::try_new_iso_datetime(0, 1, 2, 0, 0, 0).unwrap(), + DateTime::try_new_iso_datetime(0, 2, 1, 0, 0, 0).unwrap(), + DateTime::try_new_iso_datetime(1, 1, 1, 0, 0, 0).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)); + } + } + } +} diff --git a/components/calendar/src/ethiopian.rs b/components/calendar/src/ethiopian.rs index ff02b5515ab..6861f8608c3 100644 --- a/components/calendar/src/ethiopian.rs +++ b/components/calendar/src/ethiopian.rs @@ -74,11 +74,11 @@ pub enum EthiopianEraStyle { /// the `"incar"` and `"pre-incar"` eras, 1 Incarnation is 9 CE. In the Amete Alem scheme, it instead has a single era, /// `"mundi`, where 1 Anno Mundi is 5493 BCE. Dates before that use negative year numbers. // The bool specifies whether dates should be in the Amete Alem era scheme -#[derive(Copy, Clone, Debug, Hash, Default, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Hash, Default, Eq, PartialEq, PartialOrd, Ord)] pub struct Ethiopian(pub(crate) bool); /// The inner date type used for representing [`Date`]s of [`Ethiopian`]. See [`Date`] and [`Ethiopian`] for more details. -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)] pub struct EthiopianDateInner(ArithmeticDate); impl CalendarArithmetic for Ethiopian { diff --git a/components/calendar/src/gregorian.rs b/components/calendar/src/gregorian.rs index fa57c2333d6..14386ccb85a 100644 --- a/components/calendar/src/gregorian.rs +++ b/components/calendar/src/gregorian.rs @@ -52,7 +52,7 @@ use tinystr::tinystr; #[allow(clippy::exhaustive_structs)] // this type is stable pub struct Gregorian; -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)] /// The inner date type used for representing [`Date`]s of [`Gregorian`]. See [`Date`] and [`Gregorian`] for more details. pub struct GregorianDateInner(IsoDateInner); diff --git a/components/calendar/src/indian.rs b/components/calendar/src/indian.rs index 056873df74d..4309a9bf340 100644 --- a/components/calendar/src/indian.rs +++ b/components/calendar/src/indian.rs @@ -49,12 +49,12 @@ use tinystr::tinystr; /// # Era codes /// /// This calendar has a single era: `"saka"`, with Saka 0 being 78 CE. Dates before this era use negative years. -#[derive(Copy, Clone, Debug, Hash, Default, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Hash, Default, Eq, PartialEq, PartialOrd, Ord)] #[allow(clippy::exhaustive_structs)] // this type is stable pub struct Indian; /// The inner date type used for representing [`Date`]s of [`Indian`]. See [`Date`] and [`Indian`] for more details. -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)] pub struct IndianDateInner(ArithmeticDate); impl CalendarArithmetic for Indian { diff --git a/components/calendar/src/iso.rs b/components/calendar/src/iso.rs index bf14f2d35c8..1f2c090188d 100644 --- a/components/calendar/src/iso.rs +++ b/components/calendar/src/iso.rs @@ -52,11 +52,11 @@ const EPOCH: i32 = 1; /// /// This calendar supports one era, `"default"` -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] #[allow(clippy::exhaustive_structs)] // this type is stable pub struct Iso; -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)] /// The inner date type used for representing [`Date`]s of [`Iso`]. See [`Date`] and [`Iso`] for more details. pub struct IsoDateInner(pub(crate) ArithmeticDate); diff --git a/components/calendar/src/japanese.rs b/components/calendar/src/japanese.rs index b1fa78e4ca8..390b8252b12 100644 --- a/components/calendar/src/japanese.rs +++ b/components/calendar/src/japanese.rs @@ -105,7 +105,7 @@ pub struct Japanese { #[derive(Clone, Debug, Default)] pub struct JapaneseExtended(Japanese); -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)] /// The inner date type used for representing [`Date`]s of [`Japanese`]. See [`Date`] and [`Japanese`] for more details. pub struct JapaneseDateInner { inner: IsoDateInner,