icu_calendar/
datetime.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::types::{self, Time};
7use crate::{AsCalendar, Calendar, CalendarError, Date, Iso};
8use alloc::rc::Rc;
9use alloc::sync::Arc;
10
11/// A date+time for a given calendar.
12///
13/// This can work with wrappers around [`Calendar`](crate::Calendar) types,
14/// e.g. `Rc<C>`, via the [`AsCalendar`] trait, much like
15/// [`Date`].
16///
17/// This can be constructed manually from a [`Date`] and [`Time`], or can be constructed
18/// from its fields via [`Self::try_new_from_codes()`], or can be constructed with one of the
19/// `new_<calendar>_datetime()` per-calendar methods (and then freely converted between calendars).
20///
21/// ```rust
22/// use icu::calendar::DateTime;
23///
24/// // Example: Construction of ISO datetime from integers.
25/// let datetime_iso = DateTime::try_new_iso_datetime(1970, 1, 2, 13, 1, 0)
26///     .expect("Failed to initialize ISO DateTime instance.");
27///
28/// assert_eq!(datetime_iso.date.year().number, 1970);
29/// assert_eq!(datetime_iso.date.month().ordinal, 1);
30/// assert_eq!(datetime_iso.date.day_of_month().0, 2);
31/// assert_eq!(datetime_iso.time.hour.number(), 13);
32/// assert_eq!(datetime_iso.time.minute.number(), 1);
33/// assert_eq!(datetime_iso.time.second.number(), 0);
34/// ```
35#[derive(Debug)]
36#[allow(clippy::exhaustive_structs)] // this type is stable
37pub struct DateTime<A: AsCalendar> {
38    /// The date
39    pub date: Date<A>,
40    /// The time
41    pub time: Time,
42}
43
44impl<A: AsCalendar> DateTime<A> {
45    /// Construct a [`DateTime`] for a given [`Date`] and [`Time`]
46    pub fn new(date: Date<A>, time: Time) -> Self {
47        DateTime { date, time }
48    }
49
50    /// Construct a datetime from from era/month codes and fields,
51    /// and some calendar representation
52    #[inline]
53    pub fn try_new_from_codes(
54        era: types::Era,
55        year: i32,
56        month_code: types::MonthCode,
57        day: u8,
58        time: Time,
59        calendar: A,
60    ) -> Result<Self, CalendarError> {
61        let date = Date::try_new_from_codes(era, year, month_code, day, calendar)?;
62        Ok(DateTime { date, time })
63    }
64
65    /// Construct a DateTime from an ISO datetime and some calendar representation
66    #[inline]
67    pub fn new_from_iso(iso: DateTime<Iso>, calendar: A) -> Self {
68        let date = Date::new_from_iso(iso.date, calendar);
69        DateTime {
70            date,
71            time: iso.time,
72        }
73    }
74
75    /// Convert the DateTime to an ISO DateTime
76    #[inline]
77    pub fn to_iso(&self) -> DateTime<Iso> {
78        DateTime {
79            date: self.date.to_iso(),
80            time: self.time,
81        }
82    }
83
84    /// Convert the DateTime to a DateTime in a different calendar
85    #[inline]
86    pub fn to_calendar<A2: AsCalendar>(&self, calendar: A2) -> DateTime<A2> {
87        DateTime {
88            date: self.date.to_calendar(calendar),
89            time: self.time,
90        }
91    }
92}
93
94impl<C: IntoAnyCalendar, A: AsCalendar<Calendar = C>> DateTime<A> {
95    /// Type-erase the date, converting it to a date for [`AnyCalendar`]
96    pub fn to_any(&self) -> DateTime<AnyCalendar> {
97        DateTime {
98            date: self.date.to_any(),
99            time: self.time,
100        }
101    }
102}
103
104impl<C: Calendar> DateTime<C> {
105    /// Wrap the calendar type in `Rc<T>`
106    ///
107    /// Useful when paired with [`Self::to_any()`] to obtain a `DateTime<Rc<AnyCalendar>>`
108    pub fn wrap_calendar_in_rc(self) -> DateTime<Rc<C>> {
109        DateTime {
110            date: self.date.wrap_calendar_in_rc(),
111            time: self.time,
112        }
113    }
114
115    /// Wrap the calendar type in `Arc<T>`
116    ///
117    /// Useful when paired with [`Self::to_any()`] to obtain a `DateTime<Rc<AnyCalendar>>`
118    pub fn wrap_calendar_in_arc(self) -> DateTime<Arc<C>> {
119        DateTime {
120            date: self.date.wrap_calendar_in_arc(),
121            time: self.time,
122        }
123    }
124}
125
126impl<C, A, B> PartialEq<DateTime<B>> for DateTime<A>
127where
128    C: Calendar,
129    A: AsCalendar<Calendar = C>,
130    B: AsCalendar<Calendar = C>,
131{
132    fn eq(&self, other: &DateTime<B>) -> bool {
133        self.date == other.date && self.time == other.time
134    }
135}
136
137// We can do this since DateInner is required to be Eq by the Calendar trait
138impl<A: AsCalendar> Eq for DateTime<A> {}
139
140impl<C, A, B> PartialOrd<DateTime<B>> for DateTime<A>
141where
142    C: Calendar,
143    C::DateInner: PartialOrd,
144    A: AsCalendar<Calendar = C>,
145    B: AsCalendar<Calendar = C>,
146{
147    fn partial_cmp(&self, other: &DateTime<B>) -> Option<core::cmp::Ordering> {
148        match self.date.partial_cmp(&other.date) {
149            Some(core::cmp::Ordering::Equal) => self.time.partial_cmp(&other.time),
150            other => other,
151        }
152    }
153}
154
155impl<C, A> Ord for DateTime<A>
156where
157    C: Calendar,
158    C::DateInner: Ord,
159    A: AsCalendar<Calendar = C>,
160{
161    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
162        (&self.date, &self.time).cmp(&(&other.date, &other.time))
163    }
164}
165
166impl<A: AsCalendar + Clone> Clone for DateTime<A> {
167    fn clone(&self) -> Self {
168        Self {
169            date: self.date.clone(),
170            time: self.time,
171        }
172    }
173}
174
175impl<A> Copy for DateTime<A>
176where
177    A: AsCalendar + Copy,
178    <<A as AsCalendar>::Calendar as Calendar>::DateInner: Copy,
179{
180}
181
182#[cfg(test)]
183mod tests {
184    use super::*;
185
186    #[test]
187    fn test_ord() {
188        let dates_in_order = [
189            DateTime::try_new_iso_datetime(0, 1, 1, 0, 0, 0).unwrap(),
190            DateTime::try_new_iso_datetime(0, 1, 1, 0, 0, 1).unwrap(),
191            DateTime::try_new_iso_datetime(0, 1, 1, 0, 1, 0).unwrap(),
192            DateTime::try_new_iso_datetime(0, 1, 1, 1, 0, 0).unwrap(),
193            DateTime::try_new_iso_datetime(0, 1, 2, 0, 0, 0).unwrap(),
194            DateTime::try_new_iso_datetime(0, 2, 1, 0, 0, 0).unwrap(),
195            DateTime::try_new_iso_datetime(1, 1, 1, 0, 0, 0).unwrap(),
196        ];
197        for (i, i_date) in dates_in_order.iter().enumerate() {
198            for (j, j_date) in dates_in_order.iter().enumerate() {
199                let result1 = i_date.cmp(j_date);
200                let result2 = j_date.cmp(i_date);
201                assert_eq!(result1.reverse(), result2);
202                assert_eq!(i.cmp(&j), i_date.cmp(j_date));
203            }
204        }
205    }
206}