Skip to main content

iggy_common/utils/
timestamp.rs

1/* Licensed to the Apache Software Foundation (ASF) under one
2 * or more contributor license agreements.  See the NOTICE file
3 * distributed with this work for additional information
4 * regarding copyright ownership.  The ASF licenses this file
5 * to you under the Apache License, Version 2.0 (the
6 * "License"); you may not use this file except in compliance
7 * with the License.  You may obtain a copy of the License at
8 *
9 *   http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied.  See the License for the
15 * specific language governing permissions and limitations
16 * under the License.
17 */
18
19use chrono::{DateTime, Local, Utc};
20use core::fmt;
21use serde::{
22    Deserialize, Deserializer, Serialize, Serializer,
23    de::{self, Visitor},
24};
25use std::{
26    ops::{Add, Sub},
27    time::{Duration, SystemTime, UNIX_EPOCH},
28};
29
30use crate::IggyDuration;
31
32/// A struct that represents a timestamp.
33///
34/// This struct uses `SystemTime` from `std::time` crate.
35///
36/// # Example
37///
38/// ```
39/// use iggy_common::IggyTimestamp;
40///
41/// let timestamp = IggyTimestamp::from(1694968446131680);
42/// assert_eq!(timestamp.to_utc_string("%Y-%m-%d %H:%M:%S"), "2023-09-17 16:34:06");
43/// assert_eq!(timestamp.as_micros(), 1694968446131680);
44/// ```
45#[derive(Debug, Clone, Copy, Eq, PartialEq)]
46pub struct IggyTimestamp(SystemTime);
47
48pub const UTC_TIME_FORMAT: &str = "%Y-%m-%d %H:%M:%S";
49
50impl IggyTimestamp {
51    pub fn now() -> Self {
52        IggyTimestamp::default()
53    }
54
55    pub fn zero() -> Self {
56        IggyTimestamp(UNIX_EPOCH)
57    }
58
59    pub fn to_secs(&self) -> u64 {
60        self.0.duration_since(UNIX_EPOCH).unwrap().as_secs()
61    }
62
63    pub fn as_micros(&self) -> u64 {
64        self.0.duration_since(UNIX_EPOCH).unwrap().as_micros() as u64
65    }
66
67    pub fn as_millis(&self) -> u64 {
68        self.0.duration_since(UNIX_EPOCH).unwrap().as_millis() as u64
69    }
70
71    pub fn as_nanos(&self) -> u128 {
72        self.0.duration_since(UNIX_EPOCH).unwrap().as_nanos()
73    }
74
75    pub fn to_rfc3339_string(&self) -> String {
76        DateTime::<Utc>::from(self.0).to_rfc3339()
77    }
78
79    pub fn to_utc_string(&self, format: &str) -> String {
80        DateTime::<Utc>::from(self.0).format(format).to_string()
81    }
82
83    pub fn to_local_string(&self, format: &str) -> String {
84        DateTime::<Local>::from(self.0).format(format).to_string()
85    }
86}
87
88impl From<u64> for IggyTimestamp {
89    fn from(timestamp: u64) -> Self {
90        IggyTimestamp(UNIX_EPOCH + Duration::from_micros(timestamp))
91    }
92}
93
94impl From<IggyTimestamp> for u64 {
95    fn from(timestamp: IggyTimestamp) -> u64 {
96        timestamp.as_micros()
97    }
98}
99
100impl From<SystemTime> for IggyTimestamp {
101    fn from(timestamp: SystemTime) -> Self {
102        IggyTimestamp(timestamp)
103    }
104}
105
106impl Add<SystemTime> for IggyTimestamp {
107    type Output = IggyTimestamp;
108
109    fn add(self, other: SystemTime) -> IggyTimestamp {
110        IggyTimestamp(self.0 + other.duration_since(UNIX_EPOCH).unwrap())
111    }
112}
113
114impl Sub<SystemTime> for IggyTimestamp {
115    type Output = IggyTimestamp;
116
117    fn sub(self, rhs: SystemTime) -> Self::Output {
118        IggyTimestamp(self.0 - rhs.duration_since(UNIX_EPOCH).unwrap())
119    }
120}
121
122impl Add<IggyDuration> for IggyTimestamp {
123    type Output = IggyTimestamp;
124
125    fn add(self, other: IggyDuration) -> Self::Output {
126        IggyTimestamp(self.0 + other.get_duration())
127    }
128}
129
130impl Sub for IggyTimestamp {
131    type Output = Duration;
132
133    fn sub(self, rhs: Self) -> Self::Output {
134        self.0
135            .duration_since(rhs.0)
136            .expect("Failed to subtract timestamps rhs < self")
137    }
138}
139
140impl Default for IggyTimestamp {
141    fn default() -> Self {
142        Self(SystemTime::now())
143    }
144}
145
146impl fmt::Display for IggyTimestamp {
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        write!(f, "{}", self.to_utc_string(UTC_TIME_FORMAT))
149    }
150}
151
152impl Serialize for IggyTimestamp {
153    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
154    where
155        S: Serializer,
156    {
157        let timestamp = self.as_micros();
158        serializer.serialize_u64(timestamp)
159    }
160}
161
162impl<'de> Deserialize<'de> for IggyTimestamp {
163    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
164    where
165        D: Deserializer<'de>,
166    {
167        deserializer.deserialize_u64(IggyTimestampVisitor)
168    }
169}
170struct IggyTimestampVisitor;
171
172impl Visitor<'_> for IggyTimestampVisitor {
173    type Value = IggyTimestamp;
174
175    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
176        formatter.write_str("a microsecond timestamp as a u64")
177    }
178
179    fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
180    where
181        E: de::Error,
182    {
183        Ok(IggyTimestamp::from(value))
184    }
185}
186
187#[cfg(test)]
188mod tests {
189    use super::*;
190
191    #[test]
192    fn test_timestamp_get() {
193        let timestamp = IggyTimestamp::now();
194        assert!(timestamp.as_micros() > 0);
195    }
196
197    #[test]
198    fn test_timestamp_to_micros() {
199        let timestamp = IggyTimestamp::from(1663472051111);
200        assert_eq!(timestamp.as_micros(), 1663472051111);
201    }
202
203    #[test]
204    fn test_timestamp_to_string() {
205        let timestamp = IggyTimestamp::from(1694968446131680);
206        assert_eq!(
207            timestamp.to_utc_string("%Y-%m-%d %H:%M:%S"),
208            "2023-09-17 16:34:06"
209        );
210    }
211
212    #[test]
213    fn test_timestamp_from_u64() {
214        let timestamp = IggyTimestamp::from(1663472051111);
215        assert_eq!(timestamp.as_micros(), 1663472051111);
216    }
217}