goog_cc/api/units/
timestamp.rs

1/*
2 *  Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11//! Timestamp represents the time that has passed since some unspecified epoch.
12//! The epoch is assumed to be before any represented timestamps, this means that
13//! negative values are not valid. The most notable feature is that the
14//! difference of two Timestamps results in a TimeDelta.
15super::unit_base!(Timestamp);
16
17use std::{fmt, sync::LazyLock, time::{Duration, Instant}};
18use std::ops::*;
19
20use super::TimeDelta;
21
22impl Timestamp {
23    const ONE_SIDED: bool = false;
24
25    pub const fn from_seconds(value: i64) -> Self {
26        Self::from_fraction(1_000_000, value)
27    }
28
29    pub fn from_seconds_float(value: f64) -> Self {
30        Self::from_fraction_float(1_000_000.0, value)
31    }
32
33    pub const fn from_millis(value: i64) -> Self {
34        Self::from_fraction(1_000, value)
35    }
36
37    pub fn from_millis_float(value: f64) -> Self {
38        Self::from_fraction_float(1_000.0, value)
39    }
40
41    pub const fn from_micros(value: i64) -> Self {
42        Self::from_value(value)
43    }
44
45    pub fn from_micros_float(value: f64) -> Self {
46        Self::from_value_float(value)
47    }
48
49    pub const fn seconds(&self) -> i64 {
50        self.to_fraction(1_000_000)
51    }
52
53    pub fn seconds_float(&self) -> f64 {
54        self.to_fraction_float(1_000_000.0)
55    }
56
57    pub const fn ms(&self) -> i64 {
58        self.to_fraction(1_000)
59    }
60
61    pub fn ms_float(&self) -> f64 {
62        self.to_fraction_float(1_000.0)
63    }
64
65    pub const fn us(&self) -> i64 {
66        self.to_value()
67    }
68
69    pub const fn us_float(&self) -> f64 {
70        self.to_value_float()
71    }
72
73    pub const fn seconds_or(&self, fallback_value: i64) -> i64 {
74        self.to_fraction_or(1_000_000, fallback_value)
75    }
76
77    pub const fn ms_or(&self, fallback_value: i64) -> i64 {
78        self.to_fraction_or(1_000, fallback_value)
79    }
80
81    pub const fn us_or(&self, fallback_value: i64) -> i64 {
82        self.to_value_or(fallback_value)
83    }
84}
85
86impl Add<TimeDelta> for Timestamp {
87    type Output = Self;
88
89    fn add(self, delta: TimeDelta) -> Self {
90        if self.is_plus_infinity() || delta.is_plus_infinity() {
91            assert!(!self.is_minus_infinity());
92            assert!(!delta.is_minus_infinity());
93            return Self::plus_infinity();
94        } else if self.is_minus_infinity() || delta.is_minus_infinity() {
95            assert!(!self.is_plus_infinity());
96            assert!(!delta.is_plus_infinity());
97            return Self::minus_infinity();
98        }
99        Timestamp::from_micros(self.us() + delta.us())
100    }
101}
102
103impl Sub<TimeDelta> for Timestamp {
104    type Output = Self;
105
106    fn sub(self, delta: TimeDelta) -> Self {
107        if self.is_plus_infinity() || delta.is_minus_infinity() {
108            assert!(!self.is_minus_infinity());
109            assert!(!delta.is_plus_infinity());
110            return Self::plus_infinity();
111        } else if self.is_minus_infinity() || delta.is_plus_infinity() {
112            assert!(!self.is_plus_infinity());
113            assert!(!delta.is_minus_infinity());
114            return Self::minus_infinity();
115        }
116        Timestamp::from_micros(self.us() - delta.us())
117    }
118}
119
120impl Sub for Timestamp {
121    type Output = TimeDelta;
122
123    fn sub(self, other: Self) -> TimeDelta {
124        if self.is_plus_infinity() || other.is_minus_infinity() {
125            assert!(!self.is_minus_infinity());
126            assert!(!other.is_plus_infinity());
127            return TimeDelta::plus_infinity();
128        } else if self.is_minus_infinity() || other.is_plus_infinity() {
129            assert!(!self.is_plus_infinity());
130            assert!(!other.is_minus_infinity());
131            return TimeDelta::minus_infinity();
132        }
133        TimeDelta::from_micros(self.us() - other.us())
134    }
135}
136
137impl AddAssign<TimeDelta> for Timestamp {
138    fn add_assign(&mut self, delta: TimeDelta) {
139        *self = *self + delta;
140    }
141}
142
143impl SubAssign<TimeDelta> for Timestamp {
144    fn sub_assign(&mut self, delta: TimeDelta) {
145        *self = *self - delta;
146    }
147}
148
149impl fmt::Debug for Timestamp {
150    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
151        if self.is_plus_infinity() {
152            write!(f, "+inf ms")
153        } else if self.is_minus_infinity() {
154            write!(f, "-inf ms")
155        } else if self.us() == 0 || (self.us() % 1000) != 0 {
156            write!(f, "{} us", self.us())
157        } else if self.ms() % 1000 != 0 {
158            write!(f, "{} ms", self.ms())
159        } else {
160            write!(f, "{} s", self.seconds())
161        }
162    }
163}
164
165// Custom
166impl From<Instant> for Timestamp {
167    fn from(instant: Instant) -> Self {
168        let r = *REF;
169        if let Some(after) = instant.checked_duration_since(r) {
170            Timestamp::from_micros(after.as_micros() as i64)
171        } else if let Some(before) = r.checked_duration_since(instant) {
172            Timestamp::from_micros(-(before.as_micros() as i64))
173        } else {
174            Timestamp::zero()
175        }
176    }
177}
178
179impl From<Timestamp> for Instant {
180    fn from(timestamp: Timestamp) -> Self {
181        let r = *REF;
182        let duration = Duration::from_micros(timestamp.us().unsigned_abs());
183
184        if timestamp.0 > 0 {
185            r.checked_add(duration).expect("Timestamp is too far in the future")
186        } else {
187            r.checked_sub(duration).expect("Timestamp is from before the program started")
188        }
189    }
190}
191
192static REF: LazyLock<std::time::Instant> = LazyLock::new(|| {
193    std::time::Instant::now()
194});
195
196
197#[cfg(test)]
198mod test {
199    use super::*;
200
201    #[test]
202    fn const_expr() {
203        const VALUE: i64 = 12345;
204        const TIMESTAMP_INF: Timestamp = Timestamp::plus_infinity();
205        assert!(TIMESTAMP_INF.is_infinite());
206        assert!(TIMESTAMP_INF.ms_or(-1) == -1);
207
208        const TIMESTAMP_SECONDS: Timestamp = Timestamp::from_seconds(VALUE);
209        const TIMESTAMP_MS: Timestamp = Timestamp::from_millis(VALUE);
210        const TIMESTAMP_US: Timestamp = Timestamp::from_micros(VALUE);
211
212        assert!(TIMESTAMP_SECONDS.seconds_or(0) == VALUE);
213        assert!(TIMESTAMP_MS.ms_or(0) == VALUE);
214        assert!(TIMESTAMP_US.us_or(0) == VALUE);
215
216        assert!(TIMESTAMP_MS > TIMESTAMP_US);
217
218        assert_eq!(TIMESTAMP_SECONDS.seconds(), VALUE);
219        assert_eq!(TIMESTAMP_MS.ms(), VALUE);
220        assert_eq!(TIMESTAMP_US.us(), VALUE);
221    }
222
223    #[test]
224    fn get_back_same_values() {
225        const VALUE: i64 = 499;
226        assert_eq!(Timestamp::from_millis(VALUE).ms(), VALUE);
227        assert_eq!(Timestamp::from_micros(VALUE).us(), VALUE);
228        assert_eq!(Timestamp::from_seconds(VALUE).seconds(), VALUE);
229    }
230
231    #[test]
232    fn get_different_prefix() {
233        const VALUE: i64 = 3000000;
234        assert_eq!(Timestamp::from_micros(VALUE).seconds(), VALUE / 1000000);
235        assert_eq!(Timestamp::from_millis(VALUE).seconds(), VALUE / 1000);
236        assert_eq!(Timestamp::from_micros(VALUE).ms(), VALUE / 1000);
237
238        assert_eq!(Timestamp::from_millis(VALUE).us(), VALUE * 1000);
239        assert_eq!(Timestamp::from_seconds(VALUE).ms(), VALUE * 1000);
240        assert_eq!(Timestamp::from_seconds(VALUE).us(), VALUE * 1000000);
241    }
242
243    #[test]
244    fn identity_checks() {
245        const VALUE: i64 = 3000;
246
247        assert!(Timestamp::plus_infinity().is_infinite());
248        assert!(Timestamp::minus_infinity().is_infinite());
249        assert!(!Timestamp::from_millis(VALUE).is_infinite());
250
251        assert!(!Timestamp::plus_infinity().is_finite());
252        assert!(!Timestamp::minus_infinity().is_finite());
253        assert!(Timestamp::from_millis(VALUE).is_finite());
254
255        assert!(Timestamp::plus_infinity().is_plus_infinity());
256        assert!(!Timestamp::minus_infinity().is_plus_infinity());
257
258        assert!(Timestamp::minus_infinity().is_minus_infinity());
259        assert!(!Timestamp::plus_infinity().is_minus_infinity());
260    }
261
262    #[test]
263    fn comparison_operators() {
264        const SMALL: i64 = 450;
265        const LARGE: i64 = 451;
266
267        assert_eq!(Timestamp::plus_infinity(), Timestamp::plus_infinity());
268        assert!(Timestamp::plus_infinity() >= Timestamp::plus_infinity());
269        assert!(Timestamp::plus_infinity() > Timestamp::from_millis(LARGE));
270        assert_eq!(Timestamp::from_millis(SMALL), Timestamp::from_millis(SMALL));
271        assert!(Timestamp::from_millis(SMALL) <= Timestamp::from_millis(SMALL));
272        assert!(Timestamp::from_millis(SMALL) >= Timestamp::from_millis(SMALL));
273        assert!(Timestamp::from_millis(SMALL) != Timestamp::from_millis(LARGE));
274        assert!(Timestamp::from_millis(SMALL) <= Timestamp::from_millis(LARGE));
275        assert!(Timestamp::from_millis(SMALL) < Timestamp::from_millis(LARGE));
276        assert!(Timestamp::from_millis(LARGE) >= Timestamp::from_millis(SMALL));
277        assert!(Timestamp::from_millis(LARGE) > Timestamp::from_millis(SMALL));
278    }
279
280    #[test]
281    fn can_be_inititialized_from_large_int() {
282        const MAX_INT: i32 = i32::MAX;
283        assert_eq!(
284            Timestamp::from_seconds(MAX_INT as _).us(),
285            MAX_INT as i64 * 1000000
286        );
287        assert_eq!(
288            Timestamp::from_millis(MAX_INT as _).us(),
289            MAX_INT as i64 * 1000
290        );
291    }
292
293    #[test]
294    fn converts_to_and_from_double() {
295        const MICROS: i64 = 17017;
296        const MICROS_DOUBLE: f64 = MICROS as f64;
297        const MILLIS_DOUBLE: f64 = MICROS as f64 * 1e-3;
298        const SECONDS_DOUBLE: f64 = MILLIS_DOUBLE * 1e-3;
299
300        assert_eq!(
301            Timestamp::from_micros(MICROS).seconds_float(),
302            SECONDS_DOUBLE
303        );
304        assert_eq!(Timestamp::from_seconds_float(SECONDS_DOUBLE).us(), MICROS);
305
306        assert_eq!(Timestamp::from_micros(MICROS).ms_float(), MILLIS_DOUBLE);
307        assert_eq!(Timestamp::from_millis_float(MILLIS_DOUBLE).us(), MICROS);
308
309        assert_eq!(Timestamp::from_micros(MICROS).us_float(), MICROS_DOUBLE);
310        assert_eq!(Timestamp::from_micros_float(MICROS_DOUBLE).us(), MICROS);
311
312        const PLUS_INFINITY: f64 = f64::INFINITY;
313        const MINUS_INFINITY: f64 = -PLUS_INFINITY;
314
315        assert_eq!(Timestamp::plus_infinity().seconds_float(), PLUS_INFINITY);
316        assert_eq!(Timestamp::minus_infinity().seconds_float(), MINUS_INFINITY);
317        assert_eq!(Timestamp::plus_infinity().ms_float(), PLUS_INFINITY);
318        assert_eq!(Timestamp::minus_infinity().ms_float(), MINUS_INFINITY);
319        assert_eq!(Timestamp::plus_infinity().us_float(), PLUS_INFINITY);
320        assert_eq!(Timestamp::minus_infinity().us_float(), MINUS_INFINITY);
321
322        assert!(Timestamp::from_seconds_float(PLUS_INFINITY).is_plus_infinity());
323        assert!(Timestamp::from_seconds_float(MINUS_INFINITY).is_minus_infinity());
324        assert!(Timestamp::from_millis_float(PLUS_INFINITY).is_plus_infinity());
325        assert!(Timestamp::from_millis_float(MINUS_INFINITY).is_minus_infinity());
326        assert!(Timestamp::from_micros_float(PLUS_INFINITY).is_plus_infinity());
327        assert!(Timestamp::from_micros_float(MINUS_INFINITY).is_minus_infinity());
328    }
329
330    #[test]
331    fn timestamp_and_time_delta_math() {
332        const VALUE_A: i64 = 267;
333        const VALUE_B: i64 = 450;
334        const TIME_A: Timestamp = Timestamp::from_millis(VALUE_A);
335        const TIME_B: Timestamp = Timestamp::from_millis(VALUE_B);
336        const DELTA_A: TimeDelta = TimeDelta::from_millis(VALUE_A);
337        const DELTA_B: TimeDelta = TimeDelta::from_millis(VALUE_B);
338
339        assert_eq!((TIME_A - TIME_B), TimeDelta::from_millis(VALUE_A - VALUE_B));
340        assert_eq!(
341            (TIME_B - DELTA_A),
342            Timestamp::from_millis(VALUE_B - VALUE_A)
343        );
344        assert_eq!(
345            (TIME_B + DELTA_A),
346            Timestamp::from_millis(VALUE_B + VALUE_A)
347        );
348
349        let mut mutable_time: Timestamp = TIME_A;
350        mutable_time += DELTA_B;
351        assert_eq!(mutable_time, TIME_A + DELTA_B);
352        mutable_time -= DELTA_B;
353        assert_eq!(mutable_time, TIME_A);
354    }
355
356    #[test]
357    fn infinity_operations() {
358        const VALUE: i64 = 267;
359        const FINITE_TIME: Timestamp = Timestamp::from_millis(VALUE);
360        const FINITE_DELTA: TimeDelta = TimeDelta::from_millis(VALUE);
361        assert!((Timestamp::plus_infinity() + FINITE_DELTA).is_infinite());
362        assert!((Timestamp::plus_infinity() - FINITE_DELTA).is_infinite());
363        assert!((FINITE_TIME + TimeDelta::plus_infinity()).is_infinite());
364        assert!((FINITE_TIME - TimeDelta::minus_infinity()).is_infinite());
365    }
366} // namespace test