Skip to main content

deep_time/dt/
ops.rs

1use crate::Dt;
2use core::cmp::Ordering;
3use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
4
5impl Add<Dt> for Dt {
6    type Output = Self;
7
8    #[inline]
9    fn add(self, rhs: Dt) -> Self {
10        self.add(rhs)
11    }
12}
13
14impl AddAssign<Dt> for Dt {
15    #[inline]
16    fn add_assign(&mut self, rhs: Dt) {
17        *self = self.add(rhs);
18    }
19}
20
21impl Sub<Dt> for Dt {
22    type Output = Self;
23
24    #[inline]
25    fn sub(self, rhs: Dt) -> Self {
26        self.sub(rhs)
27    }
28}
29
30impl SubAssign<Dt> for Dt {
31    #[inline]
32    fn sub_assign(&mut self, rhs: Dt) {
33        *self = self.sub(rhs);
34    }
35}
36
37impl Neg for Dt {
38    type Output = Self;
39
40    /// Negates this `Dt` (returns the additive inverse).
41    #[inline]
42    fn neg(self) -> Self {
43        self.neg()
44    }
45}
46
47impl Mul<i64> for Dt {
48    type Output = Self;
49
50    /// Multiplies this `Dt` by an integer scalar.
51    #[inline]
52    fn mul(self, rhs: i64) -> Self {
53        self.mul(rhs)
54    }
55}
56
57impl MulAssign<i64> for Dt {
58    /// Multiplies this `Dt` by an integer scalar in place.
59    #[inline]
60    fn mul_assign(&mut self, rhs: i64) {
61        *self = self.mul(rhs);
62    }
63}
64
65impl Div<i64> for Dt {
66    type Output = Self;
67
68    /// Divides this `Dt` by an integer scalar.
69    #[inline]
70    fn div(self, rhs: i64) -> Self {
71        self.div(rhs)
72    }
73}
74
75impl DivAssign<i64> for Dt {
76    /// Divides this `Dt` by an integer scalar in place.
77    #[inline]
78    fn div_assign(&mut self, rhs: i64) {
79        *self = self.div(rhs);
80    }
81}
82
83impl Dt {
84    /// Compares this `Dt` with another by converting both to the TAI timescale
85    /// (the library's canonical physical-time reference) and then comparing their
86    /// `(sec, attos)` pairs.
87    ///
88    /// This is a `const fn` so it can be used in const contexts and is allocation-free.
89    /// It provides the total order used by `<`, `>`, `<=`, `>=`, `cmp`, etc.
90    ///
91    /// Two `Dt`s that represent the exact same physical instant (after all
92    /// leap-second, relativistic, and scale conversions) compare as `Equal`, even if
93    /// they were constructed with different [`Scale`]s.
94    pub const fn cmp(&self, other: &Self) -> Ordering {
95        if self.sec < other.sec {
96            Ordering::Less
97        } else if self.sec > other.sec {
98            Ordering::Greater
99        } else if self.attos < other.attos {
100            Ordering::Less
101        } else if self.attos > other.attos {
102            Ordering::Greater
103        } else {
104            Ordering::Equal
105        }
106    }
107
108    /// Returns the smaller of two `Dt`s according to the total physical-time order
109    /// defined by [`Self::cmp`].
110    ///
111    /// Both instants are converted to TAI internally, so the result is the physically
112    /// earlier instant even when the two `Dt`s belong to different [`Scale`]s
113    /// (leap seconds, relativistic offsets, etc. are all taken into account).
114    ///
115    /// This is a `const fn` and can be used in const contexts.
116    #[inline]
117    pub const fn min(self, other: Self) -> Self {
118        match self.cmp(&other) {
119            Ordering::Greater => other,
120            _ => self,
121        }
122    }
123
124    /// Returns the larger of two `Dt`s according to the total physical-time order
125    /// defined by [`Self::cmp`].
126    ///
127    /// See [`Self::min`] for more details.
128    #[inline]
129    pub const fn max(self, other: Self) -> Self {
130        match self.cmp(&other) {
131            Ordering::Less => other,
132            _ => self,
133        }
134    }
135
136    #[inline]
137    pub const fn eq(&self, other: &Self) -> bool {
138        match Dt::cmp(self, other) {
139            Ordering::Equal => true,
140            _ => false,
141        }
142    }
143
144    /// Returns `true` if this `Dt` is less than the other.
145    ///
146    /// This is a `const fn` so it can be used in const contexts.
147    pub const fn lt(&self, other: &Self) -> bool {
148        matches!(self.cmp(other), Ordering::Less)
149    }
150
151    /// Returns `true` if this `Dt` is greater than the other.
152    ///
153    /// This is a `const fn` so it can be used in const contexts.
154    pub const fn gt(&self, other: &Self) -> bool {
155        matches!(self.cmp(other), Ordering::Greater)
156    }
157
158    /// Returns `true` if this `Dt` is less than or equal to the other.
159    ///
160    /// This is a `const fn` so it can be used in const contexts.
161    pub const fn le(&self, other: &Self) -> bool {
162        !matches!(self.cmp(other), Ordering::Greater)
163    }
164
165    /// Returns `true` if this `Dt` is greater than or equal to the other.
166    ///
167    /// This is a `const fn` so it can be used in const contexts.
168    pub const fn ge(&self, other: &Self) -> bool {
169        !matches!(self.cmp(other), Ordering::Less)
170    }
171}
172
173impl PartialEq for Dt {
174    #[inline]
175    fn eq(&self, other: &Self) -> bool {
176        Dt::eq(self, other)
177    }
178}
179
180impl Eq for Dt {}
181
182impl PartialOrd for Dt {
183    #[inline]
184    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
185        Some(Dt::cmp(self, other))
186    }
187}
188
189impl Ord for Dt {
190    #[inline]
191    fn cmp(&self, other: &Self) -> Ordering {
192        Dt::cmp(self, other)
193    }
194}
195
196impl core::hash::Hash for Dt {
197    /// Hashes the canonical TAI representation so that two `Dt`s that are
198    /// physically equal (after conversion) produce the same hash, regardless of
199    /// the original [`Scale`].
200    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
201        self.sec.hash(state);
202        self.attos.hash(state);
203    }
204}