1use std::{
2 fmt::Display,
3 ops::{Add, Sub},
4 time::{Duration, SystemTime, UNIX_EPOCH},
5};
6
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
10pub enum TimestampError {
11 #[error(r#"invalid hex string "{0}": can't convert to Timestamp"#)]
12 InvalidHexString(String),
13}
14
15#[derive(
16 Clone,
17 Copy,
18 Debug,
19 PartialEq,
20 Eq,
21 PartialOrd,
22 Ord,
23 Serialize,
24 Deserialize,
25 std::hash::Hash,
26 Default,
27)]
28#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
29pub struct Timestamp(u128);
30
31impl Timestamp {
32 pub const MAX: Timestamp = Timestamp(u128::MAX);
33
34 pub fn zero() -> Self {
35 Timestamp(0)
36 }
37
38 pub fn as_micros(&self) -> u128 {
39 self.0
40 }
41
42 pub fn as_seconds(&self) -> u128 {
43 self.0 / 1_000_000
44 }
45
46 pub fn from_micros(micros: u128) -> Self {
47 Timestamp(micros)
48 }
49
50 pub fn from_seconds(seconds: u64) -> Self {
51 Timestamp(u128::from(seconds) * 1_000_000)
52 }
53
54 pub fn now() -> Self {
55 SystemClock.now()
56 }
57
58 pub fn from_hex_seconds_str(s: &str) -> Result<Self, TimestampError> {
59 match s {
60 "earliest" => Ok(Self::zero()),
61 "latest" | "finalized" => Ok(Self::now()),
62 "pending" => {
64 tracing::warn!(
65 "Using 'pending' as a timestamp isn't properly implemented. Using current time."
66 );
67 Ok(Self::now())
68 }
69 s => Ok(Self::from_seconds(
70 u64::from_str_radix(s.strip_prefix("0x").unwrap_or(s), 16)
71 .map_err(|_| TimestampError::InvalidHexString(s.to_string()))?,
72 )),
73 }
74 }
75}
76
77impl Display for Timestamp {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 write!(f, "{}", self.0)
80 }
81}
82
83impl From<SystemTime> for Timestamp {
84 fn from(value: SystemTime) -> Self {
85 Timestamp::from_micros(
86 value
87 .duration_since(UNIX_EPOCH)
88 .expect("Time went backwards")
89 .as_micros(),
90 )
91 }
92}
93
94impl From<Timestamp> for SystemTime {
95 fn from(value: Timestamp) -> Self {
96 UNIX_EPOCH + Duration::from_micros(value.as_micros() as u64)
97 }
98}
99
100impl Sub<Duration> for Timestamp {
101 type Output = Timestamp;
102
103 fn sub(self, rhs: Duration) -> Self::Output {
104 Timestamp(self.0 - rhs.as_micros())
105 }
106}
107
108impl Sub<Timestamp> for Timestamp {
109 type Output = Timestamp;
110
111 fn sub(self, rhs: Timestamp) -> Self::Output {
112 Timestamp::from_micros(self.0 - rhs.0)
113 }
114}
115
116impl Add<Duration> for Timestamp {
117 type Output = Timestamp;
118
119 fn add(self, rhs: Duration) -> Self::Output {
120 Timestamp(self.0 + rhs.as_micros())
121 }
122}
123pub trait Clock {
124 fn now(&self) -> Timestamp;
125}
126
127#[derive(Clone)]
128pub struct SystemClock;
129
130impl Clock for SystemClock {
131 fn now(&self) -> Timestamp {
132 SystemTime::now().into()
133 }
134}
135
136pub struct MockClock {
137 time: Timestamp,
138}
139
140impl MockClock {
141 pub fn new(time: Timestamp) -> Self {
142 Self { time }
143 }
144
145 pub fn set_time(&mut self, time: Timestamp) {
146 self.time = time;
147 }
148
149 pub fn advance(&mut self, duration: Duration) {
150 self.time = self.time + duration;
151 }
152}
153
154impl Clock for MockClock {
155 fn now(&self) -> Timestamp {
156 self.time
157 }
158}