1use core::{
2 cmp::Ordering,
3 ops::{Add, Sub},
4 time::Duration as StdDuration,
5};
6
7use crate::{Duration, Timestamp};
8
9impl<'b> Sub<&'b Duration> for &Timestamp {
10 type Output = Timestamp;
11
12 fn sub(self, rhs: &'b Duration) -> Self::Output {
13 let duration = rhs.normalized();
14
15 let mut new = Timestamp {
16 seconds: self.seconds.saturating_sub(duration.seconds),
17 nanos: self.nanos - duration.nanos,
18 };
19
20 new.normalize();
21
22 new
23 }
24}
25
26impl Sub<StdDuration> for Timestamp {
27 type Output = Self;
28
29 fn sub(self, rhs: StdDuration) -> Self::Output {
30 let mut new = Self {
31 seconds: self
32 .seconds
33 .saturating_sub(rhs.as_secs().cast_signed()),
34 nanos: self
35 .nanos
36 .saturating_sub(rhs.subsec_nanos().cast_signed()),
37 };
38
39 new.normalize();
40
41 new
42 }
43}
44
45#[cfg(feature = "chrono")]
46impl Sub<chrono::TimeDelta> for Timestamp {
47 type Output = Self;
48
49 fn sub(self, rhs: chrono::TimeDelta) -> Self::Output {
50 let mut new = Self {
51 seconds: self.seconds.saturating_sub(rhs.num_seconds()),
52 nanos: self.nanos.saturating_sub(rhs.subsec_nanos()),
53 };
54
55 new.normalize();
56
57 new
58 }
59}
60
61impl Sub<Duration> for Timestamp {
62 type Output = Self;
63 #[inline]
64 fn sub(self, rhs: Duration) -> Self::Output {
65 <&Self as Sub<&Duration>>::sub(&self, &rhs)
66 }
67}
68
69impl<'b> Sub<&'b Duration> for Timestamp {
70 type Output = Self;
71 #[inline]
72 fn sub(self, rhs: &'b Duration) -> Self::Output {
73 <&Self as Sub<&Duration>>::sub(&self, rhs)
74 }
75}
76
77impl<'a> Sub<Duration> for &'a Timestamp {
78 type Output = Timestamp;
79 #[inline]
80 fn sub(self, rhs: Duration) -> Self::Output {
81 <&'a Timestamp as Sub<&Duration>>::sub(self, &rhs)
82 }
83}
84
85impl<'b> Add<&'b Duration> for &Timestamp {
86 type Output = Timestamp;
87
88 fn add(self, rhs: &'b Duration) -> Self::Output {
89 let duration = rhs.normalized();
90
91 let mut new = Timestamp {
92 seconds: self.seconds.saturating_add(duration.seconds),
93 nanos: self.nanos + duration.nanos,
94 };
95
96 new.normalize();
97
98 new
99 }
100}
101
102impl Add<StdDuration> for Timestamp {
103 type Output = Self;
104
105 fn add(self, rhs: StdDuration) -> Self::Output {
106 let mut new = Self {
107 seconds: self
108 .seconds
109 .saturating_add(rhs.as_secs().cast_signed()),
110 nanos: self
111 .nanos
112 .saturating_add(rhs.subsec_nanos().cast_signed()),
113 };
114
115 new.normalize();
116
117 new
118 }
119}
120
121#[cfg(feature = "chrono")]
122impl Add<chrono::TimeDelta> for Timestamp {
123 type Output = Self;
124
125 fn add(self, rhs: chrono::TimeDelta) -> Self::Output {
126 let mut new = Self {
127 seconds: self.seconds.saturating_add(rhs.num_seconds()),
128 nanos: self.nanos.saturating_add(rhs.subsec_nanos()),
129 };
130
131 new.normalize();
132
133 new
134 }
135}
136
137impl<'b> Add<&'b Duration> for Timestamp {
138 type Output = Self;
139 #[inline]
140 fn add(self, rhs: &'b Duration) -> Self::Output {
141 <&Self as Add<&Duration>>::add(&self, rhs)
142 }
143}
144
145impl Add<Duration> for &Timestamp {
146 type Output = Timestamp;
147 #[inline]
148 fn add(self, rhs: Duration) -> Self::Output {
149 <Self as Add<&Duration>>::add(self, &rhs)
150 }
151}
152
153impl Add<Duration> for Timestamp {
154 type Output = Self;
155
156 #[inline]
157 fn add(self, rhs: Duration) -> Self::Output {
158 &self + &rhs
159 }
160}
161
162impl PartialOrd for Timestamp {
163 #[inline]
164 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
165 Some(self.cmp(other))
166 }
167}
168
169impl Ord for Timestamp {
170 #[inline]
171 fn cmp(&self, other: &Self) -> Ordering {
172 (self.seconds, self.nanos).cmp(&(other.seconds, other.nanos))
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 macro_rules! get_duration {
181 (duration, $secs:literal, $nanos:literal) => {
182 Duration::new($secs, $nanos)
183 };
184
185 (std, $secs:literal, $nanos:literal) => {
186 StdDuration::new($secs, $nanos)
187 };
188
189 (chrono, $secs:literal, $nanos:literal) => {
190 TimeDelta::new($secs, $nanos).unwrap()
191 };
192 }
193
194 macro_rules! test_ops {
195 ($duration:ident) => {
196 #[test]
197 fn test_simple_addition() {
198 let t = Timestamp::new(100, 500);
199 let d = get_duration!($duration, 50, 100);
200 let res = t + d;
201 assert_eq!(res, Timestamp::new(150, 600));
202 }
203
204 #[test]
205 fn test_nano_overflow_addition() {
206 let t = Timestamp::new(100, 900_000_000);
207 let d = get_duration!($duration, 0, 200_000_000);
208 let res = t + d;
209 assert_eq!(res, Timestamp::new(101, 100_000_000));
211 }
212
213 #[test]
214 fn test_simple_subtraction() {
215 let t = Timestamp::new(100, 500);
216 let d = get_duration!($duration, 50, 100);
217 let res = t - d;
218 assert_eq!(res, Timestamp::new(50, 400));
219 }
220
221 #[test]
222 fn test_subtraction_crossing_zero() {
223 let t = Timestamp::new(100, 100);
224 let d = get_duration!($duration, 200, 0);
225 let res = t - d;
226 assert_eq!(res, Timestamp::new(-100, 100));
228 }
229
230 #[test]
231 fn test_subtraction_borrowing_nanos() {
232 let t = Timestamp::new(10, 100);
236 let d = get_duration!($duration, 0, 200);
237 let res = t - d;
238 assert_eq!(res, Timestamp::new(9, 999_999_900));
239 }
240
241 #[test]
242 fn test_add_saturation_max() {
243 let t = Timestamp::new(i64::MAX, 0);
245 let d = get_duration!($duration, 1, 0);
246 let res = t + d;
247
248 assert_eq!(res.seconds, i64::MAX);
249 }
250
251 #[test]
252 fn test_sub_saturation_min() {
253 let t = Timestamp::new(i64::MIN, 0);
255 let d = get_duration!($duration, 1, 0);
256 let res = t - d;
257
258 assert_eq!(res.seconds, i64::MIN);
259 }
260 };
261 }
262
263 macro_rules! test_saturation {
264 ($duration:ident) => {
265 #[test]
266 fn test_add_saturation_with_nanos() {
267 let t = Timestamp::new(i64::MAX, 0);
269 let d = get_duration!($duration, 0, 2_000_000_000);
271 let res = t + d;
272
273 assert_eq!(res.seconds, i64::MAX);
274 }
275 };
276 }
277
278 #[cfg(feature = "chrono")]
279 mod chrono_test {
280 use super::*;
281
282 use chrono::TimeDelta;
283
284 test_ops!(chrono);
285 }
286
287 test_ops!(duration);
288 test_saturation!(duration);
289
290 #[test]
291 fn test_sub_double_negative_saturation() {
292 let t = Timestamp::new(i64::MAX, 0);
294 let d = Duration::new(-1, 0);
295 let res = t - d;
296
297 assert_eq!(res.seconds, i64::MAX);
298 }
299
300 #[test]
301 fn test_add_negative_duration() {
302 let t = Timestamp::new(100, 0);
303 let d = Duration::new(-50, 0);
304 let res = t + d;
305 assert_eq!(res, Timestamp::new(50, 0));
306 }
307
308 mod std_duration {
309 use super::*;
310
311 test_ops!(std);
312 test_saturation!(std);
313 }
314}