1 //
2 //  Copyright 2023 Google, Inc.
3 //
4 //  Licensed under the Apache License, Version 2.0 (the "License");
5 //  you may not use this file except in compliance with the License.
6 //  You may obtain a copy of the License at:
7 //
8 //  http://www.apache.org/licenses/LICENSE-2.0
9 //
10 //  Unless required by applicable law or agreed to in writing, software
11 //  distributed under the License is distributed on an "AS IS" BASIS,
12 //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 //  See the License for the specific language governing permissions and
14 //  limitations under the License.
15 
16 //! # Time Display class
17 
18 use std::time::{SystemTime, UNIX_EPOCH};
19 
20 use chrono::{DateTime, Datelike, NaiveDateTime, Timelike, Utc};
21 
22 /// A simple class that contains information required to display time
23 pub struct TimeDisplay {
24     /// seconds since std::time::UNIX_EPOCH
25     secs: i64,
26     /// nano sub seconds since std::time::UNIX_EPOCH
27     nsecs: u32,
28 }
29 
30 impl TimeDisplay {
31     /// Creates a new TimeDisplay with given secs and nsecs
32     ///
33     /// # Arguments
34     ///
35     /// * `secs` - seconds since std::time::UNIX_EPOCH
36     /// * `nsecs` - nano sub seconds since std::time::UNIX_EPOCH
new(secs: i64, nsecs: u32) -> TimeDisplay37     pub fn new(secs: i64, nsecs: u32) -> TimeDisplay {
38         TimeDisplay { secs, nsecs }
39     }
40 
41     /// Displays date & time in UTC with a format YYYY-MM-DD-HH:MM:SS
42     ///
43     /// # Returns
44     ///
45     /// `String` display of utc time.
utc_display(&self) -> String46     pub fn utc_display(&self) -> String {
47         if let Some(datetime) = NaiveDateTime::from_timestamp_opt(self.secs, self.nsecs) {
48             let current_datetime = DateTime::<Utc>::from_naive_utc_and_offset(datetime, Utc);
49             return format!(
50                 "{}-{:02}-{:02}-{:02}-{:02}-{:02}",
51                 current_datetime.year(),
52                 current_datetime.month(),
53                 current_datetime.day(),
54                 current_datetime.hour(),
55                 current_datetime.minute(),
56                 current_datetime.second()
57             );
58         }
59         "INVALID-TIMESTAMP".to_string()
60     }
61 
62     /// Displays time in UTC without date with a format HH:MM:SS
63     ///
64     /// # Returns
65     ///
66     /// `Ok(String)` if the display was successful, `Error` otherwise.
utc_display_hms(&self) -> String67     pub fn utc_display_hms(&self) -> String {
68         if let Some(datetime) = NaiveDateTime::from_timestamp_opt(self.secs, self.nsecs) {
69             let current_datetime = DateTime::<Utc>::from_naive_utc_and_offset(datetime, Utc);
70             return format!(
71                 "{:02}:{:02}:{:02}",
72                 current_datetime.hour(),
73                 current_datetime.minute(),
74                 current_datetime.second()
75             );
76         }
77         "INVALID".to_string()
78     }
79 
80     /// Displays time in UTC for logs
utc_display_log(&self) -> String81     fn utc_display_log(&self) -> String {
82         if let Some(datetime) = NaiveDateTime::from_timestamp_opt(self.secs, self.nsecs) {
83             let current_datetime = DateTime::<Utc>::from_naive_utc_and_offset(datetime, Utc);
84             return format!(
85                 "{:02}-{:02} {:02}:{:02}:{:02}.{:.3}",
86                 current_datetime.month(),
87                 current_datetime.day(),
88                 current_datetime.hour(),
89                 current_datetime.minute(),
90                 current_datetime.second(),
91                 current_datetime.timestamp_subsec_nanos().to_string(),
92             );
93         }
94         "INVALID-TIMESTAMP".to_string()
95     }
96 }
97 
98 // Get TimeDisplay of current_time
get_current_time() -> TimeDisplay99 fn get_current_time() -> TimeDisplay {
100     let since_epoch = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards");
101     TimeDisplay::new(since_epoch.as_secs() as i64, since_epoch.subsec_nanos())
102 }
103 
104 /// Return the timestamp of the current time for logs
log_current_time() -> String105 pub fn log_current_time() -> String {
106     get_current_time().utc_display_log()
107 }
108 
109 /// Return the timestamp of the current time for files
file_current_time() -> String110 pub fn file_current_time() -> String {
111     get_current_time().utc_display()
112 }
113 
114 #[cfg(test)]
115 mod tests {
116 
117     use super::TimeDisplay;
118 
119     #[test]
test_utc_display_ok()120     fn test_utc_display_ok() {
121         let epoch_time = TimeDisplay::new(0, 0);
122         let utc_epoch = epoch_time.utc_display();
123         assert_eq!(utc_epoch, "1970-01-01-00-00-00");
124         let twok_time = TimeDisplay::new(946684900, 0);
125         let utc_twok = twok_time.utc_display();
126         assert_eq!(utc_twok, "2000-01-01-00-01-40");
127     }
128 
129     #[test]
test_utc_display_err()130     fn test_utc_display_err() {
131         let max_seconds = TimeDisplay::new(i64::MAX, 0);
132         assert_eq!("INVALID-TIMESTAMP", max_seconds.utc_display());
133         let max_nanos = TimeDisplay::new(0, 2_000_000_000);
134         assert_eq!("INVALID-TIMESTAMP", max_nanos.utc_display());
135     }
136 
137     #[test]
test_utc_display_hms()138     fn test_utc_display_hms() {
139         let epoch_time = TimeDisplay::new(0, 0);
140         let utc_epoch = epoch_time.utc_display_hms();
141         assert_eq!(utc_epoch, "00:00:00");
142         let twok_time = TimeDisplay::new(946684900, 0);
143         let utc_twok = twok_time.utc_display_hms();
144         assert_eq!(utc_twok, "00:01:40");
145     }
146 
147     #[test]
test_utc_display_log()148     fn test_utc_display_log() {
149         let epoch_time = TimeDisplay::new(0, 0);
150         let utc_epoch = epoch_time.utc_display_log();
151         assert_eq!(utc_epoch, "01-01 00:00:00.0");
152         let twok_time = TimeDisplay::new(946684900, 200);
153         let utc_twok = twok_time.utc_display_log();
154         assert_eq!(utc_twok, "01-01 00:01:40.200");
155     }
156 }
157