flagsmith_flag_engine/utils/
datetime.rs

1// A module that helps serialize/deserialize datetime that may or may not have timezone using the rfc3339 format
2// NOTE: Datetime that don't have timezone info are assumed to be in UTC
3use chrono::{DateTime, Utc};
4use serde::{self, Deserialize, Deserializer, Serializer};
5
6pub fn serialize<S>(date: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
7where
8    S: Serializer,
9{
10    serializer.serialize_str(&date.to_rfc3339())
11}
12
13pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
14where
15    D: Deserializer<'de>,
16{
17    let s = String::deserialize(deserializer)?;
18    let datetime = match DateTime::parse_from_rfc3339(&s) {
19        Ok(datetime) => datetime,
20        // TODO: pattern match on ErrorKind once it's public
21        // ref: https://github.com/chronotope/chrono/issues/319
22        Err(error) => match error.to_string().as_str() {
23            "premature end of input" => {
24                let datetime_str = s + "Z";
25                DateTime::parse_from_rfc3339(&datetime_str).map_err(serde::de::Error::custom)?
26            }
27            _ => panic!("Invalid datetime"),
28        },
29    };
30    let datatime_utc = datetime.with_timezone(&Utc);
31    return Ok(datatime_utc);
32}
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37    use crate::utils;
38    use chrono::Utc;
39    use rstest::*;
40    use serde::Serialize;
41
42    #[rstest]
43    #[case(serde_json::json!({"datetime":"2021-11-29T17:15:51.694223Z"}))]
44    #[case(serde_json::json!({"datetime":"2021-11-29T17:15:51.694223"}))]
45    #[case(serde_json::json!({"datetime":"2021-11-29T17:15:51.694223+00:00"}))]
46    fn serialize_deserialize_datetime(#[case] given_datetime: serde_json::Value) {
47        // Given
48        #[derive(Serialize, Deserialize)]
49        struct AStruct {
50            #[serde(with = "utils::datetime")]
51            datetime: DateTime<Utc>,
52        }
53
54        // When
55        let deserialized_struct: AStruct = serde_json::from_value(given_datetime.clone()).unwrap();
56
57        // Then
58        assert_eq!(deserialized_struct.datetime.timezone(), Utc);
59
60        // and
61        let serialized_struct = serde_json::to_value(deserialized_struct).unwrap();
62        assert_eq!(
63            serialized_struct,
64            serde_json::json!({"datetime":"2021-11-29T17:15:51.694223+00:00"})
65        );
66    }
67}