1use core::fmt;
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4
5use crate::Uint64;
6
7#[derive(
26 Serialize, Deserialize, Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema,
27)]
28pub struct Timestamp(Uint64);
29
30impl Timestamp {
31 pub const fn from_nanos(nanos_since_epoch: u64) -> Self {
33 Timestamp(Uint64::new(nanos_since_epoch))
34 }
35
36 pub const fn from_seconds(seconds_since_epoch: u64) -> Self {
38 Timestamp(Uint64::new(seconds_since_epoch * 1_000_000_000))
39 }
40
41 #[must_use = "this returns the result of the operation, without modifying the original"]
46 #[inline]
47 pub const fn plus_days(&self, addition: u64) -> Timestamp {
48 self.plus_hours(addition * 24)
49 }
50
51 #[must_use = "this returns the result of the operation, without modifying the original"]
56 #[inline]
57 pub const fn plus_hours(&self, addition: u64) -> Timestamp {
58 self.plus_minutes(addition * 60)
59 }
60
61 #[must_use = "this returns the result of the operation, without modifying the original"]
66 #[inline]
67 pub const fn plus_minutes(&self, addition: u64) -> Timestamp {
68 self.plus_seconds(addition * 60)
69 }
70
71 #[must_use = "this returns the result of the operation, without modifying the original"]
76 #[inline]
77 pub const fn plus_seconds(&self, addition: u64) -> Timestamp {
78 self.plus_nanos(addition * 1_000_000_000)
79 }
80
81 #[must_use = "this returns the result of the operation, without modifying the original"]
86 pub const fn plus_nanos(&self, addition: u64) -> Timestamp {
88 let nanos = self.0.strict_add(Uint64::new(addition));
89 Timestamp(nanos)
90 }
91
92 #[must_use = "this returns the result of the operation, without modifying the original"]
97 #[inline]
98 pub const fn minus_days(&self, subtrahend: u64) -> Timestamp {
99 self.minus_hours(subtrahend * 24)
100 }
101
102 #[must_use = "this returns the result of the operation, without modifying the original"]
107 #[inline]
108 pub const fn minus_hours(&self, subtrahend: u64) -> Timestamp {
109 self.minus_minutes(subtrahend * 60)
110 }
111
112 #[must_use = "this returns the result of the operation, without modifying the original"]
117 #[inline]
118 pub const fn minus_minutes(&self, subtrahend: u64) -> Timestamp {
119 self.minus_seconds(subtrahend * 60)
120 }
121
122 #[must_use = "this returns the result of the operation, without modifying the original"]
127 #[inline]
128 pub const fn minus_seconds(&self, subtrahend: u64) -> Timestamp {
129 self.minus_nanos(subtrahend * 1_000_000_000)
130 }
131
132 #[must_use = "this returns the result of the operation, without modifying the original"]
137 pub const fn minus_nanos(&self, subtrahend: u64) -> Timestamp {
139 Timestamp(self.0.strict_sub(Uint64::new(subtrahend)))
140 }
141
142 #[inline]
144 pub fn nanos(&self) -> u64 {
145 self.0.u64()
146 }
147
148 #[inline]
150 pub fn seconds(&self) -> u64 {
151 self.0.u64() / 1_000_000_000
152 }
153
154 #[inline]
157 pub fn subsec_nanos(&self) -> u64 {
158 self.0.u64() % 1_000_000_000
159 }
160}
161
162impl fmt::Display for Timestamp {
163 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
164 let whole = self.seconds();
165 let fractional = self.subsec_nanos();
166 write!(f, "{whole}.{fractional:09}")
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173
174 #[test]
175 fn timestamp_from_nanos() {
176 let t = Timestamp::from_nanos(123);
177 assert_eq!(t.0.u64(), 123);
178 let t = Timestamp::from_nanos(0);
179 assert_eq!(t.0.u64(), 0);
180 }
181
182 #[test]
183 fn timestamp_from_seconds() {
184 let t = Timestamp::from_seconds(123);
185 assert_eq!(t.0.u64(), 123_000_000_000);
186 let t = Timestamp::from_seconds(0);
187 assert_eq!(t.0.u64(), 0);
188 }
189
190 #[test]
191 fn timestamp_plus_seconds() {
192 let sum = Timestamp::from_nanos(123).plus_seconds(42);
193 assert_eq!(sum.0.u64(), 42_000_000_123);
194 let sum = Timestamp::from_nanos(123).plus_seconds(0);
195 assert_eq!(sum.0.u64(), 123);
196 }
197
198 #[test]
199 fn timestamp_plus_nanos() {
200 let sum = Timestamp::from_nanos(123).plus_nanos(3);
201 assert_eq!(sum.0.u64(), 126);
202 let sum = Timestamp::from_nanos(123).plus_nanos(0);
203 assert_eq!(sum.0.u64(), 123);
204 }
205
206 #[test]
207 #[should_panic(expected = "attempt to add with overflow")]
208 fn timestamp_plus_nanos_panics_on_overflow() {
209 let max = Timestamp::from_nanos(u64::MAX);
210 let _earlier = max.plus_nanos(20);
211 }
212
213 #[test]
214 fn timestamp_minus_seconds() {
215 let earlier = Timestamp::from_seconds(123).minus_seconds(0);
216 assert_eq!(earlier.0.u64(), 123_000_000_000);
217 let earlier = Timestamp::from_seconds(123).minus_seconds(3);
218 assert_eq!(earlier.0.u64(), 120_000_000_000);
219 let earlier = Timestamp::from_seconds(123).minus_seconds(123);
220 assert_eq!(earlier.0.u64(), 0);
221 }
222
223 #[test]
224 #[should_panic(expected = "attempt to subtract with overflow")]
225 fn timestamp_minus_seconds_panics_on_overflow() {
226 let _earlier = Timestamp::from_seconds(100).minus_seconds(101);
227 }
228
229 #[test]
230 fn timestamp_minus_nanos() {
231 let earlier = Timestamp::from_seconds(123).minus_nanos(0);
232 assert_eq!(earlier.0.u64(), 123_000_000_000);
233 let earlier = Timestamp::from_seconds(123).minus_nanos(3);
234 assert_eq!(earlier.0.u64(), 122_999_999_997);
235 let earlier = Timestamp::from_seconds(123).minus_nanos(123_000_000_000);
236 assert_eq!(earlier.0.u64(), 0);
237 }
238
239 #[test]
240 #[should_panic(expected = "attempt to subtract with overflow")]
241 fn timestamp_minus_nanos_panics_on_overflow() {
242 let _earlier = Timestamp::from_nanos(100).minus_nanos(101);
243 }
244
245 #[test]
246 fn timestamp_plus_days() {
247 let ts = Timestamp::from_seconds(123).plus_days(0);
248 assert_eq!(ts.0.u64(), 123_000_000_000);
249 let ts = Timestamp::from_seconds(123).plus_days(10);
250 assert_eq!(ts.0.u64(), 864_123_000_000_000);
251 }
252
253 #[test]
254 fn timestamp_minus_days() {
255 let ts = Timestamp::from_seconds(123).minus_days(0);
256 assert_eq!(ts.0.u64(), 123_000_000_000);
257 let ts = Timestamp::from_seconds(2 * 86400 + 123).minus_days(1);
258 assert_eq!(ts.0.u64(), 86_523_000_000_000);
259 let ts = Timestamp::from_seconds(86400).minus_days(1);
260 assert_eq!(ts.0.u64(), 0);
261 }
262
263 #[test]
264 fn timestamp_plus_hours() {
265 let ts = Timestamp::from_seconds(123).plus_hours(0);
266 assert_eq!(ts.0.u64(), 123_000_000_000);
267 let ts = Timestamp::from_seconds(123).plus_hours(2);
268 assert_eq!(ts.0.u64(), 123_000_000_000 + 60 * 60 * 2 * 1_000_000_000);
269 }
270
271 #[test]
272 fn timestamp_minus_hours() {
273 let ts = Timestamp::from_seconds(2 * 60 * 60).minus_hours(0);
274 assert_eq!(ts.0.u64(), 2 * 60 * 60 * 1_000_000_000);
275 let ts = Timestamp::from_seconds(2 * 60 * 60 + 123).minus_hours(1);
276 assert_eq!(ts.0.u64(), 60 * 60 * 1_000_000_000 + 123_000_000_000);
277 }
278
279 #[test]
280 fn timestamp_plus_minutes() {
281 let ts = Timestamp::from_seconds(123).plus_minutes(0);
282 assert_eq!(ts.0.u64(), 123_000_000_000);
283 let ts = Timestamp::from_seconds(123).plus_minutes(2);
284 assert_eq!(ts.0.u64(), 123_000_000_000 + 60 * 2 * 1_000_000_000);
285 }
286
287 #[test]
288 fn timestamp_minus_minutes() {
289 let ts = Timestamp::from_seconds(5 * 60).minus_minutes(0);
290 assert_eq!(ts.0.u64(), 5 * 60 * 1_000_000_000);
291 let ts = Timestamp::from_seconds(5 * 60 + 123).minus_minutes(1);
292 assert_eq!(ts.0.u64(), 4 * 60 * 1_000_000_000 + 123_000_000_000);
293 }
294
295 #[test]
296 fn timestamp_nanos() {
297 let sum = Timestamp::from_nanos(123);
298 assert_eq!(sum.nanos(), 123);
299 let sum = Timestamp::from_nanos(0);
300 assert_eq!(sum.nanos(), 0);
301 let sum = Timestamp::from_nanos(987654321000);
302 assert_eq!(sum.nanos(), 987654321000);
303 }
304
305 #[test]
306 fn timestamp_seconds() {
307 let sum = Timestamp::from_nanos(987654321000);
308 assert_eq!(sum.seconds(), 987);
309 let sum = Timestamp::from_seconds(1234567).plus_nanos(8765436);
310 assert_eq!(sum.seconds(), 1234567);
311 }
312
313 #[test]
314 fn timestamp_subsec_nanos() {
315 let sum = Timestamp::from_nanos(987654321000);
316 assert_eq!(sum.subsec_nanos(), 654321000);
317 let sum = Timestamp::from_seconds(1234567).plus_nanos(8765436);
318 assert_eq!(sum.subsec_nanos(), 8765436);
319 }
320
321 #[test]
322 fn timestamp_implements_display() {
323 let embedded = format!("Time: {}", Timestamp::from_nanos(0));
324 assert_eq!(embedded, "Time: 0.000000000");
325 let embedded = format!("Time: {}", Timestamp::from_nanos(1));
326 assert_eq!(embedded, "Time: 0.000000001");
327 let embedded = format!("Time: {}", Timestamp::from_nanos(10));
328 assert_eq!(embedded, "Time: 0.000000010");
329 let embedded = format!("Time: {}", Timestamp::from_nanos(100));
330 assert_eq!(embedded, "Time: 0.000000100");
331 let embedded = format!("Time: {}", Timestamp::from_nanos(1000));
332 assert_eq!(embedded, "Time: 0.000001000");
333 let embedded = format!("Time: {}", Timestamp::from_nanos(10000));
334 assert_eq!(embedded, "Time: 0.000010000");
335 let embedded = format!("Time: {}", Timestamp::from_nanos(100000));
336 assert_eq!(embedded, "Time: 0.000100000");
337 let embedded = format!("Time: {}", Timestamp::from_nanos(1000000));
338 assert_eq!(embedded, "Time: 0.001000000");
339 let embedded = format!("Time: {}", Timestamp::from_nanos(1000000));
340 assert_eq!(embedded, "Time: 0.001000000");
341 let embedded = format!("Time: {}", Timestamp::from_nanos(10000000));
342 assert_eq!(embedded, "Time: 0.010000000");
343 let embedded = format!("Time: {}", Timestamp::from_nanos(100000000));
344 assert_eq!(embedded, "Time: 0.100000000");
345 let embedded = format!("Time: {}", Timestamp::from_nanos(1000000000));
346 assert_eq!(embedded, "Time: 1.000000000");
347 let embedded = format!("Time: {}", Timestamp::from_nanos(10000000000));
348 assert_eq!(embedded, "Time: 10.000000000");
349 let embedded = format!("Time: {}", Timestamp::from_nanos(100000000000));
350 assert_eq!(embedded, "Time: 100.000000000");
351 }
352}