taos_query/common/
timestamp.rs

1use std::fmt::{self, Debug, Display};
2
3use chrono::Local;
4use serde::{Deserialize, Serialize};
5
6use super::Precision;
7
8#[derive(Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
9pub enum Timestamp {
10    Milliseconds(i64),
11    Microseconds(i64),
12    Nanoseconds(i64),
13}
14
15impl Debug for Timestamp {
16    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17        if f.alternate() {
18            match self {
19                Self::Milliseconds(arg0) => f.debug_tuple("Milliseconds").field(arg0).finish(),
20                Self::Microseconds(arg0) => f.debug_tuple("Microseconds").field(arg0).finish(),
21                Self::Nanoseconds(arg0) => f.debug_tuple("Nanoseconds").field(arg0).finish(),
22            }
23        } else {
24            Debug::fmt(&self.to_naive_datetime(), f)
25        }
26    }
27}
28
29impl Display for Timestamp {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        Display::fmt(&self.to_datetime_with_tz().to_rfc3339(), f)
32    }
33}
34
35impl Timestamp {
36    pub fn new(raw: i64, precision: Precision) -> Self {
37        match precision {
38            Precision::Millisecond => Timestamp::Milliseconds(raw),
39            Precision::Microsecond => Timestamp::Microseconds(raw),
40            Precision::Nanosecond => Timestamp::Nanoseconds(raw),
41        }
42    }
43
44    pub fn precision(&self) -> Precision {
45        match self {
46            Timestamp::Milliseconds(_) => Precision::Millisecond,
47            Timestamp::Microseconds(_) => Precision::Microsecond,
48            Timestamp::Nanoseconds(_) => Precision::Nanosecond,
49        }
50    }
51    pub fn as_raw_i64(&self) -> i64 {
52        match self {
53            Timestamp::Milliseconds(raw)
54            | Timestamp::Microseconds(raw)
55            | Timestamp::Nanoseconds(raw) => *raw,
56        }
57    }
58    pub fn to_naive_datetime(&self) -> chrono::NaiveDateTime {
59        let duration = match self {
60            Timestamp::Milliseconds(raw) => chrono::Duration::milliseconds(*raw),
61            Timestamp::Microseconds(raw) => chrono::Duration::microseconds(*raw),
62            Timestamp::Nanoseconds(raw) => chrono::Duration::nanoseconds(*raw),
63        };
64        chrono::DateTime::from_timestamp(0, 0)
65            .expect("timestamp value could always be mapped to a chrono::NaiveDateTime")
66            .checked_add_signed(duration)
67            .unwrap()
68            .naive_utc()
69    }
70
71    // todo: support to tz.
72    pub fn to_datetime_with_tz(&self) -> chrono::DateTime<Local> {
73        use chrono::TimeZone;
74        Local.from_utc_datetime(&self.to_naive_datetime())
75    }
76
77    pub fn cast_precision(&self, precision: Precision) -> Timestamp {
78        let raw = self.as_raw_i64();
79        match (self.precision(), precision) {
80            (Precision::Millisecond, Precision::Microsecond) => Timestamp::Microseconds(raw * 1000),
81            (Precision::Millisecond, Precision::Nanosecond) => {
82                Timestamp::Nanoseconds(raw * 1_000_000)
83            }
84            (Precision::Microsecond, Precision::Millisecond) => Timestamp::Milliseconds(raw / 1000),
85            (Precision::Microsecond, Precision::Nanosecond) => Timestamp::Nanoseconds(raw * 1000),
86            (Precision::Nanosecond, Precision::Millisecond) => {
87                Timestamp::Milliseconds(raw / 1_000_000)
88            }
89            (Precision::Nanosecond, Precision::Microsecond) => Timestamp::Microseconds(raw / 1000),
90            _ => Timestamp::new(raw, precision),
91        }
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn ts_new() {
101        use Precision::*;
102        for prec in [Millisecond, Microsecond, Nanosecond] {
103            let ts = Timestamp::new(0, prec);
104            assert!(ts.as_raw_i64() == 0);
105            assert!(
106                ts.to_naive_datetime()
107                    == chrono::DateTime::from_timestamp(0, 0).unwrap().naive_utc()
108            );
109            dbg!(ts.to_datetime_with_tz());
110        }
111    }
112
113    #[test]
114    fn ts_cast_precision() {
115        let precisions = [
116            Precision::Millisecond,
117            Precision::Microsecond,
118            Precision::Nanosecond,
119        ];
120        for (i, prec) in precisions.iter().enumerate() {
121            let ts = Timestamp::new(1_000_000 * (i as i64), *prec);
122            for (_j, new_prec) in precisions.iter().enumerate() {
123                let new_ts = ts.cast_precision(*new_prec);
124                assert_eq!(
125                    new_ts.precision(),
126                    *new_prec,
127                    "from {:?} to {:?}",
128                    prec,
129                    new_prec
130                );
131                assert_eq!(
132                    new_ts.to_naive_datetime(),
133                    ts.to_naive_datetime(),
134                    "from {:?} to {:?}",
135                    prec,
136                    new_prec
137                );
138                match (prec, new_prec) {
139                    (Precision::Millisecond, Precision::Microsecond) => {
140                        assert_eq!(new_ts.as_raw_i64(), 1_000_000 * (i as i64) * 1000);
141                    }
142                    (Precision::Millisecond, Precision::Nanosecond) => {
143                        assert_eq!(new_ts.as_raw_i64(), 1_000_000 * (i as i64) * 1_000_000);
144                    }
145                    (Precision::Microsecond, Precision::Millisecond) => {
146                        assert_eq!(new_ts.as_raw_i64(), 1_000_000 * (i as i64) / 1000);
147                    }
148                    (Precision::Microsecond, Precision::Nanosecond) => {
149                        assert_eq!(new_ts.as_raw_i64(), 1_000_000 * (i as i64) * 1000);
150                    }
151                    (Precision::Nanosecond, Precision::Millisecond) => {
152                        assert_eq!(new_ts.as_raw_i64(), 1_000_000 * (i as i64) / 1_000_000);
153                    }
154                    (Precision::Nanosecond, Precision::Microsecond) => {
155                        assert_eq!(new_ts.as_raw_i64(), 1_000_000 * (i as i64) / 1000);
156                    }
157                    _ => {
158                        assert_eq!(new_ts.as_raw_i64(), 1_000_000 * (i as i64));
159                    }
160                }
161            }
162        }
163    }
164
165    #[test]
166    fn ts_debug() {
167        let ts = Timestamp::new(0, Precision::Millisecond);
168        assert_eq!(format!("{:?}", ts), "1970-01-01T00:00:00");
169        assert_eq!(format!("{:#?}", ts), "Milliseconds(\n    0,\n)");
170        assert_eq!(format!("{}", ts), "1970-01-01T08:00:00+08:00");
171    }
172}