use chrono::{DateTime, Utc};
use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Time(pub DateTime<Utc>);
#[derive(Debug, Serialize, Deserialize)]
struct TimeInternal {
unix: i64,
us: i64,
#[serde(skip_serializing_if = "Option::is_none")]
tz: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
iso: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
full: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
unixms: Option<String>,
}
impl Time {
pub fn new(dt: DateTime<Utc>) -> Self {
Time(dt)
}
pub fn from_unix(unix: i64, usec: i64) -> Option<Self> {
let nanos = (usec as u32).saturating_mul(1000);
DateTime::from_timestamp(unix, nanos).map(Time)
}
pub fn unix(&self) -> i64 {
self.0.timestamp()
}
pub fn usec(&self) -> i64 {
(self.0.timestamp_subsec_nanos() / 1000) as i64
}
pub fn unix_micro(&self) -> i64 {
self.0.timestamp_micros()
}
pub fn unix_milli(&self) -> i64 {
self.0.timestamp_millis()
}
pub fn iso(&self) -> String {
self.0.format("%Y-%m-%d %H:%M:%S").to_string()
}
}
impl From<DateTime<Utc>> for Time {
fn from(dt: DateTime<Utc>) -> Self {
Time(dt)
}
}
impl From<Time> for DateTime<Utc> {
fn from(t: Time) -> Self {
t.0
}
}
impl Serialize for Time {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let internal = TimeInternal {
unix: self.unix(),
us: self.usec(),
tz: Some("UTC".to_string()),
iso: Some(self.iso()),
full: Some(self.unix_micro().to_string()),
unixms: Some(self.unix_milli().to_string()),
};
internal.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Time {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let internal = TimeInternal::deserialize(deserializer)?;
Time::from_unix(internal.unix, internal.us)
.ok_or_else(|| D::Error::custom("timestamp out of range"))
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json;
#[test]
fn test_time_serialization() {
let time = Time::from_unix(1597242491, 747497).unwrap();
let json = serde_json::to_string(&time).unwrap();
let value: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(value["unix"], 1597242491);
assert_eq!(value["us"], 747497);
}
#[test]
fn test_time_deserialization() {
let json = r#"{"unix": 1597242491, "us": 747497}"#;
let time: Time = serde_json::from_str(json).unwrap();
assert_eq!(time.unix(), 1597242491);
assert_eq!(time.usec(), 747497);
}
#[test]
fn test_time_null() {
let json = "null";
let result: Result<Option<Time>, _> = serde_json::from_str(json);
assert!(result.is_ok());
assert!(result.unwrap().is_none());
}
}