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