1use std::sync::atomic;
2use std::time::{SystemTime, UNIX_EPOCH};
3
4#[derive(Debug, PartialEq, Eq, Clone, Copy, Ord, PartialOrd, Default)]
8pub struct LocalTime {
9 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 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 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 pub const fn from_secs(secs: u64) -> Self {
38 Self {
39 millis: secs as u128 * 1000,
40 }
41 }
42
43 pub fn as_secs(&self) -> u64 {
45 (self.millis / 1000).try_into().unwrap()
46 }
47
48 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 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 pub fn elapse(&mut self, duration: LocalDuration) {
74 self.millis += duration.as_millis()
75 }
76}
77
78impl 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
87impl 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
96impl 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
107impl 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#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
120pub struct LocalDuration(u128);
121
122impl LocalDuration {
123 pub const BLOCK_INTERVAL: LocalDuration = Self::from_mins(10);
125
126 pub const MAX: LocalDuration = LocalDuration(u128::MAX);
128
129 pub const fn from_secs(secs: u64) -> Self {
131 Self(secs as u128 * 1000)
132 }
133
134 pub const fn from_mins(mins: u64) -> Self {
136 Self::from_secs(mins * 60)
137 }
138
139 pub const fn from_millis(millis: u128) -> Self {
141 Self(millis)
142 }
143
144 pub const fn as_mins(&self) -> u64 {
146 self.as_secs() / 60
147 }
148
149 pub const fn as_secs(&self) -> u64 {
151 (self.0 / 1000) as u64
152 }
153
154 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}