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#[must_use]
241pub fn time_to_he_timestamp(t: Time) -> zerodds_rtps::header_extension::HeTimestamp {
242 let fraction = ((u64::from(t.nanosec) << 32) / 1_000_000_000) as u32;
244 zerodds_rtps::header_extension::HeTimestamp {
245 seconds: t.sec,
246 fraction,
247 }
248}
249
250#[must_use]
253pub fn he_timestamp_to_time(ts: zerodds_rtps::header_extension::HeTimestamp) -> Time {
254 let nanosec = ((u64::from(ts.fraction) * 1_000_000_000) >> 32) as u32;
256 Time {
257 sec: ts.seconds,
258 nanosec: nanosec.min(999_999_999),
259 }
260}
261
262#[cfg(test)]
263#[allow(clippy::expect_used)]
264mod tests {
265 use super::*;
266
267 #[test]
268 fn time_zero_sentinel() {
269 assert!(Time::ZERO.is_zero());
270 assert!(!Time::ZERO.is_invalid());
271 assert!(!Time::ZERO.is_infinite());
272 assert_eq!(Time::ZERO, Time::default());
273 }
274
275 #[test]
276 fn time_invalid_sentinel() {
277 assert!(Time::INVALID.is_invalid());
278 assert!(!Time::INVALID.is_infinite());
279 let zero = Time::default();
280 assert!(!zero.is_invalid());
281 }
282
283 #[test]
284 fn time_infinite_sentinel() {
285 assert!(Time::INFINITE.is_infinite());
286 assert!(!Time::INFINITE.is_invalid());
287 }
288
289 #[test]
290 fn duration_zero_and_infinite_sentinels() {
291 assert!(Duration::ZERO.is_zero());
292 assert!(Duration::INFINITE.is_infinite());
293 assert!(!Duration::INFINITE.is_zero());
294 }
295
296 #[test]
297 fn duration_to_core_maps_infinite_to_max() {
298 assert_eq!(Duration::INFINITE.to_core(), core::time::Duration::MAX);
299 }
300
301 #[test]
302 fn duration_to_core_preserves_value() {
303 let d = Duration::new(5, 500_000_000);
304 let c = d.to_core();
305 assert_eq!(c.as_secs(), 5);
306 assert_eq!(c.subsec_nanos(), 500_000_000);
307 }
308
309 #[test]
310 fn duration_from_core_roundtrip() {
311 let c = core::time::Duration::new(42, 123_456_789);
312 let d = Duration::from_core(c);
313 assert_eq!(d.sec, 42);
314 assert_eq!(d.nanosec, 123_456_789);
315 assert_eq!(d.to_core(), c);
316 }
317
318 #[test]
319 fn duration_negative_sec_maps_to_zero() {
320 let d = Duration::new(-1, 0);
321 assert_eq!(d.to_core(), core::time::Duration::ZERO);
322 }
323
324 #[cfg(feature = "std")]
325 #[test]
326 fn get_current_time_is_recent() {
327 let t = get_current_time();
328 assert!(!t.is_invalid());
329 assert!(t.sec > 1_700_000_000);
331 }
332
333 #[test]
338 fn time_seconds_and_nanoseconds_accessors() {
339 let t = Time::new(7, 250_000_000);
341 assert_eq!(t.seconds(), 7);
342 assert_eq!(t.nanoseconds(), 250_000_000);
343 }
344
345 #[test]
346 fn duration_seconds_and_nanoseconds_accessors() {
347 let d = Duration::new(3, 100_000_000);
349 assert_eq!(d.seconds(), 3);
350 assert_eq!(d.nanoseconds(), 100_000_000);
351 }
352
353 #[test]
354 fn time_add_duration_carries_seconds() {
355 let t = Time::new(10, 800_000_000);
358 let inc = t.add_duration(Duration::new(2, 500_000_000));
359 assert_eq!(inc.seconds(), 13);
360 assert_eq!(inc.nanoseconds(), 300_000_000);
361 }
362
363 #[test]
364 fn time_from_and_as_millis_roundtrip() {
365 let t = Time::from_millis(12_345);
367 assert_eq!(t.seconds(), 12);
368 assert_eq!(t.nanoseconds(), 345_000_000);
369 assert_eq!(t.as_millis(), 12_345);
370 }
371
372 #[test]
373 fn duration_add_duration_carries_seconds() {
374 let d = Duration::new(5, 700_000_000);
376 let inc = d.add_duration(Duration::new(1, 600_000_000));
377 assert_eq!(inc.seconds(), 7);
378 assert_eq!(inc.nanoseconds(), 300_000_000);
379 }
380
381 #[test]
382 fn duration_from_and_as_millis_roundtrip() {
383 let d = Duration::from_millis(2_500);
385 assert_eq!(d.seconds(), 2);
386 assert_eq!(d.nanoseconds(), 500_000_000);
387 assert_eq!(d.as_millis(), 2_500);
388 }
389}