ros2_client/
steady_time.rs

1//! Steady time has an arbitrary origin, but is guaranteed to run
2//! monotonically (i.e. non-decreasing), regardless of clock corrections or
3//! timezones.  
4//!
5//! *Note* Steady time is not actually steady in the sense that the underlying
6//! clock would always run. Steady time clock might be paused e.g. because of
7//! operating system sleep modes, hypervisors, or other infrastructure below
8//! this library. This module is called `steady_time` only to better conform to
9//! ROS 2 naming.
10//!
11//! The current implementation is based on `std::time::Instant`. Because of
12//! this, there is no directo conversion to/from nanoseconds, or other time
13//! types.
14//!
15//! Steady time should be used only when it is necessary, e.g. because of
16//! interacting with hardware. Steady time cannot be simulated. Use ROS time
17//! instead, whenever possible.
18
19use std::{
20  cmp::Ordering,
21  convert::TryFrom,
22  fmt,
23  ops::{Add, Sub},
24  time::{Duration, Instant},
25};
26
27use chrono::{DateTime, Utc};
28
29use crate::ROSTime;
30
31/// Monotonic time in nanoseconds
32///
33/// To get offset to UTC time, use now_with_utc() note that the offset will
34/// change over time, latest at the next leap second.
35#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug)]
36pub struct Time {
37  instant: Instant,
38}
39
40impl Time {
41  pub fn now() -> Time {
42    Self {
43      instant: Instant::now(),
44    }
45  }
46
47  /// returns the current time in two formats: Time and ROSTime
48  pub fn now_with_ros_time() -> (Time, ROSTime) {
49    let (st, ct) = Self::now_with_utc();
50    let ros_time = ROSTime::try_from(ct)
51      // try_from conversion already logs an error
52      .unwrap_or(ROSTime::ZERO);
53    (st, ros_time)
54  }
55
56  #[doc(hidden)]
57  pub fn now_with_utc() -> (Time, DateTime<Utc>) {
58    let m0 = Self::now();
59    let utc = Utc::now();
60    let m1 = Self::now();
61    let diff = m1 - m0;
62    //println!("now_with_utc() diff = {} ns" , diff.as_nanos() );
63    // TODO: check that diff is very small and complain if not
64
65    // We add half of the diff to compensate for the difference in call times.
66    (m0 + TimeDiff::from_nanos(diff.as_nanos() / 2), utc)
67  }
68} // impl Time
69
70impl fmt::Display for Time {
71  fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
72    // TODO: needs a display customization
73    fmt::Debug::fmt(self, fmt)
74  }
75}
76
77impl Sub for Time {
78  type Output = TimeDiff;
79
80  fn sub(self, other: Time) -> TimeDiff {
81    self
82      .instant
83      .checked_duration_since(other.instant)
84      // This fails if other > self. Then we try the other way around
85      .map(|duration| TimeDiff {
86        duration,
87        is_negative: false,
88      })
89      .unwrap_or_else(|| TimeDiff {
90        duration: other.instant.saturating_duration_since(self.instant),
91        is_negative: true,
92      })
93  }
94}
95
96/// Note: This may panic on over/underflows
97impl Sub<TimeDiff> for Time {
98  type Output = Time;
99
100  fn sub(self, diff: TimeDiff) -> Time {
101    if diff.is_negative {
102      Time {
103        instant: self.instant - diff.duration,
104      }
105    } else {
106      Time {
107        instant: self.instant + diff.duration,
108      }
109    }
110  }
111}
112
113/// Note: This may panic on over/underflows
114impl Add<TimeDiff> for Time {
115  type Output = Time;
116
117  fn add(self, diff: TimeDiff) -> Time {
118    if diff.is_negative {
119      Time {
120        instant: self.instant + diff.duration,
121      }
122    } else {
123      Time {
124        instant: self.instant - diff.duration,
125      }
126    }
127  }
128}
129
130/// Time difference can be negative, unlike std::time::Duration
131#[derive(Clone, Copy, Debug, Eq, PartialEq)]
132pub struct TimeDiff {
133  duration: Duration,
134  is_negative: bool, // must be false if duration == Duration::ZERO
135}
136
137#[derive(Debug, Clone, Copy)]
138pub struct NegativetimeDiffError {}
139
140impl TimeDiff {
141  pub const fn from_nanos(nanos: i64) -> TimeDiff {
142    if nanos >= 0 {
143      TimeDiff {
144        duration: Duration::from_nanos(nanos as u64),
145        is_negative: false,
146      }
147    } else {
148      TimeDiff {
149        duration: Duration::from_nanos(-nanos as u64),
150        is_negative: true,
151      }
152    }
153  }
154
155  pub const fn from_millis(millis: i64) -> TimeDiff {
156    Self::from_nanos(millis * 1_000_000)
157  }
158
159  pub const fn from_secs(secs: i64) -> TimeDiff {
160    Self::from_nanos(secs * 1_000_000_000)
161  }
162
163  pub const fn as_nanos(self) -> i64 {
164    let n = self.duration.as_nanos();
165    let n = if n > (i64::MAX as u128) {
166      i64::MAX
167    } else {
168      n as i64
169    };
170    if self.is_negative {
171      -n
172    } else {
173      n
174    }
175  }
176
177  pub const fn as_millis(self) -> i64 {
178    self.as_nanos() / 1_000_000
179  }
180
181  #[allow(dead_code)]
182  pub const fn as_seconds(self) -> i64 {
183    self.as_nanos() / 1_000_000_000
184  }
185
186  pub fn as_duration(self) -> Result<Duration, NegativetimeDiffError> {
187    if self.is_negative {
188      Err(NegativetimeDiffError {})
189    } else {
190      Ok(self.duration)
191    }
192  }
193
194  pub fn as_saturating_duration(self) -> Duration {
195    if self.is_negative {
196      Duration::ZERO
197    } else {
198      self.duration
199    }
200  }
201}
202
203impl Ord for TimeDiff {
204  fn cmp(&self, other: &Self) -> Ordering {
205    match (self.is_negative, other.is_negative) {
206      (false, false) => self.duration.cmp(&other.duration),
207      (true, true) => self.duration.cmp(&other.duration).reverse(),
208      (false, true) => Ordering::Greater,
209      (true, false) => Ordering::Less,
210    }
211  }
212}
213
214impl PartialOrd for TimeDiff {
215  fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
216    Some(self.cmp(other))
217  }
218}
219
220impl fmt::Display for TimeDiff {
221  fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
222    // TODO: needs a display customization
223    fmt::Debug::fmt(self, fmt)
224  }
225}
226
227impl Add for TimeDiff {
228  type Output = TimeDiff;
229  fn add(self, other: TimeDiff) -> TimeDiff {
230    Self::from_nanos(self.as_nanos() + other.as_nanos())
231  }
232}
233
234impl Sub for TimeDiff {
235  type Output = TimeDiff;
236  fn sub(self, other: TimeDiff) -> TimeDiff {
237    Self::from_nanos(self.as_nanos() - other.as_nanos())
238  }
239}