1use crate::Duration;
2
3#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Hash)]
7pub struct Time {
8 pub(crate) inner: cyclonedds_sys::dds_time_t,
9}
10
11impl Time {
12 pub const NEVER: Self = Time {
16 inner: cyclonedds_sys::TIME_NEVER,
17 };
18
19 #[must_use]
29 pub const fn from_nanos(nanos: i64) -> Self {
30 Self { inner: nanos }
31 }
32
33 #[must_use]
43 pub const fn from_millis(millis: i64) -> Self {
44 Self {
45 inner: millis * 1_000_000,
46 }
47 }
48
49 #[must_use]
59 pub const fn from_secs(secs: i64) -> Self {
60 Self {
61 inner: secs * 1_000_000_000,
62 }
63 }
64
65 #[must_use]
76 pub const fn as_nanos(&self) -> i64 {
77 self.inner
78 }
79
80 pub fn elapsed(&self) -> crate::Result<Duration> {
87 Time::try_from(std::time::SystemTime::now()).and_then(|now| {
88 if self.inner <= now.inner {
89 let nanos = now.inner - self.inner;
90 Ok(Duration::from_nanos(nanos))
91 } else {
92 Err(crate::Error::BadParameter)
93 }
94 })
95 }
96
97 #[must_use]
108 pub fn checked_add(&self, duration: Duration) -> Option<Self> {
109 self.inner
110 .checked_add(duration.inner)
111 .map(|inner| Self { inner })
112 }
113
114 #[must_use]
125 pub fn checked_sub(&self, duration: Duration) -> Option<Self> {
126 self.inner
127 .checked_sub(duration.inner)
128 .map(|inner| Self { inner })
129 }
130}
131
132impl TryFrom<std::time::SystemTime> for Time {
133 type Error = crate::Error;
134
135 fn try_from(value: std::time::SystemTime) -> Result<Self, Self::Error> {
136 let value = value
137 .duration_since(std::time::SystemTime::UNIX_EPOCH)
138 .map_err(|_err| crate::Error::BadParameter)?;
139
140 let inner = cyclonedds_sys::dds_time_t::try_from(value.as_nanos())
141 .map_err(|_err| crate::Error::BadParameter)?;
142
143 if inner == Self::NEVER.inner {
144 Err(crate::Error::BadParameter)
145 } else {
146 Ok(Self { inner })
147 }
148 }
149}
150
151impl From<Time> for std::time::SystemTime {
152 fn from(time: Time) -> Self {
153 std::time::SystemTime::UNIX_EPOCH
154 + std::time::Duration::from_nanos(time.inner.cast_unsigned())
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161 use crate::Duration;
162
163 #[test]
164 fn test_time_create() {
165 let nanos = 1_000_000_000;
166 let time = Time::from_nanos(nanos);
167 assert_eq!(time.inner, nanos);
168 }
169
170 #[test]
171 fn test_time_from_std_system_time() {
172 let nanos = 1_000_000_000;
173 let standard =
174 std::time::SystemTime::UNIX_EPOCH + std::time::Duration::from_nanos(nanos as u64);
175 let time = Time::try_from(standard).unwrap();
176 assert_eq!(time.inner, nanos);
177 }
178
179 #[test]
180 #[cfg(not(target_os = "windows"))]
183 fn test_time_from_never_std_system_time() {
184 let nanos = Time::NEVER.inner as u64;
185 let standard = std::time::SystemTime::UNIX_EPOCH + std::time::Duration::from_nanos(nanos);
186 let result = Time::try_from(standard).unwrap_err();
187 assert_eq!(result, crate::Error::BadParameter);
188 }
189
190 #[test]
191 fn test_time_from_out_of_range_std_system_time() {
192 let standard = std::time::SystemTime::UNIX_EPOCH - std::time::Duration::from_nanos(100);
193 let result = Time::try_from(standard).unwrap_err();
194 assert_eq!(result, crate::Error::BadParameter);
195
196 let nanos = u64::MAX;
197 let standard = std::time::SystemTime::UNIX_EPOCH + std::time::Duration::from_nanos(nanos);
198 let result = Time::try_from(standard).unwrap_err();
199 assert_eq!(result, crate::Error::BadParameter);
200 }
201
202 #[test]
203 fn test_time_from_millis() {
204 let time = Time::from_millis(1_000);
205 assert_eq!(time.inner, 1_000_000_000);
206 }
207
208 #[test]
209 fn test_time_from_secs() {
210 let time = Time::from_secs(1);
211 assert_eq!(time.inner, 1_000_000_000);
212 }
213
214 #[test]
215 fn test_time_as_nanos() {
216 let time = Time::from_nanos(1_000_000_000);
217 assert_eq!(time.as_nanos(), 1_000_000_000);
218 }
219
220 #[test]
221 fn test_time_checked_add() {
222 let result = Time::from_secs(1).checked_add(Duration::from_secs(2));
223 assert_eq!(result, Some(Time::from_secs(3)));
224 }
225
226 #[test]
227 fn test_time_checked_add_overflow() {
228 let result = Time::from_nanos(i64::MAX).checked_add(Duration::from_nanos(1));
229 assert_eq!(result, None);
230 }
231
232 #[test]
233 fn test_time_checked_sub() {
234 let result = Time::from_secs(3).checked_sub(Duration::from_secs(1));
235 assert_eq!(result, Some(Time::from_secs(2)));
236 }
237
238 #[test]
239 fn test_time_checked_sub_underflow() {
240 let result = Time::from_nanos(i64::MIN).checked_sub(Duration::from_nanos(1));
241 assert_eq!(result, None);
242 }
243
244 #[test]
245 fn test_time_elapsed_increases() {
246 let before = Time::try_from(std::time::SystemTime::now()).unwrap();
247 std::thread::sleep(std::time::Duration::from_millis(10));
248 let elapsed = before.elapsed().unwrap();
249 assert!(elapsed.as_nanos() >= 10_000_000);
250 }
251
252 #[test]
253 fn test_time_elapsed_future_returns_error() {
254 let future = Time::from_nanos(i64::MAX);
255 assert_eq!(future.elapsed().unwrap_err(), crate::Error::BadParameter);
256 }
257
258 #[test]
259 fn test_to_std_system_time() {
260 let nanos = 11_222_33;
261 let time = Time::from_nanos(nanos);
262 let expected =
263 std::time::SystemTime::UNIX_EPOCH + std::time::Duration::from_nanos(nanos as u64);
264 let actual: std::time::SystemTime = time.into();
265 assert_eq!(actual, expected);
266 }
267}