1use std::{fmt::Display, str::FromStr};
7
8use time::OffsetDateTime;
9
10#[derive(
11 Debug,
12 Clone,
13 PartialEq,
14 Eq,
15 PartialOrd,
16 Ord,
17 Hash,
18 derive_more::AsRef,
19 derive_more::From,
20 derive_more::Into,
21 serde_with::SerializeDisplay,
22 serde_with::DeserializeFromStr,
23)]
24#[cfg_attr(feature = "sea-orm", derive(sea_orm::DeriveValueType))]
25pub struct DateTime(OffsetDateTime);
26
27impl DateTime {
28 pub fn now() -> Self {
29 Self(OffsetDateTime::now_utc())
30 }
31}
32
33impl FromStr for DateTime {
34 type Err = time::error::Parse;
35
36 fn from_str(s: &str) -> Result<Self, Self::Err> {
37 use time::format_description::well_known::Iso8601;
38
39 Ok(Self(OffsetDateTime::parse(s, &Iso8601::DEFAULT)?))
40 }
41}
42
43impl Display for DateTime {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 use time::format_description::well_known::{
46 iso8601::{Config, EncodedConfig},
47 Iso8601,
48 };
49
50 const CONFIG: EncodedConfig = Config::DEFAULT
51 .set_time_precision(
52 time::format_description::well_known::iso8601::TimePrecision::Second {
53 decimal_digits: None,
54 },
55 )
56 .encode();
57
58 write!(
59 f,
60 "{}",
61 self.0.format(&Iso8601::<CONFIG>).expect("valid date")
62 )
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::DateTime;
69
70 use time::macros::datetime;
71
72 #[test]
73 fn from_str() {
74 let parsed: DateTime = "2025-01-07T05:06:07+01:00"
75 .parse()
76 .expect("string must be parseable as DateTime");
77 assert_eq!(parsed, DateTime(datetime!(2025-01-07 05:06:07 +01:00)));
78 }
79
80 #[test]
81 fn to_string() {
82 let dt = DateTime(datetime!(2025-01-07 05:06:07 +01:00));
83
84 assert_eq!(dt.to_string(), "2025-01-07T05:06:07+01:00".to_string());
85 }
86}
87
88#[cfg(test)]
89mod serde_tests {
90 use super::DateTime;
91 use pretty_assertions::assert_eq;
92 use serde_json::json;
93 use time::macros::datetime;
94
95 #[test]
96 fn serialize() {
97 assert_eq!(
98 json!(DateTime(datetime!(2025-01-07 05:06:07 +01:00))),
99 json!("2025-01-07T05:06:07+01:00")
100 );
101 }
102
103 #[test]
104 fn deserialize_good() {
105 let deserialized: DateTime = serde_json::from_value(json!("2025-01-07T05:06:07+01:00"))
106 .expect("json must be parseable as DateTime");
107
108 assert_eq!(
109 deserialized,
110 DateTime(datetime!(2025-01-07 05:06:07 +01:00))
111 );
112 }
113
114 #[test]
115 fn deserialize_bad() {
116 assert!(serde_json::from_value::<DateTime>(json!("xyzabcd")).is_err());
117 assert!(serde_json::from_value::<DateTime>(json!(true)).is_err());
118 assert!(serde_json::from_value::<DateTime>(json!(123)).is_err());
119 }
120}