1use std::{
9 convert::TryFrom,
10 ops::{Add, Sub},
11 time::Duration,
12};
13
14use serde::{Deserialize, Serialize};
15use chrono::{DateTime, Utc};
16use log::error;
17use rustdds::Timestamp;
18
19#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Serialize, Deserialize)]
23pub struct ROSTime {
24 nanos_since_epoch: i64,
25}
26
27impl ROSTime {
28 pub(crate) fn now() -> Self {
32 Self::try_from(chrono::Utc::now()).unwrap_or(Self::ZERO)
33 }
34
35 pub const ZERO: Self = Self::from_nanos(0);
36 pub const UNIX_EPOCH: Self = Self::from_nanos(0);
37
38 pub fn to_nanos(&self) -> i64 {
39 self.nanos_since_epoch
40 }
41
42 pub const fn from_nanos(nanos_since_unix_epoch: i64) -> Self {
43 Self {
44 nanos_since_epoch: nanos_since_unix_epoch,
45 }
46 }
47}
48
49#[derive(Clone, Debug)]
51pub struct OutOfRangeError {}
52
53impl TryFrom<chrono::DateTime<Utc>> for ROSTime {
65 type Error = OutOfRangeError;
66
67 fn try_from(chrono_time: chrono::DateTime<Utc>) -> Result<ROSTime, OutOfRangeError> {
68 chrono_time
69 .timestamp_nanos_opt()
70 .ok_or_else(|| {
71 error!("ROSTime: chrono timestamp is out of range: {chrono_time:?}");
72 OutOfRangeError {}
73 })
74 .map(ROSTime::from_nanos)
75 }
76}
77
78impl From<ROSTime> for chrono::DateTime<Utc> {
79 fn from(rt: ROSTime) -> chrono::DateTime<Utc> {
80 DateTime::<Utc>::from_timestamp_nanos(rt.to_nanos())
81 }
82}
83
84impl From<ROSTime> for Timestamp {
87 fn from(rt: ROSTime) -> Timestamp {
88 let chrono_time = chrono::DateTime::<Utc>::from(rt);
89 Timestamp::try_from(chrono_time).unwrap_or_else(|e| {
90 error!("Time conversion ROSTime to Timestamp error: {e} source={rt:?}");
91 rustdds::Timestamp::INVALID
92 })
93 }
94}
95
96pub enum TimestampConversionError {
103 Invalid,
105 Infinite,
107}
108
109impl TryFrom<Timestamp> for ROSTime {
110 type Error = TimestampConversionError;
111 fn try_from(ts: Timestamp) -> Result<ROSTime, TimestampConversionError> {
112 match ts {
113 Timestamp::INVALID => Err(TimestampConversionError::Invalid),
114 Timestamp::INFINITE => Err(TimestampConversionError::Infinite),
115 ts => {
116 let ticks: u64 = ts.to_ticks(); let seconds = ticks >> 32;
118 let frac_ticks = ticks - (seconds << 32); let frac_nanos = (frac_ticks * 1_000_000_000) >> 32;
120 Ok(ROSTime::from_nanos(
128 (seconds * 1_000_000_000 + frac_nanos) as i64,
129 ))
130 }
131 }
132 }
133}
134
135impl Sub for ROSTime {
136 type Output = ROSDuration;
137
138 fn sub(self, other: ROSTime) -> ROSDuration {
139 ROSDuration {
140 diff: self.nanos_since_epoch - other.nanos_since_epoch,
141 }
142 }
143}
144
145impl Sub<ROSDuration> for ROSTime {
146 type Output = ROSTime;
147
148 fn sub(self, other: ROSDuration) -> ROSTime {
149 ROSTime {
150 nanos_since_epoch: self.nanos_since_epoch - other.diff,
151 }
152 }
153}
154
155impl Add<ROSDuration> for ROSTime {
156 type Output = ROSTime;
157
158 fn add(self, other: ROSDuration) -> ROSTime {
159 ROSTime {
160 nanos_since_epoch: self.nanos_since_epoch + other.diff,
161 }
162 }
163}
164
165pub struct ROSDuration {
171 diff: i64,
172}
173
174impl ROSDuration {
175 pub const fn from_nanos(nanos: i64) -> Self {
177 ROSDuration { diff: nanos }
178 }
179
180 pub const fn to_nanos(&self) -> i64 {
183 self.diff
184 }
185}
186
187impl TryFrom<Duration> for ROSDuration {
190 type Error = OutOfRangeError;
191
192 fn try_from(std_duration: Duration) -> Result<Self, Self::Error> {
193 let nanos = std_duration.as_nanos();
194 if nanos <= (i64::MAX as u128) {
195 Ok(ROSDuration { diff: nanos as i64 })
196 } else {
197 Err(OutOfRangeError {})
198 }
199 }
200}
201
202impl TryFrom<ROSDuration> for Duration {
203 type Error = OutOfRangeError;
204
205 fn try_from(ros_duration: ROSDuration) -> Result<Duration, Self::Error> {
206 Ok(Duration::from_nanos(
207 u64::try_from(ros_duration.to_nanos()).map_err(|_e| OutOfRangeError {})?,
208 ))
209 }
210}
211
212impl From<ROSDuration> for chrono::Duration {
215 fn from(d: ROSDuration) -> chrono::Duration {
216 chrono::Duration::nanoseconds(d.to_nanos())
217 }
218}
219
220impl TryFrom<chrono::Duration> for ROSDuration {
221 type Error = OutOfRangeError;
222
223 fn try_from(c_duration: chrono::Duration) -> Result<Self, Self::Error> {
224 c_duration
225 .num_nanoseconds()
226 .map(ROSDuration::from_nanos)
227 .ok_or(OutOfRangeError {})
228 }
229}
230
231impl Add for ROSDuration {
235 type Output = ROSDuration;
236 fn add(self, other: ROSDuration) -> ROSDuration {
237 ROSDuration {
238 diff: self.diff + other.diff,
239 }
240 }
241}
242
243impl Sub for ROSDuration {
245 type Output = ROSDuration;
246 fn sub(self, other: ROSDuration) -> ROSDuration {
247 ROSDuration {
248 diff: self.diff - other.diff,
249 }
250 }
251}
252
253#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Serialize, Deserialize)]
257pub struct SystemTime {
258 ros_time: ROSTime,
259}
260
261#[cfg(test)]
264mod test {
265 #[test]
270 fn conversion() {
271 }
273}