1#[cfg(feature = "std")]
23use crate::time_base::current_time;
24use crate::time_base::{InaccuracyT, IntervalT, TdfT, TimeT, UtcT};
25use crate::tio::Tio;
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum ComparisonType {
30 IntervalC,
32 MidC,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum TimeComparison {
40 EqualTo,
42 LessThan,
44 GreaterThan,
46 Indeterminate,
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub struct Uto {
54 inner: UtcT,
55}
56
57impl Uto {
58 #[must_use]
60 pub const fn from_utc(utc: UtcT) -> Self {
61 Self { inner: utc }
62 }
63
64 #[must_use]
67 pub const fn new(time: TimeT, inaccuracy: InaccuracyT, tdf: TdfT) -> Self {
68 Self {
69 inner: UtcT::new(time, inaccuracy, tdf),
70 }
71 }
72
73 #[must_use]
75 pub const fn time(self) -> TimeT {
76 self.inner.time
77 }
78
79 #[must_use]
81 pub const fn inaccuracy(self) -> InaccuracyT {
82 self.inner.inaccuracy()
83 }
84
85 #[must_use]
87 pub const fn tdf(self) -> TdfT {
88 self.inner.tdf
89 }
90
91 #[must_use]
93 pub const fn utc_time(self) -> UtcT {
94 self.inner
95 }
96
97 #[cfg(feature = "std")]
101 #[must_use]
102 pub fn absolute_time(self) -> Option<Self> {
103 let now = current_time();
104 let absolute = now.checked_add(self.inner.time)?;
105 Some(Self::from_utc(UtcT::new(
106 absolute,
107 self.inner.inaccuracy(),
108 self.inner.tdf,
109 )))
110 }
111
112 #[must_use]
115 pub fn compare_time(self, comparison_type: ComparisonType, other: Self) -> TimeComparison {
116 match comparison_type {
117 ComparisonType::MidC => {
118 match self.inner.time.cmp(&other.inner.time) {
120 core::cmp::Ordering::Less => TimeComparison::LessThan,
121 core::cmp::Ordering::Greater => TimeComparison::GreaterThan,
122 core::cmp::Ordering::Equal => TimeComparison::EqualTo,
123 }
124 }
125 ComparisonType::IntervalC => {
126 let self_lo = self.inner.time.saturating_sub(self.inaccuracy());
130 let self_hi = self.inner.time.saturating_add(self.inaccuracy());
131 let other_lo = other.inner.time.saturating_sub(other.inaccuracy());
132 let other_hi = other.inner.time.saturating_add(other.inaccuracy());
133
134 if self.inner.time == other.inner.time
135 && self.inaccuracy() == 0
136 && other.inaccuracy() == 0
137 {
138 TimeComparison::EqualTo
139 } else if self_hi < other_lo {
140 TimeComparison::LessThan
141 } else if self_lo > other_hi {
142 TimeComparison::GreaterThan
143 } else {
144 TimeComparison::Indeterminate
146 }
147 }
148 }
149 }
150
151 #[must_use]
155 pub fn time_to_interval(self, other: Self) -> Option<Tio> {
156 let (lo, hi) = if self.inner.time <= other.inner.time {
157 (self.inner.time, other.inner.time)
158 } else {
159 (other.inner.time, self.inner.time)
160 };
161 IntervalT::new(lo, hi).map(Tio::from_interval)
162 }
163
164 #[must_use]
167 pub fn interval(self) -> Tio {
168 let lo = self.inner.time.saturating_sub(self.inaccuracy());
169 let hi = self.inner.time.saturating_add(self.inaccuracy());
170 Tio::from_interval(IntervalT::new(lo, hi).unwrap_or(IntervalT {
171 lower_bound: lo,
172 upper_bound: lo,
173 }))
174 }
175}
176
177#[cfg(test)]
178#[allow(clippy::expect_used, clippy::unwrap_used)]
179mod tests {
180 use super::*;
181
182 #[test]
183 fn attributes_return_constructor_values() {
184 let uto = Uto::new(1_000, 50, 60);
186 assert_eq!(uto.time(), 1_000);
187 assert_eq!(uto.inaccuracy(), 50);
188 assert_eq!(uto.tdf(), 60);
189 let utc = uto.utc_time();
190 assert_eq!(utc.time, 1_000);
191 assert_eq!(utc.tdf, 60);
192 }
193
194 #[test]
195 fn compare_time_midc_equal() {
196 let a = Uto::new(100, 50, 0);
197 let b = Uto::new(100, 200, 0);
198 assert_eq!(
199 a.compare_time(ComparisonType::MidC, b),
200 TimeComparison::EqualTo
201 );
202 }
203
204 #[test]
205 fn compare_time_midc_less_than() {
206 let a = Uto::new(100, 0, 0);
207 let b = Uto::new(200, 0, 0);
208 assert_eq!(
209 a.compare_time(ComparisonType::MidC, b),
210 TimeComparison::LessThan
211 );
212 }
213
214 #[test]
215 fn compare_time_midc_greater_than() {
216 let a = Uto::new(300, 0, 0);
217 let b = Uto::new(200, 0, 0);
218 assert_eq!(
219 a.compare_time(ComparisonType::MidC, b),
220 TimeComparison::GreaterThan
221 );
222 }
223
224 #[test]
225 fn compare_time_intervalc_equal_when_inaccuracy_zero_and_time_match() {
226 let a = Uto::new(100, 0, 0);
227 let b = Uto::new(100, 0, 0);
228 assert_eq!(
229 a.compare_time(ComparisonType::IntervalC, b),
230 TimeComparison::EqualTo
231 );
232 }
233
234 #[test]
235 fn compare_time_intervalc_indeterminate_on_envelope_overlap() {
236 let a = Uto::new(100, 50, 0); let b = Uto::new(120, 50, 0); assert_eq!(
240 a.compare_time(ComparisonType::IntervalC, b),
241 TimeComparison::Indeterminate
242 );
243 }
244
245 #[test]
246 fn compare_time_intervalc_less_than_when_envelopes_disjoint() {
247 let a = Uto::new(100, 10, 0); let b = Uto::new(200, 10, 0); assert_eq!(
250 a.compare_time(ComparisonType::IntervalC, b),
251 TimeComparison::LessThan
252 );
253 }
254
255 #[test]
256 fn compare_time_intervalc_greater_than_when_envelopes_disjoint() {
257 let a = Uto::new(300, 10, 0);
258 let b = Uto::new(200, 10, 0);
259 assert_eq!(
260 a.compare_time(ComparisonType::IntervalC, b),
261 TimeComparison::GreaterThan
262 );
263 }
264
265 #[test]
266 fn time_to_interval_uses_midpoints() {
267 let a = Uto::new(100, 9999, 0);
270 let b = Uto::new(200, 9999, 0);
271 let tio = a.time_to_interval(b).expect("ok");
272 assert_eq!(tio.time_interval().lower_bound, 100);
273 assert_eq!(tio.time_interval().upper_bound, 200);
274 }
275
276 #[test]
277 fn interval_returns_inaccuracy_envelope() {
278 let uto = Uto::new(1_000, 50, 0);
280 let tio = uto.interval();
281 assert_eq!(tio.time_interval().lower_bound, 950);
282 assert_eq!(tio.time_interval().upper_bound, 1_050);
283 }
284
285 #[cfg(feature = "std")]
286 #[test]
287 fn absolute_time_adds_current_to_relative() {
288 let relative = Uto::new(1_000, 0, 0);
289 let absolute = relative.absolute_time().expect("ok");
290 assert!(absolute.time() > 1_000);
292 }
293}