Skip to main content

display_more/
display_unix_epoch.rs

1// Copyright 2021 Datafuse Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::fmt;
16use std::time::Duration;
17use std::time::UNIX_EPOCH;
18
19use chrono::DateTime;
20use chrono::Utc;
21
22pub struct DisplayUnixTimeStamp {
23    /// The duration since the UNIX epoch.
24    duration: Option<Duration>,
25
26    in_millis: bool,
27
28    with_timezone: bool,
29}
30
31impl fmt::Display for DisplayUnixTimeStamp {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        let duration = match self.duration {
34            Some(d) => d,
35            None => return write!(f, "None"),
36        };
37
38        let system_time = UNIX_EPOCH + duration;
39        let datetime: DateTime<Utc> = system_time.into();
40
41        let fmt = if self.in_millis {
42            if self.with_timezone {
43                "%Y-%m-%dT%H:%M:%S%.3fZ%z"
44            } else {
45                "%Y-%m-%dT%H:%M:%S%.3f"
46            }
47        } else if self.with_timezone {
48            "%Y-%m-%dT%H:%M:%S%.6fZ%z"
49        } else {
50            "%Y-%m-%dT%H:%M:%S%.6f"
51        };
52
53        write!(f, "{}", datetime.format(fmt))
54    }
55}
56
57impl DisplayUnixTimeStamp {
58    pub fn new(duration: Option<Duration>) -> Self {
59        Self {
60            duration,
61            in_millis: false,
62            with_timezone: true,
63        }
64    }
65
66    pub fn in_millis(self, in_millis: bool) -> Self {
67        Self { in_millis, ..self }
68    }
69
70    pub fn with_timezone(self, with_timezone: bool) -> Self {
71        Self {
72            with_timezone,
73            ..self
74        }
75    }
76}
77
78/// Implement `Display` for `Duration` to display the duration since the UNIX epoch.
79///
80/// # Example
81///
82/// ```rust
83/// use std::time::Duration;
84///
85/// use display_more::DisplayUnixTimeStampExt;
86///
87/// let duration = Duration::from_millis(1723102819023);
88/// assert_eq!(
89///     duration.display_unix_timestamp().to_string(),
90///     "2024-08-08T07:40:19.023000Z+0000"
91/// );
92/// ```
93pub trait DisplayUnixTimeStampExt {
94    fn display_unix_timestamp(&self) -> DisplayUnixTimeStamp;
95
96    /// Display the duration since the UNIX epoch in milliseconds without timezone.
97    fn display_unix_timestamp_short(&self) -> DisplayUnixTimeStamp {
98        self.display_unix_timestamp()
99            .in_millis(true)
100            .with_timezone(false)
101    }
102}
103
104impl DisplayUnixTimeStampExt for Duration {
105    fn display_unix_timestamp(&self) -> DisplayUnixTimeStamp {
106        DisplayUnixTimeStamp::new(Some(*self))
107    }
108}
109
110impl DisplayUnixTimeStampExt for Option<Duration> {
111    fn display_unix_timestamp(&self) -> DisplayUnixTimeStamp {
112        DisplayUnixTimeStamp::new(*self)
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use std::time::Duration;
119
120    use super::*;
121
122    #[test]
123    fn test_display_unix_epoch() {
124        let epoch = Duration::from_millis(0);
125        let display = epoch.display_unix_timestamp();
126        assert_eq!(format!("{}", display), "1970-01-01T00:00:00.000000Z+0000");
127
128        let epoch = Duration::from_millis(1723102819023);
129        let display = epoch.display_unix_timestamp();
130        assert_eq!(format!("{}", display), "2024-08-08T07:40:19.023000Z+0000");
131
132        let display = epoch.display_unix_timestamp_short();
133        assert_eq!(format!("{}", display), "2024-08-08T07:40:19.023");
134
135        // Option<Duration>: Some
136        let some = Some(Duration::from_millis(1723102819023));
137        assert_eq!(
138            some.display_unix_timestamp().to_string(),
139            "2024-08-08T07:40:19.023000Z+0000"
140        );
141        assert_eq!(
142            some.display_unix_timestamp_short().to_string(),
143            "2024-08-08T07:40:19.023"
144        );
145
146        // Option<Duration>: None
147        let none: Option<Duration> = None;
148        assert_eq!(none.display_unix_timestamp().to_string(), "None");
149        assert_eq!(none.display_unix_timestamp_short().to_string(), "None");
150    }
151}