1use std::fmt;
11
12pub type TimeStamp = u64;
15
16pub type Duration = u64;
18
19#[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd)]
23pub struct Time {
24 pub seconds: u64,
25 pub frac: f64,
26}
27
28impl Time {
29 const SECONDS_PER_MINUTE: u64 = 60;
30 const SECONDS_PER_HOUR: u64 = 60 * 60;
31 const NANOSECONDS_PER_SECOND: u32 = 1_000_000_000;
32 const NANOSECONDS_PER_SECOND_INV: f64 = 1.0 / 1_000_000_000.0;
33
34 pub fn new(seconds: u64, frac: f64) -> Self {
35 Time { seconds, frac }
36 }
37
38 pub fn from_ss(s: u8, ns: u32) -> Option<Time> {
39 if s > 59 || ns >= Time::NANOSECONDS_PER_SECOND {
40 return None;
41 }
42
43 let seconds = u64::from(s);
44 let frac = Time::NANOSECONDS_PER_SECOND_INV * f64::from(ns);
45
46 Some(Time { seconds, frac })
47 }
48
49 pub fn from_mmss(m: u8, s: u8, ns: u32) -> Option<Time> {
50 if m > 59 || s > 59 || ns >= Time::NANOSECONDS_PER_SECOND {
51 return None;
52 }
53
54 let seconds = (Time::SECONDS_PER_MINUTE * u64::from(m)) + u64::from(s);
55 let frac = Time::NANOSECONDS_PER_SECOND_INV * f64::from(ns);
56
57 Some(Time { seconds, frac })
58 }
59
60 pub fn from_hhmmss(h: u32, m: u8, s: u8, ns: u32) -> Option<Time> {
61 if m > 59 || s > 59 || ns >= Time::NANOSECONDS_PER_SECOND {
62 return None;
63 }
64
65 let seconds = (Time::SECONDS_PER_HOUR * u64::from(h))
66 + (Time::SECONDS_PER_MINUTE * u64::from(m))
67 + u64::from(s);
68
69 let frac = Time::NANOSECONDS_PER_SECOND_INV * f64::from(ns);
70
71 Some(Time { seconds, frac })
72 }
73}
74
75impl From<u8> for Time {
76 fn from(seconds: u8) -> Self {
77 Time::new(u64::from(seconds), 0.0)
78 }
79}
80
81impl From<u16> for Time {
82 fn from(seconds: u16) -> Self {
83 Time::new(u64::from(seconds), 0.0)
84 }
85}
86
87impl From<u32> for Time {
88 fn from(seconds: u32) -> Self {
89 Time::new(u64::from(seconds), 0.0)
90 }
91}
92
93impl From<u64> for Time {
94 fn from(seconds: u64) -> Self {
95 Time::new(seconds, 0.0)
96 }
97}
98
99impl From<f32> for Time {
100 fn from(seconds: f32) -> Self {
101 if seconds >= 0.0 {
102 Time::new(seconds.trunc() as u64, f64::from(seconds.fract()))
103 }
104 else {
105 Time::new(0, 0.0)
106 }
107 }
108}
109
110impl From<f64> for Time {
111 fn from(seconds: f64) -> Self {
112 if seconds >= 0.0 {
113 Time::new(seconds.trunc() as u64, seconds.fract())
114 }
115 else {
116 Time::new(0, 0.0)
117 }
118 }
119}
120
121impl From<std::time::Duration> for Time {
122 fn from(duration: std::time::Duration) -> Self {
123 Time::new(duration.as_secs(), f64::from(duration.subsec_nanos()) / 1_000_000_000.0)
124 }
125}
126
127impl From<Time> for std::time::Duration {
128 fn from(time: Time) -> Self {
129 std::time::Duration::new(time.seconds, (1_000_000_000.0 * time.frac) as u32)
130 }
131}
132
133#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
139pub struct TimeBase {
140 pub numer: u32,
142 pub denom: u32,
144}
145
146impl TimeBase {
147 pub fn new(numer: u32, denom: u32) -> Self {
149 if numer == 0 || denom == 0 {
150 panic!("TimeBase cannot have 0 numerator or denominator");
151 }
152
153 TimeBase { numer, denom }
154 }
155
156 pub fn calc_time(&self, ts: TimeStamp) -> Time {
159 assert!(self.numer > 0 && self.denom > 0, "TimeBase numerator or denominator are 0.");
160
161 let dividend = u128::from(ts) * u128::from(self.numer);
163
164 if dividend < (1 << 52) {
171 let seconds = (dividend as f64) / f64::from(self.denom);
172
173 Time::new(seconds.trunc() as u64, seconds.fract())
174 }
175 else {
176 let quotient = dividend / u128::from(self.denom);
179
180 let rem = (dividend - (quotient * u128::from(self.denom))) as u32;
184
185 let frac = f64::from(rem) / f64::from(self.denom);
188
189 Time::new(quotient as u64, frac)
190 }
191 }
192
193 pub fn calc_timestamp(&self, time: Time) -> TimeStamp {
196 assert!(self.numer > 0 && self.denom > 0, "TimeBase numerator or denominator are 0.");
197 assert!(time.frac >= 0.0 && time.frac < 1.0, "Invalid range for Time fractional part.");
198
199 let k = 1.0 / f64::from(self.numer);
201
202 let product = u128::from(time.seconds) * u128::from(self.denom);
205
206 let a = if product > (1 << 52) {
210 let u = ((product & !0xffff_ffff_ffff) >> 48) as u64;
212 let l = ((product & 0xffff_ffff_ffff) >> 0) as u64;
213
214 let uk = (u as f64) * k;
215 let ul = (l as f64) * k;
216
217 ((uk as u64) << 48).wrapping_add(ul as u64)
219 }
220 else {
221 ((product as f64) * k) as u64
222 };
223
224 let b = (k * f64::from(self.denom) * time.frac) as u64;
226
227 a.wrapping_add(b)
228 }
229}
230
231impl From<TimeBase> for f64 {
232 fn from(timebase: TimeBase) -> Self {
233 f64::from(timebase.numer) / f64::from(timebase.denom)
234 }
235}
236
237impl fmt::Display for TimeBase {
238 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239 write!(f, "{}/{}", self.numer, self.denom)
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use super::{Time, TimeBase};
246 use std::time::Duration;
247
248 #[test]
249 fn verify_timebase() {
250 let tb1 = TimeBase::new(1, 320);
252
253 assert_eq!(tb1.calc_time(0), Time::new(0, 0.0));
254 assert_eq!(tb1.calc_time(12_345), Time::new(38, 0.578125));
255 assert_eq!(tb1.calc_time(0x0f_ffff_ffff_ffff), Time::new(14_073_748_835_532, 0.796875));
256 assert_eq!(tb1.calc_time(0x10_0000_0000_0001), Time::new(14_073_748_835_532, 0.803125));
257 assert_eq!(tb1.calc_time(u64::MAX), Time::new(57_646_075_230_342_348, 0.796875));
258
259 let tb2 = TimeBase::new(320, 1);
261 assert_eq!(tb2.calc_time(u64::MAX), Time::new(18_446_744_073_709_551_296, 0.0));
262
263 assert_eq!(tb1.calc_timestamp(Time::new(0, 0.0)), 0);
265 assert_eq!(tb1.calc_timestamp(Time::new(38, 0.578125)), 12_345);
266 assert_eq!(
267 tb1.calc_timestamp(Time::new(14_073_748_835_532, 0.796875)),
268 0x0f_ffff_ffff_ffff
269 );
270 assert_eq!(
271 tb1.calc_timestamp(Time::new(14_073_748_835_532, 0.803125)),
272 0x10_0000_0000_0001
273 );
274 assert_eq!(tb1.calc_timestamp(Time::new(57_646_075_230_342_348, 0.796875)), u64::MAX);
275 }
276
277 #[test]
278 fn verify_duration_to_time() {
279 let dur1 = Duration::from_secs_f64(38.578125);
281 let time1 = Time::from(dur1);
282
283 assert_eq!(time1.seconds, 38);
284 assert_eq!(time1.frac, 0.578125);
285 }
286
287 #[test]
288 fn verify_time_to_duration() {
289 let time1 = Time::new(38, 0.578125);
291 let dur1 = Duration::from(time1);
292
293 let seconds = dur1.as_secs_f64();
294
295 assert_eq!(seconds.trunc(), 38.0);
296 assert_eq!(seconds.fract(), 0.578125);
297 }
298}