use std::{fmt::Display, str::FromStr};
use time::OffsetDateTime;
#[derive(
Debug,
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
derive_more::AsRef,
derive_more::From,
derive_more::Into,
serde_with::SerializeDisplay,
serde_with::DeserializeFromStr,
)]
pub struct DateTime(OffsetDateTime);
impl FromStr for DateTime {
type Err = time::error::Parse;
fn from_str(s: &str) -> Result<Self, Self::Err> {
use time::format_description::well_known::Iso8601;
Ok(Self(OffsetDateTime::parse(s, &Iso8601::DEFAULT)?))
}
}
impl Display for DateTime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use time::format_description::well_known::{
iso8601::{Config, EncodedConfig},
Iso8601,
};
const CONFIG: EncodedConfig = Config::DEFAULT
.set_time_precision(
time::format_description::well_known::iso8601::TimePrecision::Second {
decimal_digits: None,
},
)
.encode();
write!(
f,
"{}",
self.0.format(&Iso8601::<CONFIG>).expect("valid date")
)
}
}
#[cfg(test)]
mod tests {
use super::DateTime;
use time::macros::datetime;
#[test]
fn from_str() {
let parsed: DateTime = "2025-01-07T05:06:07+01:00"
.parse()
.expect("string must be parseable as DateTime");
assert_eq!(parsed, DateTime(datetime!(2025-01-07 05:06:07 +01:00)));
}
#[test]
fn to_string() {
let dt = DateTime(datetime!(2025-01-07 05:06:07 +01:00));
assert_eq!(dt.to_string(), "2025-01-07T05:06:07+01:00".to_string());
}
}
#[cfg(test)]
mod serde_tests {
use super::DateTime;
use pretty_assertions::assert_eq;
use serde_json::json;
use time::macros::datetime;
#[test]
fn serialize() {
assert_eq!(
json!(DateTime(datetime!(2025-01-07 05:06:07 +01:00))),
json!("2025-01-07T05:06:07+01:00")
);
}
#[test]
fn deserialize_good() {
let deserialized: DateTime = serde_json::from_value(json!("2025-01-07T05:06:07+01:00"))
.expect("json must be parseable as DateTime");
assert_eq!(
deserialized,
DateTime(datetime!(2025-01-07 05:06:07 +01:00))
);
}
#[test]
fn deserialize_bad() {
assert!(serde_json::from_value::<DateTime>(json!("xyzabcd")).is_err());
assert!(serde_json::from_value::<DateTime>(json!(true)).is_err());
assert!(serde_json::from_value::<DateTime>(json!(123)).is_err());
}
}