1#![allow(clippy::field_reassign_with_default)]
3
4use alloc::vec::Vec;
5use core::{
6 ops::{Add, AddAssign, Div, Mul, Rem, Shl, Shr, Sub, SubAssign},
7 time::Duration,
8};
9#[cfg(any(feature = "std", test))]
10use std::{
11 fmt::{self, Display, Formatter},
12 str::FromStr,
13 time::SystemTime,
14};
15
16#[cfg(feature = "datasize")]
17use datasize::DataSize;
18#[cfg(any(feature = "std", test))]
19use humantime::{DurationError, TimestampError};
20#[cfg(any(feature = "testing", test))]
21use rand::Rng;
22#[cfg(feature = "json-schema")]
23use schemars::JsonSchema;
24#[cfg(any(feature = "std", test))]
25use serde::{de::Error as SerdeError, Deserialize, Deserializer, Serialize, Serializer};
26
27use crate::bytesrepr::{self, FromBytes, ToBytes};
28
29#[cfg(any(feature = "testing", test))]
30use crate::testing::TestRng;
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
34#[cfg_attr(feature = "datasize", derive(DataSize))]
35#[cfg_attr(
36 feature = "json-schema",
37 derive(JsonSchema),
38 schemars(with = "String", description = "Timestamp formatted as per RFC 3339")
39)]
40pub struct Timestamp(u64);
41
42impl Timestamp {
43 pub const MAX: Timestamp = Timestamp(u64::MAX);
45
46 #[cfg(any(feature = "std", test))]
47 pub fn now() -> Self {
49 let millis = SystemTime::UNIX_EPOCH.elapsed().unwrap().as_millis() as u64;
50 Timestamp(millis)
51 }
52
53 #[cfg(any(feature = "std", test))]
54 pub fn elapsed(&self) -> TimeDiff {
56 TimeDiff(Timestamp::now().0.saturating_sub(self.0))
57 }
58
59 pub fn zero() -> Self {
61 Timestamp(0)
62 }
63
64 pub fn millis(&self) -> u64 {
66 self.0
67 }
68
69 pub fn saturating_diff(self, other: Timestamp) -> TimeDiff {
71 TimeDiff(self.0.saturating_sub(other.0))
72 }
73
74 #[must_use]
76 pub fn saturating_sub(self, other: TimeDiff) -> Timestamp {
77 Timestamp(self.0.saturating_sub(other.0))
78 }
79
80 #[must_use]
83 pub fn saturating_add(self, other: TimeDiff) -> Timestamp {
84 Timestamp(self.0.saturating_add(other.0))
85 }
86
87 pub fn trailing_zeros(&self) -> u8 {
89 self.0.trailing_zeros() as u8
90 }
91}
92
93#[cfg(any(feature = "testing", test))]
94impl Timestamp {
95 pub fn random(rng: &mut TestRng) -> Self {
97 Timestamp(1_596_763_000_000 + rng.gen_range(200_000..1_000_000))
98 }
99
100 pub fn checked_sub(self, other: TimeDiff) -> Option<Timestamp> {
102 self.0.checked_sub(other.0).map(Timestamp)
103 }
104}
105
106#[cfg(any(feature = "std", test))]
107impl Display for Timestamp {
108 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
109 match SystemTime::UNIX_EPOCH.checked_add(Duration::from_millis(self.0)) {
110 Some(system_time) => write!(f, "{}", humantime::format_rfc3339_millis(system_time))
111 .or_else(|e| write!(f, "Invalid timestamp: {}: {}", e, self.0)),
112 None => write!(f, "invalid Timestamp: {} ms after the Unix epoch", self.0),
113 }
114 }
115}
116
117#[cfg(any(feature = "std", test))]
118impl FromStr for Timestamp {
119 type Err = TimestampError;
120
121 fn from_str(value: &str) -> Result<Self, Self::Err> {
122 let system_time = humantime::parse_rfc3339_weak(value)?;
123 let inner = system_time
124 .duration_since(SystemTime::UNIX_EPOCH)
125 .map_err(|_| TimestampError::OutOfRange)?
126 .as_millis() as u64;
127 Ok(Timestamp(inner))
128 }
129}
130
131impl Add<TimeDiff> for Timestamp {
132 type Output = Timestamp;
133
134 fn add(self, diff: TimeDiff) -> Timestamp {
135 Timestamp(self.0 + diff.0)
136 }
137}
138
139impl AddAssign<TimeDiff> for Timestamp {
140 fn add_assign(&mut self, rhs: TimeDiff) {
141 self.0 += rhs.0;
142 }
143}
144
145#[cfg(any(feature = "testing", test))]
146impl std::ops::Sub<TimeDiff> for Timestamp {
147 type Output = Timestamp;
148
149 fn sub(self, diff: TimeDiff) -> Timestamp {
150 Timestamp(self.0 - diff.0)
151 }
152}
153
154impl Rem<TimeDiff> for Timestamp {
155 type Output = TimeDiff;
156
157 fn rem(self, diff: TimeDiff) -> TimeDiff {
158 TimeDiff(self.0 % diff.0)
159 }
160}
161
162impl<T> Shl<T> for Timestamp
163where
164 u64: Shl<T, Output = u64>,
165{
166 type Output = Timestamp;
167
168 fn shl(self, rhs: T) -> Timestamp {
169 Timestamp(self.0 << rhs)
170 }
171}
172
173impl<T> Shr<T> for Timestamp
174where
175 u64: Shr<T, Output = u64>,
176{
177 type Output = Timestamp;
178
179 fn shr(self, rhs: T) -> Timestamp {
180 Timestamp(self.0 >> rhs)
181 }
182}
183
184#[cfg(any(feature = "std", test))]
185impl Serialize for Timestamp {
186 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
187 if serializer.is_human_readable() {
188 self.to_string().serialize(serializer)
189 } else {
190 self.0.serialize(serializer)
191 }
192 }
193}
194
195#[cfg(any(feature = "std", test))]
196impl<'de> Deserialize<'de> for Timestamp {
197 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
198 if deserializer.is_human_readable() {
199 let value_as_string = String::deserialize(deserializer)?;
200 Timestamp::from_str(&value_as_string).map_err(SerdeError::custom)
201 } else {
202 let inner = u64::deserialize(deserializer)?;
203 Ok(Timestamp(inner))
204 }
205 }
206}
207
208impl ToBytes for Timestamp {
209 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
210 self.0.to_bytes()
211 }
212
213 fn serialized_length(&self) -> usize {
214 self.0.serialized_length()
215 }
216}
217
218impl FromBytes for Timestamp {
219 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
220 u64::from_bytes(bytes).map(|(inner, remainder)| (Timestamp(inner), remainder))
221 }
222}
223
224impl From<u64> for Timestamp {
225 fn from(milliseconds_since_epoch: u64) -> Timestamp {
226 Timestamp(milliseconds_since_epoch)
227 }
228}
229
230#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
232#[cfg_attr(feature = "datasize", derive(DataSize))]
233#[cfg_attr(
234 feature = "json-schema",
235 derive(JsonSchema),
236 schemars(with = "String", description = "Human-readable duration.")
237)]
238pub struct TimeDiff(u64);
239
240#[cfg(any(feature = "std", test))]
241impl Display for TimeDiff {
242 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
243 write!(f, "{}", humantime::format_duration(Duration::from(*self)))
244 }
245}
246
247#[cfg(any(feature = "std", test))]
248impl FromStr for TimeDiff {
249 type Err = DurationError;
250
251 fn from_str(value: &str) -> Result<Self, Self::Err> {
252 let inner = humantime::parse_duration(value)?.as_millis() as u64;
253 Ok(TimeDiff(inner))
254 }
255}
256
257impl TimeDiff {
258 pub fn millis(&self) -> u64 {
260 self.0
261 }
262
263 pub const fn from_seconds(seconds: u32) -> Self {
265 TimeDiff(seconds as u64 * 1_000)
266 }
267
268 pub const fn from_millis(millis: u64) -> Self {
270 TimeDiff(millis)
271 }
272
273 #[must_use]
275 pub fn saturating_add(self, rhs: u64) -> Self {
276 TimeDiff(self.0.saturating_add(rhs))
277 }
278
279 #[must_use]
281 pub fn saturating_mul(self, rhs: u64) -> Self {
282 TimeDiff(self.0.saturating_mul(rhs))
283 }
284
285 #[must_use]
287 pub fn checked_mul(self, rhs: u64) -> Option<Self> {
288 Some(TimeDiff(self.0.checked_mul(rhs)?))
289 }
290}
291
292impl Add<TimeDiff> for TimeDiff {
293 type Output = TimeDiff;
294
295 fn add(self, rhs: TimeDiff) -> TimeDiff {
296 TimeDiff(self.0 + rhs.0)
297 }
298}
299
300impl AddAssign<TimeDiff> for TimeDiff {
301 fn add_assign(&mut self, rhs: TimeDiff) {
302 self.0 += rhs.0;
303 }
304}
305
306impl Sub<TimeDiff> for TimeDiff {
307 type Output = TimeDiff;
308
309 fn sub(self, rhs: TimeDiff) -> TimeDiff {
310 TimeDiff(self.0 - rhs.0)
311 }
312}
313
314impl SubAssign<TimeDiff> for TimeDiff {
315 fn sub_assign(&mut self, rhs: TimeDiff) {
316 self.0 -= rhs.0;
317 }
318}
319
320impl Mul<u64> for TimeDiff {
321 type Output = TimeDiff;
322
323 fn mul(self, rhs: u64) -> TimeDiff {
324 TimeDiff(self.0 * rhs)
325 }
326}
327
328impl Div<u64> for TimeDiff {
329 type Output = TimeDiff;
330
331 fn div(self, rhs: u64) -> TimeDiff {
332 TimeDiff(self.0 / rhs)
333 }
334}
335
336impl Div<TimeDiff> for TimeDiff {
337 type Output = u64;
338
339 fn div(self, rhs: TimeDiff) -> u64 {
340 self.0 / rhs.0
341 }
342}
343
344impl From<TimeDiff> for Duration {
345 fn from(diff: TimeDiff) -> Duration {
346 Duration::from_millis(diff.0)
347 }
348}
349
350#[cfg(any(feature = "std", test))]
351impl Serialize for TimeDiff {
352 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
353 if serializer.is_human_readable() {
354 self.to_string().serialize(serializer)
355 } else {
356 self.0.serialize(serializer)
357 }
358 }
359}
360
361#[cfg(any(feature = "std", test))]
362impl<'de> Deserialize<'de> for TimeDiff {
363 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
364 if deserializer.is_human_readable() {
365 let value_as_string = String::deserialize(deserializer)?;
366 TimeDiff::from_str(&value_as_string).map_err(SerdeError::custom)
367 } else {
368 let inner = u64::deserialize(deserializer)?;
369 Ok(TimeDiff(inner))
370 }
371 }
372}
373
374impl ToBytes for TimeDiff {
375 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
376 self.0.to_bytes()
377 }
378
379 fn serialized_length(&self) -> usize {
380 self.0.serialized_length()
381 }
382}
383
384impl FromBytes for TimeDiff {
385 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
386 u64::from_bytes(bytes).map(|(inner, remainder)| (TimeDiff(inner), remainder))
387 }
388}
389
390impl From<Duration> for TimeDiff {
391 fn from(duration: Duration) -> TimeDiff {
392 TimeDiff(duration.as_millis() as u64)
393 }
394}
395
396#[cfg(any(feature = "std", test))]
399pub mod serde_option_time_diff {
400 use super::*;
401
402 pub fn serialize<S: Serializer>(
404 maybe_td: &Option<TimeDiff>,
405 serializer: S,
406 ) -> Result<S::Ok, S::Error> {
407 maybe_td
408 .unwrap_or_else(|| TimeDiff::from_millis(0))
409 .serialize(serializer)
410 }
411
412 pub fn deserialize<'de, D: Deserializer<'de>>(
414 deserializer: D,
415 ) -> Result<Option<TimeDiff>, D::Error> {
416 let td = TimeDiff::deserialize(deserializer)?;
417 if td.0 == 0 {
418 Ok(None)
419 } else {
420 Ok(Some(td))
421 }
422 }
423}
424
425#[cfg(test)]
426mod tests {
427 use super::*;
428
429 #[test]
430 fn timestamp_serialization_roundtrip() {
431 let timestamp = Timestamp::now();
432
433 let timestamp_as_string = timestamp.to_string();
434 assert_eq!(
435 timestamp,
436 Timestamp::from_str(×tamp_as_string).unwrap()
437 );
438
439 let serialized_json = serde_json::to_string(×tamp).unwrap();
440 assert_eq!(timestamp, serde_json::from_str(&serialized_json).unwrap());
441
442 let serialized_bincode = bincode::serialize(×tamp).unwrap();
443 assert_eq!(
444 timestamp,
445 bincode::deserialize(&serialized_bincode).unwrap()
446 );
447
448 bytesrepr::test_serialization_roundtrip(×tamp);
449 }
450
451 #[test]
452 fn timediff_serialization_roundtrip() {
453 let mut rng = TestRng::new();
454 let timediff = TimeDiff(rng.gen());
455
456 let timediff_as_string = timediff.to_string();
457 assert_eq!(timediff, TimeDiff::from_str(&timediff_as_string).unwrap());
458
459 let serialized_json = serde_json::to_string(&timediff).unwrap();
460 assert_eq!(timediff, serde_json::from_str(&serialized_json).unwrap());
461
462 let serialized_bincode = bincode::serialize(&timediff).unwrap();
463 assert_eq!(timediff, bincode::deserialize(&serialized_bincode).unwrap());
464
465 bytesrepr::test_serialization_roundtrip(&timediff);
466 }
467
468 #[test]
469 fn does_not_crash_for_big_timestamp_value() {
470 assert!(Timestamp::MAX.to_string().starts_with("Invalid timestamp:"));
471 }
472}