opendev_models/
datetime_compat.rs1use chrono::{DateTime, NaiveDateTime, Utc};
8use serde::{self, Deserialize, Deserializer, Serializer};
9
10const FORMAT: &str = "%Y-%m-%dT%H:%M:%S";
11const FORMAT_WITH_FRAC: &str = "%Y-%m-%dT%H:%M:%S%.f";
12
13pub fn serialize<S>(date: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
15where
16 S: Serializer,
17{
18 serializer.serialize_str(&date.to_rfc3339())
19}
20
21pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
23where
24 D: Deserializer<'de>,
25{
26 let s = String::deserialize(deserializer)?;
27 parse_flexible_datetime(&s).map_err(serde::de::Error::custom)
28}
29
30pub fn parse_flexible_datetime(s: &str) -> Result<DateTime<Utc>, String> {
36 if let Ok(dt) = DateTime::parse_from_rfc3339(s) {
38 return Ok(dt.with_timezone(&Utc));
39 }
40
41 if let Ok(naive) = NaiveDateTime::parse_from_str(s, FORMAT_WITH_FRAC) {
43 return Ok(naive.and_utc());
44 }
45
46 if let Ok(naive) = NaiveDateTime::parse_from_str(s, FORMAT) {
48 return Ok(naive.and_utc());
49 }
50
51 Err(format!("Cannot parse datetime: {s}"))
52}
53
54pub mod option {
56 use chrono::{DateTime, Utc};
57 use serde::{self, Deserialize, Deserializer, Serializer};
58
59 pub fn serialize<S>(date: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error>
60 where
61 S: Serializer,
62 {
63 match date {
64 Some(dt) => serializer.serialize_str(&dt.to_rfc3339()),
65 None => serializer.serialize_none(),
66 }
67 }
68
69 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<DateTime<Utc>>, D::Error>
70 where
71 D: Deserializer<'de>,
72 {
73 let opt: Option<String> = Option::deserialize(deserializer)?;
74 match opt {
75 Some(s) => super::parse_flexible_datetime(&s)
76 .map(Some)
77 .map_err(serde::de::Error::custom),
78 None => Ok(None),
79 }
80 }
81}
82
83#[cfg(test)]
84#[path = "datetime_compat_tests.rs"]
85mod tests;