1extern crate alloc;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
20pub struct Time {
21 pub sec: i32,
23 pub nanosec: u32,
25}
26
27impl Time {
28 pub const ZERO: Self = Self { sec: 0, nanosec: 0 };
30
31 pub const INVALID: Self = Self {
34 sec: -1,
35 nanosec: 0xFFFF_FFFF,
36 };
37
38 pub const INFINITE: Self = Self {
40 sec: 0x7FFF_FFFF,
41 nanosec: 0xFFFF_FFFE,
42 };
43
44 #[must_use]
46 pub const fn is_zero(&self) -> bool {
47 self.sec == 0 && self.nanosec == 0
48 }
49
50 #[must_use]
52 pub const fn new(sec: i32, nanosec: u32) -> Self {
53 Self { sec, nanosec }
54 }
55
56 #[must_use]
58 pub const fn is_invalid(&self) -> bool {
59 self.sec == -1 && self.nanosec == 0xFFFF_FFFF
60 }
61
62 #[must_use]
64 pub const fn is_infinite(&self) -> bool {
65 self.sec == 0x7FFF_FFFF && self.nanosec == 0xFFFF_FFFE
66 }
67
68 #[must_use]
70 pub const fn seconds(&self) -> i32 {
71 self.sec
72 }
73
74 #[must_use]
76 pub const fn nanoseconds(&self) -> u32 {
77 self.nanosec
78 }
79
80 #[must_use]
82 pub fn add_duration(self, d: Duration) -> Self {
83 let total_ns = u64::from(self.nanosec) + u64::from(d.nanosec);
84 let extra_sec = (total_ns / 1_000_000_000) as i32;
85 let nanosec = (total_ns % 1_000_000_000) as u32;
86 Self {
87 sec: self.sec.saturating_add(d.sec).saturating_add(extra_sec),
88 nanosec,
89 }
90 }
91
92 #[must_use]
94 pub const fn from_millis(ms: i64) -> Self {
95 let sec = (ms / 1000) as i32;
96 let nanosec = ((ms % 1000) * 1_000_000) as u32;
97 Self { sec, nanosec }
98 }
99
100 #[must_use]
102 pub const fn as_millis(&self) -> i64 {
103 (self.sec as i64) * 1000 + (self.nanosec as i64) / 1_000_000
104 }
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
110pub struct Duration {
111 pub sec: i32,
113 pub nanosec: u32,
115}
116
117impl Duration {
118 pub const ZERO: Self = Self { sec: 0, nanosec: 0 };
120
121 pub const INFINITE: Self = Self {
123 sec: 0x7FFF_FFFF,
124 nanosec: 0xFFFF_FFFF,
125 };
126
127 #[must_use]
129 pub const fn new(sec: i32, nanosec: u32) -> Self {
130 Self { sec, nanosec }
131 }
132
133 #[must_use]
135 pub const fn is_infinite(&self) -> bool {
136 self.sec == 0x7FFF_FFFF && self.nanosec == 0xFFFF_FFFF
137 }
138
139 #[must_use]
141 pub const fn is_zero(&self) -> bool {
142 self.sec == 0 && self.nanosec == 0
143 }
144
145 #[must_use]
149 pub fn to_core(self) -> core::time::Duration {
150 if self.is_infinite() {
151 core::time::Duration::MAX
152 } else if self.sec < 0 {
153 core::time::Duration::ZERO
154 } else {
155 core::time::Duration::new(self.sec as u64, self.nanosec)
156 }
157 }
158
159 #[must_use]
162 pub fn from_core(d: core::time::Duration) -> Self {
163 let sec = i32::try_from(d.as_secs()).unwrap_or(i32::MAX);
164 Self {
165 sec,
166 nanosec: d.subsec_nanos(),
167 }
168 }
169
170 #[must_use]
172 pub const fn seconds(&self) -> i32 {
173 self.sec
174 }
175
176 #[must_use]
178 pub const fn nanoseconds(&self) -> u32 {
179 self.nanosec
180 }
181
182 #[must_use]
184 pub fn add_duration(self, d: Duration) -> Self {
185 let total_ns = u64::from(self.nanosec) + u64::from(d.nanosec);
186 let extra_sec = (total_ns / 1_000_000_000) as i32;
187 let nanosec = (total_ns % 1_000_000_000) as u32;
188 Self {
189 sec: self.sec.saturating_add(d.sec).saturating_add(extra_sec),
190 nanosec,
191 }
192 }
193
194 #[must_use]
196 pub const fn from_millis(ms: i64) -> Self {
197 let sec = (ms / 1000) as i32;
198 let nanosec = ((ms % 1000) * 1_000_000) as u32;
199 Self { sec, nanosec }
200 }
201
202 #[must_use]
204 pub const fn as_millis(&self) -> i64 {
205 (self.sec as i64) * 1000 + (self.nanosec as i64) / 1_000_000
206 }
207}
208
209#[must_use]
216#[cfg(feature = "std")]
217pub fn get_current_time() -> Time {
218 use std::time::{SystemTime, UNIX_EPOCH};
219 SystemTime::now()
220 .duration_since(UNIX_EPOCH)
221 .map(|d| Time {
222 sec: i32::try_from(d.as_secs()).unwrap_or(i32::MAX),
223 nanosec: d.subsec_nanos(),
224 })
225 .unwrap_or(Time::INVALID)
226}
227
228#[must_use]
230#[cfg(not(feature = "std"))]
231pub fn get_current_time() -> Time {
232 Time::INVALID
233}
234
235#[cfg(test)]
236#[allow(clippy::expect_used)]
237mod tests {
238 use super::*;
239
240 #[test]
241 fn time_zero_sentinel() {
242 assert!(Time::ZERO.is_zero());
243 assert!(!Time::ZERO.is_invalid());
244 assert!(!Time::ZERO.is_infinite());
245 assert_eq!(Time::ZERO, Time::default());
246 }
247
248 #[test]
249 fn time_invalid_sentinel() {
250 assert!(Time::INVALID.is_invalid());
251 assert!(!Time::INVALID.is_infinite());
252 let zero = Time::default();
253 assert!(!zero.is_invalid());
254 }
255
256 #[test]
257 fn time_infinite_sentinel() {
258 assert!(Time::INFINITE.is_infinite());
259 assert!(!Time::INFINITE.is_invalid());
260 }
261
262 #[test]
263 fn duration_zero_and_infinite_sentinels() {
264 assert!(Duration::ZERO.is_zero());
265 assert!(Duration::INFINITE.is_infinite());
266 assert!(!Duration::INFINITE.is_zero());
267 }
268
269 #[test]
270 fn duration_to_core_maps_infinite_to_max() {
271 assert_eq!(Duration::INFINITE.to_core(), core::time::Duration::MAX);
272 }
273
274 #[test]
275 fn duration_to_core_preserves_value() {
276 let d = Duration::new(5, 500_000_000);
277 let c = d.to_core();
278 assert_eq!(c.as_secs(), 5);
279 assert_eq!(c.subsec_nanos(), 500_000_000);
280 }
281
282 #[test]
283 fn duration_from_core_roundtrip() {
284 let c = core::time::Duration::new(42, 123_456_789);
285 let d = Duration::from_core(c);
286 assert_eq!(d.sec, 42);
287 assert_eq!(d.nanosec, 123_456_789);
288 assert_eq!(d.to_core(), c);
289 }
290
291 #[test]
292 fn duration_negative_sec_maps_to_zero() {
293 let d = Duration::new(-1, 0);
294 assert_eq!(d.to_core(), core::time::Duration::ZERO);
295 }
296
297 #[cfg(feature = "std")]
298 #[test]
299 fn get_current_time_is_recent() {
300 let t = get_current_time();
301 assert!(!t.is_invalid());
302 assert!(t.sec > 1_700_000_000);
304 }
305
306 #[test]
311 fn time_seconds_and_nanoseconds_accessors() {
312 let t = Time::new(7, 250_000_000);
314 assert_eq!(t.seconds(), 7);
315 assert_eq!(t.nanoseconds(), 250_000_000);
316 }
317
318 #[test]
319 fn duration_seconds_and_nanoseconds_accessors() {
320 let d = Duration::new(3, 100_000_000);
322 assert_eq!(d.seconds(), 3);
323 assert_eq!(d.nanoseconds(), 100_000_000);
324 }
325
326 #[test]
327 fn time_add_duration_carries_seconds() {
328 let t = Time::new(10, 800_000_000);
331 let inc = t.add_duration(Duration::new(2, 500_000_000));
332 assert_eq!(inc.seconds(), 13);
333 assert_eq!(inc.nanoseconds(), 300_000_000);
334 }
335
336 #[test]
337 fn time_from_and_as_millis_roundtrip() {
338 let t = Time::from_millis(12_345);
340 assert_eq!(t.seconds(), 12);
341 assert_eq!(t.nanoseconds(), 345_000_000);
342 assert_eq!(t.as_millis(), 12_345);
343 }
344
345 #[test]
346 fn duration_add_duration_carries_seconds() {
347 let d = Duration::new(5, 700_000_000);
349 let inc = d.add_duration(Duration::new(1, 600_000_000));
350 assert_eq!(inc.seconds(), 7);
351 assert_eq!(inc.nanoseconds(), 300_000_000);
352 }
353
354 #[test]
355 fn duration_from_and_as_millis_roundtrip() {
356 let d = Duration::from_millis(2_500);
358 assert_eq!(d.seconds(), 2);
359 assert_eq!(d.nanoseconds(), 500_000_000);
360 assert_eq!(d.as_millis(), 2_500);
361 }
362}