azure_core/date/
iso8601.rs

1use crate::error::{ErrorKind, ResultExt};
2use serde::{de, Deserialize, Deserializer, Serializer};
3use time::{
4    format_description::well_known::{
5        iso8601::{Config, EncodedConfig, TimePrecision},
6        Iso8601,
7    },
8    OffsetDateTime, UtcOffset,
9};
10
11const SERDE_CONFIG: EncodedConfig = Config::DEFAULT
12    .set_year_is_six_digits(false)
13    .set_time_precision(TimePrecision::Second {
14        decimal_digits: None,
15    })
16    .encode();
17
18pub fn parse_iso8601(s: &str) -> crate::Result<OffsetDateTime> {
19    OffsetDateTime::parse(s, &Iso8601::<SERDE_CONFIG>)
20        .with_context(ErrorKind::DataConversion, || {
21            format!("unable to parse iso8601 date '{s}")
22        })
23}
24
25pub fn to_iso8601(date: &OffsetDateTime) -> crate::Result<String> {
26    date.format(&Iso8601::<SERDE_CONFIG>)
27        .with_context(ErrorKind::DataConversion, || {
28            format!("unable to format date '{date:?}")
29        })
30}
31
32pub fn deserialize<'de, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
33where
34    D: Deserializer<'de>,
35{
36    let s = String::deserialize(deserializer)?;
37    parse_iso8601(&s).map_err(de::Error::custom)
38}
39
40pub fn serialize<S>(date: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
41where
42    S: Serializer,
43{
44    date.to_offset(UtcOffset::UTC);
45    let as_str = to_iso8601(date).map_err(serde::ser::Error::custom)?;
46    serializer.serialize_str(&as_str)
47}
48
49pub mod option {
50    use crate::date::iso8601::{parse_iso8601, to_iso8601};
51    use serde::{Deserialize, Deserializer, Serializer};
52    use time::OffsetDateTime;
53
54    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
55    where
56        D: Deserializer<'de>,
57    {
58        let s: Option<String> = Option::deserialize(deserializer)?;
59        s.map(|s| parse_iso8601(&s).map_err(serde::de::Error::custom))
60            .transpose()
61    }
62
63    pub fn serialize<S>(date: &Option<OffsetDateTime>, serializer: S) -> Result<S::Ok, S::Error>
64    where
65        S: Serializer,
66    {
67        if let Some(date) = date {
68            serializer.serialize_str(&to_iso8601(date).map_err(serde::ser::Error::custom)?)
69        } else {
70            serializer.serialize_none()
71        }
72    }
73}