1pub use chrono::Utc;
8use serde::{de, Serialize};
9use std::{fmt, ops::Deref, str::FromStr};
10
11#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
18pub struct DateTime<Tz: chrono::offset::TimeZone>(chrono::DateTime<Tz>);
19
20impl<Tz: chrono::offset::TimeZone> Deref for DateTime<Tz> {
22 type Target = chrono::DateTime<Tz>;
23 fn deref(&self) -> &Self::Target {
24 &self.0
25 }
26}
27
28impl<Tz: chrono::offset::TimeZone> fmt::Display for DateTime<Tz>
30where
31 Tz::Offset: fmt::Display,
32{
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 write!(f, "{}", self.0)
35 }
36}
37
38impl<Tz: chrono::offset::TimeZone> fmt::Debug for DateTime<Tz>
40where
41 Tz::Offset: fmt::Display,
42{
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 write!(f, "{}", self.0)
45 }
46}
47
48struct DateTimeVisitor;
49
50impl<'de> de::Visitor<'de> for DateTimeVisitor {
51 type Value = DateTime<Utc>;
52
53 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
54 write!(formatter, "a formatted date or date and time string")
55 }
56
57 fn visit_str<E>(self, value: &str) -> Result<DateTime<Utc>, E>
58 where
59 E: de::Error,
60 {
61 parse_date(value).map_err(|err| E::custom(format!("{}", err)))
62 }
63}
64
65impl<'de> de::Deserialize<'de> for DateTime<Utc> {
66 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
67 where
68 D: de::Deserializer<'de>,
69 {
70 deserializer.deserialize_str(DateTimeVisitor)
71 }
72}
73
74impl Serialize for DateTime<Utc> {
75 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
76 where
77 S: serde::Serializer,
78 {
79 self.0.serialize(serializer)
80 }
81}
82
83fn parse_date(s: &str) -> Result<DateTime<Utc>, chrono::ParseError> {
85 let dt: chrono::DateTime<Utc> = if s.len() == 10 {
86 let nd = chrono::NaiveDate::parse_from_str(s, "%Y-%m-%d")?;
87 chrono::DateTime::from_utc(nd.and_hms(0, 0, 0), Utc)
88 } else {
89 s.parse::<chrono::DateTime<chrono::FixedOffset>>()
90 .map(|dt| dt.with_timezone(&Utc))?
91 };
92 Ok(DateTime(dt))
93}
94
95impl FromStr for DateTime<Utc> {
96 type Err = chrono::ParseError;
97
98 fn from_str(s: &str) -> chrono::ParseResult<DateTime<Utc>> {
101 parse_date(s)
102 }
103}
104
105impl From<chrono::DateTime<Utc>> for DateTime<Utc> {
106 fn from(d: chrono::DateTime<Utc>) -> Self {
107 DateTime(d)
108 }
109}
110
111#[test]
113fn test_datetime_ser_deser() {
114 #[derive(Debug, Serialize, serde::Deserialize)]
115 struct DateStruct {
116 date: DateTime<Utc>,
117 }
118
119 let val: DateStruct = serde_json::from_str(r#"{ "date": "2020-01-01" }"#).unwrap();
120 assert_eq!(
121 format!("date: {:?}", val.date),
122 "date: 2020-01-01 00:00:00 UTC"
123 );
124
125 let json = serde_json::to_string(&val).unwrap();
126 assert_eq!(json, r#"{"date":"2020-01-01T00:00:00Z"}"#);
127}
128
129#[test]
131fn test_datetime_debug() {
132 let dt = "2020-01-01T01:02:03Z".parse::<DateTime<Utc>>().unwrap();
133 assert_eq!(format!("{:?}", dt), "2020-01-01 01:02:03 UTC");
134}
135
136#[test]
138fn test_datetime_display() {
139 let dt = "2020-01-01T01:02:03Z".parse::<DateTime<Utc>>().unwrap();
140 assert_eq!(format!("{}", dt), "2020-01-01 01:02:03 UTC");
141}
142
143#[test]
145fn test_datetime_parse_iso8601() {
146 let dt = "2020-01-01T01:02:03Z".parse::<DateTime<Utc>>().unwrap();
147 assert_eq!(format!("{}", dt), "2020-01-01 01:02:03 UTC");
148 assert_eq!(format!("{}", dt.0), "2020-01-01 01:02:03 UTC");
149}
150
151#[test]
153fn test_datetime_parse_short() {
154 let dt = "2020-01-01".parse::<DateTime<Utc>>().unwrap();
155 assert_eq!(format!("{}", dt), "2020-01-01 00:00:00 UTC");
156 assert_eq!(format!("{}", dt.0), "2020-01-01 00:00:00 UTC");
157}