| /* Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| use chrono::{DateTime, Local, Utc}; |
| use core::fmt; |
| use serde::{ |
| Deserialize, Deserializer, Serialize, Serializer, |
| de::{self, Visitor}, |
| }; |
| use std::{ |
| ops::{Add, Sub}, |
| time::{Duration, SystemTime, UNIX_EPOCH}, |
| }; |
| |
| use crate::IggyDuration; |
| |
| /// A struct that represents a timestamp. |
| /// |
| /// This struct uses `SystemTime` from `std::time` crate. |
| /// |
| /// # Example |
| /// |
| /// ``` |
| /// use iggy_common::IggyTimestamp; |
| /// |
| /// let timestamp = IggyTimestamp::from(1694968446131680); |
| /// assert_eq!(timestamp.to_utc_string("%Y-%m-%d %H:%M:%S"), "2023-09-17 16:34:06"); |
| /// assert_eq!(timestamp.as_micros(), 1694968446131680); |
| /// ``` |
| #[derive(Debug, Clone, Copy, Eq, PartialEq)] |
| pub struct IggyTimestamp(SystemTime); |
| |
| pub const UTC_TIME_FORMAT: &str = "%Y-%m-%d %H:%M:%S"; |
| |
| impl IggyTimestamp { |
| pub fn now() -> Self { |
| IggyTimestamp::default() |
| } |
| |
| pub fn zero() -> Self { |
| IggyTimestamp(UNIX_EPOCH) |
| } |
| |
| pub fn to_secs(&self) -> u64 { |
| self.0.duration_since(UNIX_EPOCH).unwrap().as_secs() |
| } |
| |
| pub fn as_micros(&self) -> u64 { |
| self.0.duration_since(UNIX_EPOCH).unwrap().as_micros() as u64 |
| } |
| |
| pub fn to_rfc3339_string(&self) -> String { |
| DateTime::<Utc>::from(self.0).to_rfc3339() |
| } |
| |
| pub fn to_utc_string(&self, format: &str) -> String { |
| DateTime::<Utc>::from(self.0).format(format).to_string() |
| } |
| |
| pub fn to_local_string(&self, format: &str) -> String { |
| DateTime::<Local>::from(self.0).format(format).to_string() |
| } |
| } |
| |
| impl From<u64> for IggyTimestamp { |
| fn from(timestamp: u64) -> Self { |
| IggyTimestamp(UNIX_EPOCH + Duration::from_micros(timestamp)) |
| } |
| } |
| |
| impl From<IggyTimestamp> for u64 { |
| fn from(timestamp: IggyTimestamp) -> u64 { |
| timestamp.as_micros() |
| } |
| } |
| |
| impl From<SystemTime> for IggyTimestamp { |
| fn from(timestamp: SystemTime) -> Self { |
| IggyTimestamp(timestamp) |
| } |
| } |
| |
| impl Add<SystemTime> for IggyTimestamp { |
| type Output = IggyTimestamp; |
| |
| fn add(self, other: SystemTime) -> IggyTimestamp { |
| IggyTimestamp(self.0 + other.duration_since(UNIX_EPOCH).unwrap()) |
| } |
| } |
| |
| impl Sub<SystemTime> for IggyTimestamp { |
| type Output = IggyTimestamp; |
| |
| fn sub(self, rhs: SystemTime) -> Self::Output { |
| IggyTimestamp(self.0 - rhs.duration_since(UNIX_EPOCH).unwrap()) |
| } |
| } |
| |
| impl Add<IggyDuration> for IggyTimestamp { |
| type Output = IggyTimestamp; |
| |
| fn add(self, other: IggyDuration) -> Self::Output { |
| IggyTimestamp(self.0 + other.get_duration()) |
| } |
| } |
| |
| impl Sub for IggyTimestamp { |
| type Output = Duration; |
| |
| fn sub(self, rhs: Self) -> Self::Output { |
| self.0 |
| .duration_since(rhs.0) |
| .expect("Failed to subtract timestamps rhs < self") |
| } |
| } |
| |
| impl Default for IggyTimestamp { |
| fn default() -> Self { |
| Self(SystemTime::now()) |
| } |
| } |
| |
| impl fmt::Display for IggyTimestamp { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| write!(f, "{}", self.to_utc_string(UTC_TIME_FORMAT)) |
| } |
| } |
| |
| impl Serialize for IggyTimestamp { |
| fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
| where |
| S: Serializer, |
| { |
| let timestamp = self.as_micros(); |
| serializer.serialize_u64(timestamp) |
| } |
| } |
| |
| impl<'de> Deserialize<'de> for IggyTimestamp { |
| fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
| where |
| D: Deserializer<'de>, |
| { |
| deserializer.deserialize_u64(IggyTimestampVisitor) |
| } |
| } |
| struct IggyTimestampVisitor; |
| |
| impl Visitor<'_> for IggyTimestampVisitor { |
| type Value = IggyTimestamp; |
| |
| fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
| formatter.write_str("a microsecond timestamp as a u64") |
| } |
| |
| fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> |
| where |
| E: de::Error, |
| { |
| Ok(IggyTimestamp::from(value)) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| #[test] |
| fn test_timestamp_get() { |
| let timestamp = IggyTimestamp::now(); |
| assert!(timestamp.as_micros() > 0); |
| } |
| |
| #[test] |
| fn test_timestamp_to_micros() { |
| let timestamp = IggyTimestamp::from(1663472051111); |
| assert_eq!(timestamp.as_micros(), 1663472051111); |
| } |
| |
| #[test] |
| fn test_timestamp_to_string() { |
| let timestamp = IggyTimestamp::from(1694968446131680); |
| assert_eq!( |
| timestamp.to_utc_string("%Y-%m-%d %H:%M:%S"), |
| "2023-09-17 16:34:06" |
| ); |
| } |
| |
| #[test] |
| fn test_timestamp_from_u64() { |
| let timestamp = IggyTimestamp::from(1663472051111); |
| assert_eq!(timestamp.as_micros(), 1663472051111); |
| } |
| } |