nakamoto_net/
time.rs

1use std::sync::atomic;
2use std::time::{SystemTime, UNIX_EPOCH};
3
4/// Local time.
5///
6/// This clock is monotonic.
7#[derive(Debug, PartialEq, Eq, Clone, Copy, Ord, PartialOrd, Default)]
8pub struct LocalTime {
9    /// Milliseconds since Epoch.
10    millis: u128,
11}
12
13impl std::fmt::Display for LocalTime {
14    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15        write!(f, "{}", self.as_secs())
16    }
17}
18
19impl LocalTime {
20    /// Construct a local time from the current system time.
21    pub fn now() -> Self {
22        static LAST: atomic::AtomicU64 = atomic::AtomicU64::new(0);
23
24        let now = Self::from(SystemTime::now()).as_secs();
25        let last = LAST.load(atomic::Ordering::SeqCst);
26
27        // If the current time is in the past, return the last recorded time instead.
28        if now < last {
29            Self::from_secs(last)
30        } else {
31            LAST.store(now, atomic::Ordering::SeqCst);
32            LocalTime::from_secs(now)
33        }
34    }
35
36    /// Construct a local time from whole seconds since Epoch.
37    pub const fn from_secs(secs: u64) -> Self {
38        Self {
39            millis: secs as u128 * 1000,
40        }
41    }
42
43    /// Construct a local time to whole seconds since Epoch.
44    pub fn as_secs(&self) -> u64 {
45        (self.millis / 1000).try_into().unwrap()
46    }
47
48    /// Get the duration since the given time.
49    ///
50    /// # Panics
51    ///
52    /// This function will panic if `earlier` is later than `self`.
53    pub fn duration_since(&self, earlier: LocalTime) -> LocalDuration {
54        LocalDuration::from_millis(
55            self.millis
56                .checked_sub(earlier.millis)
57                .expect("supplied time is later than self"),
58        )
59    }
60
61    /// Get the difference between two times.
62    pub fn diff(&self, other: LocalTime) -> LocalDuration {
63        if self > &other {
64            self.duration_since(other)
65        } else {
66            other.duration_since(*self)
67        }
68    }
69
70    /// Elapse time.
71    ///
72    /// Adds the given duration to the time.
73    pub fn elapse(&mut self, duration: LocalDuration) {
74        self.millis += duration.as_millis()
75    }
76}
77
78/// Convert a `SystemTime` into a local time.
79impl From<SystemTime> for LocalTime {
80    fn from(system: SystemTime) -> Self {
81        let millis = system.duration_since(UNIX_EPOCH).unwrap().as_millis();
82
83        Self { millis }
84    }
85}
86
87/// Substract two local times. Yields a duration.
88impl std::ops::Sub<LocalTime> for LocalTime {
89    type Output = LocalDuration;
90
91    fn sub(self, other: LocalTime) -> LocalDuration {
92        LocalDuration(self.millis.saturating_sub(other.millis))
93    }
94}
95
96/// Substract a duration from a local time. Yields a local time.
97impl std::ops::Sub<LocalDuration> for LocalTime {
98    type Output = LocalTime;
99
100    fn sub(self, other: LocalDuration) -> LocalTime {
101        LocalTime {
102            millis: self.millis - other.0,
103        }
104    }
105}
106
107/// Add a duration to a local time. Yields a local time.
108impl std::ops::Add<LocalDuration> for LocalTime {
109    type Output = LocalTime;
110
111    fn add(self, other: LocalDuration) -> LocalTime {
112        LocalTime {
113            millis: self.millis + other.0,
114        }
115    }
116}
117
118/// Time duration as measured locally.
119#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
120pub struct LocalDuration(u128);
121
122impl LocalDuration {
123    /// The time interval between blocks. The "block time".
124    pub const BLOCK_INTERVAL: LocalDuration = Self::from_mins(10);
125
126    /// Maximum duration.
127    pub const MAX: LocalDuration = LocalDuration(u128::MAX);
128
129    /// Create a new duration from whole seconds.
130    pub const fn from_secs(secs: u64) -> Self {
131        Self(secs as u128 * 1000)
132    }
133
134    /// Create a new duration from whole minutes.
135    pub const fn from_mins(mins: u64) -> Self {
136        Self::from_secs(mins * 60)
137    }
138
139    /// Construct a new duration from milliseconds.
140    pub const fn from_millis(millis: u128) -> Self {
141        Self(millis)
142    }
143
144    /// Return the number of minutes in this duration.
145    pub const fn as_mins(&self) -> u64 {
146        self.as_secs() / 60
147    }
148
149    /// Return the number of seconds in this duration.
150    pub const fn as_secs(&self) -> u64 {
151        (self.0 / 1000) as u64
152    }
153
154    /// Return the number of milliseconds in this duration.
155    pub const fn as_millis(&self) -> u128 {
156        self.0
157    }
158}
159
160impl std::fmt::Display for LocalDuration {
161    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162        if self.as_millis() < 1000 {
163            write!(f, "{} millisecond(s)", self.as_millis())
164        } else if self.as_secs() < 60 {
165            let fraction = self.as_millis() % 1000;
166            if fraction > 0 {
167                write!(f, "{}.{} second(s)", self.as_secs(), fraction)
168            } else {
169                write!(f, "{} second(s)", self.as_secs())
170            }
171        } else if self.as_mins() < 60 {
172            let fraction = self.as_secs() % 60;
173            if fraction > 0 {
174                write!(
175                    f,
176                    "{:.2} minutes(s)",
177                    self.as_mins() as f64 + (fraction as f64 / 60.)
178                )
179            } else {
180                write!(f, "{} minutes(s)", self.as_mins())
181            }
182        } else {
183            let fraction = self.as_mins() % 60;
184            if fraction > 0 {
185                write!(f, "{:.2} hour(s)", self.as_mins() as f64 / 60.)
186            } else {
187                write!(f, "{} hour(s)", self.as_mins() / 60)
188            }
189        }
190    }
191}
192
193impl<'a> std::iter::Sum<&'a LocalDuration> for LocalDuration {
194    fn sum<I: Iterator<Item = &'a LocalDuration>>(iter: I) -> LocalDuration {
195        let mut total: u128 = 0;
196
197        for entry in iter {
198            total = total
199                .checked_add(entry.0)
200                .expect("iter::sum should not overflow");
201        }
202        Self(total)
203    }
204}
205
206impl std::ops::Add<LocalDuration> for LocalDuration {
207    type Output = LocalDuration;
208
209    fn add(self, other: LocalDuration) -> LocalDuration {
210        LocalDuration(self.0 + other.0)
211    }
212}
213
214impl std::ops::Div<u32> for LocalDuration {
215    type Output = LocalDuration;
216
217    fn div(self, other: u32) -> LocalDuration {
218        LocalDuration(self.0 / other as u128)
219    }
220}
221
222impl std::ops::Mul<u64> for LocalDuration {
223    type Output = LocalDuration;
224
225    fn mul(self, other: u64) -> LocalDuration {
226        LocalDuration(self.0 * other as u128)
227    }
228}
229
230impl From<LocalDuration> for std::time::Duration {
231    fn from(other: LocalDuration) -> Self {
232        std::time::Duration::from_millis(other.0 as u64)
233    }
234}