firebase_rs_sdk/firestore/model/
timestamp.rs

1use std::cmp::Ordering;
2use std::time::{Duration, SystemTime, UNIX_EPOCH};
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
5pub struct Timestamp {
6    pub seconds: i64,
7    pub nanos: i32,
8}
9
10impl Timestamp {
11    pub fn new(seconds: i64, nanos: i32) -> Self {
12        let mut timestamp = Self { seconds, nanos };
13        timestamp.normalize();
14        timestamp
15    }
16
17    pub fn now() -> Self {
18        let duration = SystemTime::now()
19            .duration_since(UNIX_EPOCH)
20            .unwrap_or_else(|_| Duration::from_secs(0));
21        Self {
22            seconds: duration.as_secs() as i64,
23            nanos: duration.subsec_nanos() as i32,
24        }
25    }
26
27    pub fn from_system_time(time: SystemTime) -> Self {
28        match time.duration_since(UNIX_EPOCH) {
29            Ok(duration) => Self {
30                seconds: duration.as_secs() as i64,
31                nanos: duration.subsec_nanos() as i32,
32            },
33            Err(err) => {
34                let duration = err.duration();
35                Self {
36                    seconds: -(duration.as_secs() as i64),
37                    nanos: -(duration.subsec_nanos() as i32),
38                }
39            }
40        }
41    }
42
43    pub fn to_system_time(&self) -> SystemTime {
44        if self.seconds >= 0 {
45            UNIX_EPOCH
46                + Duration::from_secs(self.seconds as u64)
47                + Duration::from_nanos(self.nanos as u64)
48        } else {
49            UNIX_EPOCH
50                - Duration::from_secs((-self.seconds) as u64)
51                - Duration::from_nanos(self.nanos.unsigned_abs() as u64)
52        }
53    }
54
55    fn normalize(&mut self) {
56        let extra_seconds = self.nanos.div_euclid(1_000_000_000);
57        self.seconds += extra_seconds as i64;
58        self.nanos = self.nanos.rem_euclid(1_000_000_000);
59        if self.seconds > 0 && self.nanos < 0 {
60            self.seconds -= 1;
61            self.nanos += 1_000_000_000;
62        }
63    }
64}
65
66impl PartialOrd for Timestamp {
67    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
68        Some(self.cmp(other))
69    }
70}
71
72impl Ord for Timestamp {
73    fn cmp(&self, other: &Self) -> Ordering {
74        match self.seconds.cmp(&other.seconds) {
75            Ordering::Equal => self.nanos.cmp(&other.nanos),
76            ordering => ordering,
77        }
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    #[test]
86    fn normalize_nanoseconds() {
87        let timestamp = Timestamp::new(1, 1_500_000_000);
88        assert_eq!(timestamp.seconds, 2);
89        assert_eq!(timestamp.nanos, 500_000_000);
90    }
91
92    #[test]
93    fn ordering() {
94        let earlier = Timestamp::new(1, 0);
95        let later = Timestamp::new(2, 0);
96        assert!(earlier < later);
97    }
98}