1use core::ops::{Add, Sub};
8use nros_serdes::{CdrReader, CdrWriter, DeserError, Deserialize, SerError, Serialize};
9
10const NANOS_PER_SEC: i64 = 1_000_000_000;
12
13fn split_secs_f64(secs: f64) -> (i32, u32) {
16 let truncated = secs as i32;
17 let sec = if (truncated as f64) > secs {
20 truncated - 1
21 } else {
22 truncated
23 };
24 let frac = secs - sec as f64;
25 let frac = if frac < 0.0 { -frac } else { frac };
27 let nanosec = (frac * NANOS_PER_SEC as f64) as u32;
28 (sec, nanosec)
29}
30
31pub trait SecNanosecMsg: Sized {
52 fn from_sec_nanosec(sec: i32, nanosec: u32) -> Self;
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord)]
66pub struct Time {
67 pub sec: i32,
69 pub nanosec: u32,
71}
72
73impl Time {
74 pub const fn new(sec: i32, nanosec: u32) -> Self {
76 Self { sec, nanosec }
77 }
78
79 pub const fn zero() -> Self {
81 Self { sec: 0, nanosec: 0 }
82 }
83
84 pub const fn is_zero(&self) -> bool {
86 self.sec == 0 && self.nanosec == 0
87 }
88
89 pub const fn from_nanos(nanos: i64) -> Self {
91 let sec = (nanos / NANOS_PER_SEC) as i32;
92 let nanosec = (nanos % NANOS_PER_SEC) as u32;
93 Self { sec, nanosec }
94 }
95
96 pub const fn to_nanos(&self) -> i64 {
98 (self.sec as i64) * NANOS_PER_SEC + (self.nanosec as i64)
99 }
100
101 pub const fn from_duration(d: Duration) -> Self {
103 Self {
104 sec: d.sec,
105 nanosec: d.nanosec,
106 }
107 }
108
109 pub const fn as_duration(&self) -> Duration {
111 Duration {
112 sec: self.sec,
113 nanosec: self.nanosec,
114 }
115 }
116
117 pub fn from_secs_f64(secs: f64) -> Self {
119 let (sec, nanosec) = split_secs_f64(secs);
120 Self { sec, nanosec }
121 }
122
123 pub fn to_secs_f64(&self) -> f64 {
125 self.sec as f64 + (self.nanosec as f64 / NANOS_PER_SEC as f64)
126 }
127
128 pub fn to_ros_msg<M: SecNanosecMsg>(&self) -> M {
148 M::from_sec_nanosec(self.sec, self.nanosec)
149 }
150}
151
152impl Add<Duration> for Time {
153 type Output = Time;
154
155 fn add(self, rhs: Duration) -> Self::Output {
156 let total_nanos = self.to_nanos() + rhs.to_nanos();
157 Time::from_nanos(total_nanos)
158 }
159}
160
161impl Sub<Duration> for Time {
162 type Output = Time;
163
164 fn sub(self, rhs: Duration) -> Self::Output {
165 let total_nanos = self.to_nanos() - rhs.to_nanos();
166 Time::from_nanos(total_nanos)
167 }
168}
169
170impl Sub<Time> for Time {
171 type Output = Duration;
172
173 fn sub(self, rhs: Time) -> Self::Output {
174 let diff_nanos = self.to_nanos() - rhs.to_nanos();
175 Duration::from_nanos(diff_nanos)
176 }
177}
178
179impl Serialize for Time {
180 fn serialize(&self, writer: &mut CdrWriter) -> Result<(), SerError> {
181 writer.write_i32(self.sec)?;
182 writer.write_u32(self.nanosec)?;
183 Ok(())
184 }
185}
186
187impl Deserialize for Time {
188 fn deserialize(reader: &mut CdrReader) -> Result<Self, DeserError> {
189 Ok(Self {
190 sec: reader.read_i32()?,
191 nanosec: reader.read_u32()?,
192 })
193 }
194}
195
196#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord)]
202pub struct Duration {
203 pub sec: i32,
205 pub nanosec: u32,
207}
208
209impl Duration {
210 pub const fn new(sec: i32, nanosec: u32) -> Self {
212 Self { sec, nanosec }
213 }
214
215 pub const fn zero() -> Self {
217 Self { sec: 0, nanosec: 0 }
218 }
219
220 pub const fn is_zero(&self) -> bool {
222 self.sec == 0 && self.nanosec == 0
223 }
224
225 pub const fn from_secs(secs: i32) -> Self {
227 Self {
228 sec: secs,
229 nanosec: 0,
230 }
231 }
232
233 pub const fn from_millis(millis: i64) -> Self {
235 let sec = (millis / 1000) as i32;
236 let nanosec = ((millis % 1000) * 1_000_000) as u32;
237 Self { sec, nanosec }
238 }
239
240 pub const fn from_nanos(nanos: i64) -> Self {
242 let sec = (nanos / NANOS_PER_SEC) as i32;
243 let nanosec = (nanos % NANOS_PER_SEC).unsigned_abs() as u32;
244 Self { sec, nanosec }
245 }
246
247 pub const fn to_nanos(&self) -> i64 {
249 (self.sec as i64) * NANOS_PER_SEC + (self.nanosec as i64)
250 }
251
252 pub fn from_secs_f64(secs: f64) -> Self {
254 let (sec, nanosec) = split_secs_f64(secs);
255 Self { sec, nanosec }
256 }
257
258 pub fn to_secs_f64(&self) -> f64 {
260 self.sec as f64 + (self.nanosec as f64 / NANOS_PER_SEC as f64)
261 }
262}
263
264impl Add for Duration {
265 type Output = Duration;
266
267 fn add(self, rhs: Duration) -> Self::Output {
268 let total_nanos = self.to_nanos() + rhs.to_nanos();
269 Duration::from_nanos(total_nanos)
270 }
271}
272
273impl Sub for Duration {
274 type Output = Duration;
275
276 fn sub(self, rhs: Duration) -> Self::Output {
277 let diff_nanos = self.to_nanos() - rhs.to_nanos();
278 Duration::from_nanos(diff_nanos)
279 }
280}
281
282impl Serialize for Duration {
283 fn serialize(&self, writer: &mut CdrWriter) -> Result<(), SerError> {
284 writer.write_i32(self.sec)?;
285 writer.write_u32(self.nanosec)?;
286 Ok(())
287 }
288}
289
290impl Deserialize for Duration {
291 fn deserialize(reader: &mut CdrReader) -> Result<Self, DeserError> {
292 Ok(Self {
293 sec: reader.read_i32()?,
294 nanosec: reader.read_u32()?,
295 })
296 }
297}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302
303 #[derive(Debug, PartialEq)]
309 struct FakeTimeMsg {
310 sec: i32,
311 nanosec: u32,
312 }
313
314 impl SecNanosecMsg for FakeTimeMsg {
315 fn from_sec_nanosec(sec: i32, nanosec: u32) -> Self {
316 Self { sec, nanosec }
317 }
318 }
319
320 #[test]
321 fn to_ros_msg_copies_both_fields_and_cannot_fail() {
322 let t = Time::new(1_234_567_890, 123_456_789);
323 let msg: FakeTimeMsg = t.to_ros_msg();
326 assert_eq!(
327 msg,
328 FakeTimeMsg {
329 sec: 1_234_567_890,
330 nanosec: 123_456_789
331 }
332 );
333 }
334
335 #[test]
336 fn to_ros_msg_carries_a_pre_epoch_stamp_unchanged() {
337 let t = Time::new(-5, 999_999_999);
340 let msg: FakeTimeMsg = t.to_ros_msg();
341 assert_eq!(msg.sec, -5);
342 assert_eq!(msg.nanosec, 999_999_999);
343 }
344
345 #[test]
346 fn test_time_roundtrip() {
347 let mut buf = [0u8; 16];
348 let time = Time::new(1234567890, 123456789);
349
350 let mut writer = CdrWriter::new(&mut buf);
351 time.serialize(&mut writer).unwrap();
352
353 let mut reader = CdrReader::new(&buf);
354 let result = Time::deserialize(&mut reader).unwrap();
355 assert_eq!(result, time);
356 }
357
358 #[test]
359 fn test_duration_roundtrip() {
360 let mut buf = [0u8; 16];
361 let duration = Duration::new(-10, 500000000);
362
363 let mut writer = CdrWriter::new(&mut buf);
364 duration.serialize(&mut writer).unwrap();
365
366 let mut reader = CdrReader::new(&buf);
367 let result = Duration::deserialize(&mut reader).unwrap();
368 assert_eq!(result, duration);
369 }
370
371 #[test]
372 fn test_time_from_nanos() {
373 let time = Time::from_nanos(1_500_000_000);
374 assert_eq!(time.sec, 1);
375 assert_eq!(time.nanosec, 500_000_000);
376 }
377
378 #[test]
379 fn test_time_to_nanos() {
380 let time = Time::new(2, 500_000_000);
381 assert_eq!(time.to_nanos(), 2_500_000_000);
382 }
383
384 #[test]
385 fn test_time_add_duration() {
386 let time = Time::new(10, 500_000_000);
387 let duration = Duration::new(5, 700_000_000);
388 let result = time + duration;
389 assert_eq!(result.sec, 16);
390 assert_eq!(result.nanosec, 200_000_000);
391 }
392
393 #[test]
394 fn test_time_sub_duration() {
395 let time = Time::new(10, 500_000_000);
396 let duration = Duration::new(3, 200_000_000);
397 let result = time - duration;
398 assert_eq!(result.sec, 7);
399 assert_eq!(result.nanosec, 300_000_000);
400 }
401
402 #[test]
403 fn test_time_sub_time() {
404 let t1 = Time::new(10, 500_000_000);
405 let t2 = Time::new(7, 200_000_000);
406 let result = t1 - t2;
407 assert_eq!(result.sec, 3);
408 assert_eq!(result.nanosec, 300_000_000);
409 }
410
411 #[test]
412 fn test_time_from_duration() {
413 let duration = Duration::new(5, 123456789);
414 let time = Time::from_duration(duration);
415 assert_eq!(time.sec, 5);
416 assert_eq!(time.nanosec, 123456789);
417 }
418
419 #[test]
420 fn test_time_as_duration() {
421 let time = Time::new(5, 123456789);
422 let duration = time.as_duration();
423 assert_eq!(duration.sec, 5);
424 assert_eq!(duration.nanosec, 123456789);
425 }
426
427 #[test]
428 fn test_time_ordering() {
429 let t1 = Time::new(10, 0);
430 let t2 = Time::new(10, 500_000_000);
431 let t3 = Time::new(11, 0);
432 assert!(t1 < t2);
433 assert!(t2 < t3);
434 }
435
436 #[test]
437 fn test_duration_from_nanos() {
438 let duration = Duration::from_nanos(2_500_000_000);
439 assert_eq!(duration.sec, 2);
440 assert_eq!(duration.nanosec, 500_000_000);
441 }
442
443 #[test]
444 fn test_duration_to_nanos() {
445 let duration = Duration::new(3, 250_000_000);
446 assert_eq!(duration.to_nanos(), 3_250_000_000);
447 }
448
449 #[test]
450 fn test_duration_add() {
451 let d1 = Duration::new(2, 500_000_000);
452 let d2 = Duration::new(1, 700_000_000);
453 let result = d1 + d2;
454 assert_eq!(result.sec, 4);
455 assert_eq!(result.nanosec, 200_000_000);
456 }
457
458 #[test]
459 fn test_duration_sub() {
460 let d1 = Duration::new(5, 500_000_000);
461 let d2 = Duration::new(2, 200_000_000);
462 let result = d1 - d2;
463 assert_eq!(result.sec, 3);
464 assert_eq!(result.nanosec, 300_000_000);
465 }
466
467 #[test]
468 fn test_duration_ordering() {
469 let d1 = Duration::new(1, 0);
470 let d2 = Duration::new(1, 500_000_000);
471 let d3 = Duration::new(2, 0);
472 assert!(d1 < d2);
473 assert!(d2 < d3);
474 }
475
476 #[test]
477 fn test_time_secs_f64() {
478 let time = Time::from_secs_f64(1.5);
479 assert_eq!(time.sec, 1);
480 assert_eq!(time.nanosec, 500_000_000);
481
482 let secs = time.to_secs_f64();
483 assert!((secs - 1.5).abs() < 0.000001);
484 }
485
486 #[test]
487 fn test_duration_secs_f64() {
488 let duration = Duration::from_secs_f64(2.25);
489 assert_eq!(duration.sec, 2);
490 assert_eq!(duration.nanosec, 250_000_000);
491
492 let secs = duration.to_secs_f64();
493 assert!((secs - 2.25).abs() < 0.000001);
494 }
495}
496
497#[cfg(kani)]
502mod verification {
503 use super::*;
504
505 #[kani::proof]
508 fn duration_from_nanos_no_panic() {
509 let nanos: i64 = kani::any();
510 kani::assume(nanos >= -10_000_000_000 && nanos <= 10_000_000_000);
512 let dur = Duration::from_nanos(nanos);
513 assert!(dur.nanosec < NANOS_PER_SEC as u32);
515 }
516
517 #[kani::proof]
518 fn duration_roundtrip_nanos() {
519 let nanos: i64 = kani::any();
520 kani::assume(nanos >= 0 && nanos <= 10_000_000_000);
523 let dur = Duration::from_nanos(nanos);
524 assert_eq!(dur.to_nanos(), nanos);
525 }
526
527 #[kani::proof]
528 fn duration_zero_is_zero() {
529 let dur = Duration::zero();
530 assert!(dur.is_zero());
531 assert_eq!(dur.to_nanos(), 0);
532 }
533
534 #[kani::proof]
535 fn duration_from_secs() {
536 let secs: i32 = kani::any();
537 let dur = Duration::from_secs(secs);
538 assert_eq!(dur.sec, secs);
539 assert_eq!(dur.nanosec, 0);
540 }
541
542 #[kani::proof]
543 #[kani::unwind(5)]
544 fn duration_serialize_roundtrip() {
545 let sec: i32 = kani::any();
546 let nanosec: u32 = kani::any();
547 kani::assume(nanosec < NANOS_PER_SEC as u32);
548 let dur = Duration::new(sec, nanosec);
549
550 let mut buf = [0u8; 16];
551 let len = {
552 let mut writer = CdrWriter::new(&mut buf);
553 dur.serialize(&mut writer).unwrap();
554 writer.position()
555 };
556
557 let mut reader = CdrReader::new(&buf[..len]);
558 let deserialized = Duration::deserialize(&mut reader).unwrap();
559 assert_eq!(dur, deserialized);
560 }
561
562 #[kani::proof]
565 fn time_from_nanos_no_panic() {
566 let nanos: i64 = kani::any();
567 kani::assume(nanos >= 0 && nanos <= 10_000_000_000);
571 let time = Time::from_nanos(nanos);
572 assert!(time.nanosec < NANOS_PER_SEC as u32);
574 }
575
576 #[kani::proof]
577 fn time_roundtrip_nanos() {
578 let nanos: i64 = kani::any();
579 kani::assume(nanos >= 0 && nanos <= 10_000_000_000);
581 let time = Time::from_nanos(nanos);
582 assert_eq!(time.to_nanos(), nanos);
583 }
584
585 #[kani::proof]
586 fn time_zero_is_zero() {
587 let time = Time::zero();
588 assert!(time.is_zero());
589 assert_eq!(time.to_nanos(), 0);
590 }
591
592 #[kani::proof]
593 fn time_duration_conversion() {
594 let sec: i32 = kani::any();
595 let nanosec: u32 = kani::any();
596 kani::assume(nanosec < NANOS_PER_SEC as u32);
597 let time = Time::new(sec, nanosec);
598 let dur = time.as_duration();
599 assert_eq!(dur.sec, time.sec);
600 assert_eq!(dur.nanosec, time.nanosec);
601 let back = Time::from_duration(dur);
602 assert_eq!(back, time);
603 }
604
605 #[kani::proof]
606 #[kani::unwind(5)]
607 fn time_serialize_roundtrip() {
608 let sec: i32 = kani::any();
609 let nanosec: u32 = kani::any();
610 kani::assume(nanosec < NANOS_PER_SEC as u32);
611 let time = Time::new(sec, nanosec);
612
613 let mut buf = [0u8; 16];
614 let len = {
615 let mut writer = CdrWriter::new(&mut buf);
616 time.serialize(&mut writer).unwrap();
617 writer.position()
618 };
619
620 let mut reader = CdrReader::new(&buf[..len]);
621 let deserialized = Time::deserialize(&mut reader).unwrap();
622 assert_eq!(time, deserialized);
623 }
624}