Skip to main content

serde_with/
time_0_3.rs

1//! De/Serialization of [time v0.3][time] types
2//!
3//! This modules is only available if using the `time_0_3` feature of the crate.
4//! No extra types are exposed. Instead it enables support for [`time_0_3::Duration`] together with [`DurationSeconds`] and its variants.
5//! The types [`time_0_3::PrimitiveDateTime`] and [`time_0_3::OffsetDateTime`] are supported by [`TimestampSeconds`] and its variants.
6//! The well-known format descriptions [`Rfc2822`], [`Rfc3339`] and [`Iso8601`] are supported for [`OffsetDateTime`].
7//!
8//! [time]: https://docs.rs/time/0.3/
9
10// Serialization of large numbers can result in overflows
11// The time calculations are prone to this, so lint here extra
12// https://github.com/jonasbb/serde_with/issues/771
13#![warn(clippy::as_conversions)]
14
15use crate::{
16    formats::{Flexible, Format, Strict, Strictness},
17    prelude::*,
18    utils::duration::{use_duration_signed_de, use_duration_signed_ser},
19};
20#[cfg(feature = "std")]
21use ::time_0_3::format_description::well_known::{
22    iso8601::EncodedConfig, Iso8601, Rfc2822, Rfc3339,
23};
24use ::time_0_3::{Duration as Time03Duration, OffsetDateTime, PrimitiveDateTime};
25
26/// Create a [`PrimitiveDateTime`] for the Unix Epoch
27fn unix_epoch_primitive() -> PrimitiveDateTime {
28    PrimitiveDateTime::new(
29        ::time_0_3::Date::from_ordinal_date(1970, 1).unwrap(),
30        ::time_0_3::Time::from_hms_nano(0, 0, 0, 0).unwrap(),
31    )
32}
33
34/// Convert a [`time::Duration`][time_0_3::Duration] into a [`DurationSigned`]
35fn duration_into_duration_signed(dur: &Time03Duration) -> DurationSigned {
36    let std_dur = Duration::new(
37        dur.whole_seconds().unsigned_abs(),
38        dur.subsec_nanoseconds().unsigned_abs(),
39    );
40
41    DurationSigned::with_duration(
42        // A duration of 0 is not positive, so check for negative value.
43        if dur.is_negative() {
44            Sign::Negative
45        } else {
46            Sign::Positive
47        },
48        std_dur,
49    )
50}
51
52fn offset_datetime_to_duration(source: &OffsetDateTime) -> DurationSigned {
53    duration_into_duration_signed(&(*source - OffsetDateTime::UNIX_EPOCH))
54}
55
56fn primitive_datetime_to_duration(source: &PrimitiveDateTime) -> DurationSigned {
57    duration_into_duration_signed(&(*source - unix_epoch_primitive()))
58}
59
60// No sub-unit precision
61impl<STRICTNESS> SerializeAs<Time03Duration> for
    DurationNanoSeconds<String, STRICTNESS> where STRICTNESS: Strictness {
    fn serialize_as<S>(source: &Time03Duration, serializer: S)
        -> Result<S::Ok, S::Error> where S: Serializer {
        let dur: DurationSigned = duration_into_duration_signed(source);
        DurationNanoSeconds::<String,
                STRICTNESS>::serialize_as(&dur, serializer)
    }
}use_duration_signed_ser!(
62    DurationSeconds DurationSeconds,
63    DurationMilliSeconds DurationMilliSeconds,
64    DurationMicroSeconds DurationMicroSeconds,
65    DurationNanoSeconds DurationNanoSeconds,
66    => {
67        Time03Duration; duration_into_duration_signed =>
68        {i64, STRICTNESS => STRICTNESS: Strictness}
69        {f64, STRICTNESS => STRICTNESS: Strictness}
70        #[cfg(feature = "alloc")] {String, STRICTNESS => STRICTNESS: Strictness}
71    }
72);
73
74impl<STRICTNESS> SerializeAs<PrimitiveDateTime> for
    TimestampNanoSeconds<String, STRICTNESS> where STRICTNESS: Strictness {
    fn serialize_as<S>(source: &PrimitiveDateTime, serializer: S)
        -> Result<S::Ok, S::Error> where S: Serializer {
        let dur: DurationSigned = primitive_datetime_to_duration(source);
        DurationNanoSeconds::<String,
                STRICTNESS>::serialize_as(&dur, serializer)
    }
}use_duration_signed_ser!(
75    TimestampSeconds DurationSeconds,
76    TimestampMilliSeconds DurationMilliSeconds,
77    TimestampMicroSeconds DurationMicroSeconds,
78    TimestampNanoSeconds DurationNanoSeconds,
79    => {
80        OffsetDateTime; offset_datetime_to_duration =>
81        {i64, STRICTNESS => STRICTNESS: Strictness}
82        {f64, STRICTNESS => STRICTNESS: Strictness}
83        #[cfg(feature = "alloc")] {String, STRICTNESS => STRICTNESS: Strictness}
84    }
85    => {
86        PrimitiveDateTime; primitive_datetime_to_duration =>
87        {i64, STRICTNESS => STRICTNESS: Strictness}
88        {f64, STRICTNESS => STRICTNESS: Strictness}
89        #[cfg(feature = "alloc")] {String, STRICTNESS => STRICTNESS: Strictness}
90    }
91);
92
93// Duration/Timestamp WITH FRACTIONS
94impl<STRICTNESS> SerializeAs<Time03Duration> for
    DurationNanoSecondsWithFrac<String, STRICTNESS> where
    STRICTNESS: Strictness {
    fn serialize_as<S>(source: &Time03Duration, serializer: S)
        -> Result<S::Ok, S::Error> where S: Serializer {
        let dur: DurationSigned = duration_into_duration_signed(source);
        DurationNanoSecondsWithFrac::<String,
                STRICTNESS>::serialize_as(&dur, serializer)
    }
}use_duration_signed_ser!(
95    DurationSecondsWithFrac DurationSecondsWithFrac,
96    DurationMilliSecondsWithFrac DurationMilliSecondsWithFrac,
97    DurationMicroSecondsWithFrac DurationMicroSecondsWithFrac,
98    DurationNanoSecondsWithFrac DurationNanoSecondsWithFrac,
99    => {
100        Time03Duration; duration_into_duration_signed =>
101        {f64, STRICTNESS => STRICTNESS: Strictness}
102        #[cfg(feature = "alloc")] {String, STRICTNESS => STRICTNESS: Strictness}
103    }
104);
105
106impl<STRICTNESS> SerializeAs<PrimitiveDateTime> for
    TimestampNanoSecondsWithFrac<String, STRICTNESS> where
    STRICTNESS: Strictness {
    fn serialize_as<S>(source: &PrimitiveDateTime, serializer: S)
        -> Result<S::Ok, S::Error> where S: Serializer {
        let dur: DurationSigned = primitive_datetime_to_duration(source);
        DurationNanoSecondsWithFrac::<String,
                STRICTNESS>::serialize_as(&dur, serializer)
    }
}use_duration_signed_ser!(
107    TimestampSecondsWithFrac DurationSecondsWithFrac,
108    TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac,
109    TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac,
110    TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac,
111    => {
112        OffsetDateTime; offset_datetime_to_duration =>
113        {f64, STRICTNESS => STRICTNESS: Strictness}
114        #[cfg(feature = "alloc")] {String, STRICTNESS => STRICTNESS: Strictness}
115    }
116    => {
117        PrimitiveDateTime; primitive_datetime_to_duration =>
118        {f64, STRICTNESS => STRICTNESS: Strictness}
119        #[cfg(feature = "alloc")] {String, STRICTNESS => STRICTNESS: Strictness}
120    }
121);
122
123/// Convert a [`DurationSigned`] into a [`time_0_3::Duration`]
124fn duration_from_duration_signed<'de, D>(sdur: DurationSigned) -> Result<Time03Duration, D::Error>
125where
126    D: Deserializer<'de>,
127{
128    let mut dur: Time03Duration = match sdur.duration.try_into() {
129        Ok(dur) => dur,
130        Err(msg) => {
131            return Err(DeError::custom(format_args!("Duration is outside of the representable range: {0}", msg)format_args!(
132                "Duration is outside of the representable range: {msg}"
133            )))
134        }
135    };
136    if sdur.sign.is_negative() {
137        dur = -dur;
138    }
139    Ok(dur)
140}
141
142fn duration_to_offset_datetime<'de, D>(dur: DurationSigned) -> Result<OffsetDateTime, D::Error>
143where
144    D: Deserializer<'de>,
145{
146    Ok(OffsetDateTime::UNIX_EPOCH + duration_from_duration_signed::<D>(dur)?)
147}
148
149fn duration_to_primitive_datetime<'de, D>(
150    dur: DurationSigned,
151) -> Result<PrimitiveDateTime, D::Error>
152where
153    D: Deserializer<'de>,
154{
155    Ok(unix_epoch_primitive() + duration_from_duration_signed::<D>(dur)?)
156}
157
158// No sub-unit precision
159impl<'de> DeserializeAs<'de, Time03Duration> for
    DurationNanoSeconds<f64, Strict> where  {
    fn deserialize_as<D>(deserializer: D) -> Result<Time03Duration, D::Error>
        where D: Deserializer<'de> {
        let dur: DurationSigned =
            DurationNanoSeconds::<f64, Strict>::deserialize_as(deserializer)?;
        duration_from_duration_signed::<D>(dur)
    }
}use_duration_signed_de!(
160    DurationSeconds DurationSeconds,
161    DurationMilliSeconds DurationMilliSeconds,
162    DurationMicroSeconds DurationMicroSeconds,
163    DurationNanoSeconds DurationNanoSeconds,
164    => {
165        Time03Duration; duration_from_duration_signed =>
166        {FORMAT, Flexible => FORMAT: Format}
167        {i64, Strict =>}
168        #[cfg(feature = "alloc")] {String, Strict =>}
169        #[cfg(feature = "std")] {f64, Strict =>}
170    }
171);
172
173impl<'de> DeserializeAs<'de, PrimitiveDateTime> for
    TimestampNanoSeconds<f64, Strict> where  {
    fn deserialize_as<D>(deserializer: D)
        -> Result<PrimitiveDateTime, D::Error> where D: Deserializer<'de> {
        let dur: DurationSigned =
            DurationNanoSeconds::<f64, Strict>::deserialize_as(deserializer)?;
        duration_to_primitive_datetime::<D>(dur)
    }
}use_duration_signed_de!(
174    TimestampSeconds DurationSeconds,
175    TimestampMilliSeconds DurationMilliSeconds,
176    TimestampMicroSeconds DurationMicroSeconds,
177    TimestampNanoSeconds DurationNanoSeconds,
178    => {
179        OffsetDateTime; duration_to_offset_datetime =>
180        {FORMAT, Flexible => FORMAT: Format}
181        {i64, Strict =>}
182        #[cfg(feature = "alloc")] {String, Strict =>}
183        #[cfg(feature = "std")] {f64, Strict =>}
184    }
185    => {
186        PrimitiveDateTime; duration_to_primitive_datetime =>
187        {FORMAT, Flexible => FORMAT: Format}
188        {i64, Strict =>}
189        #[cfg(feature = "alloc")] {String, Strict =>}
190        #[cfg(feature = "std")] {f64, Strict =>}
191    }
192);
193
194// Duration/Timestamp WITH FRACTIONS
195impl<'de> DeserializeAs<'de, Time03Duration> for
    DurationNanoSecondsWithFrac<String, Strict> where  {
    fn deserialize_as<D>(deserializer: D) -> Result<Time03Duration, D::Error>
        where D: Deserializer<'de> {
        let dur: DurationSigned =
            DurationNanoSecondsWithFrac::<String,
                        Strict>::deserialize_as(deserializer)?;
        duration_from_duration_signed::<D>(dur)
    }
}use_duration_signed_de!(
196    DurationSecondsWithFrac DurationSecondsWithFrac,
197    DurationMilliSecondsWithFrac DurationMilliSecondsWithFrac,
198    DurationMicroSecondsWithFrac DurationMicroSecondsWithFrac,
199    DurationNanoSecondsWithFrac DurationNanoSecondsWithFrac,
200    => {
201        Time03Duration; duration_from_duration_signed =>
202        {FORMAT, Flexible => FORMAT: Format}
203        {f64, Strict =>}
204        #[cfg(feature = "alloc")] {String, Strict =>}
205    }
206);
207
208impl<'de> DeserializeAs<'de, PrimitiveDateTime> for
    TimestampNanoSecondsWithFrac<String, Strict> where  {
    fn deserialize_as<D>(deserializer: D)
        -> Result<PrimitiveDateTime, D::Error> where D: Deserializer<'de> {
        let dur: DurationSigned =
            DurationNanoSecondsWithFrac::<String,
                        Strict>::deserialize_as(deserializer)?;
        duration_to_primitive_datetime::<D>(dur)
    }
}use_duration_signed_de!(
209    TimestampSecondsWithFrac DurationSecondsWithFrac,
210    TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac,
211    TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac,
212    TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac,
213    => {
214        OffsetDateTime; duration_to_offset_datetime =>
215        {FORMAT, Flexible => FORMAT: Format}
216        {f64, Strict =>}
217        #[cfg(feature = "alloc")] {String, Strict =>}
218    }
219    => {
220        PrimitiveDateTime; duration_to_primitive_datetime =>
221        {FORMAT, Flexible => FORMAT: Format}
222        {f64, Strict =>}
223        #[cfg(feature = "alloc")] {String, Strict =>}
224    }
225);
226
227#[cfg(feature = "std")]
228impl SerializeAs<OffsetDateTime> for Rfc2822 {
229    fn serialize_as<S>(datetime: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
230    where
231        S: Serializer,
232    {
233        datetime
234            .format(&Rfc2822)
235            .map_err(S::Error::custom)?
236            .serialize(serializer)
237    }
238}
239
240#[cfg(feature = "std")]
241impl<'de> DeserializeAs<'de, OffsetDateTime> for Rfc2822 {
242    fn deserialize_as<D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
243    where
244        D: Deserializer<'de>,
245    {
246        struct Helper;
247        impl Visitor<'_> for Helper {
248            type Value = OffsetDateTime;
249
250            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
251                formatter.write_str("a RFC2822-formatted `OffsetDateTime`")
252            }
253
254            fn visit_str<E: DeError>(self, value: &str) -> Result<Self::Value, E> {
255                Self::Value::parse(value, &Rfc2822).map_err(E::custom)
256            }
257        }
258
259        deserializer.deserialize_str(Helper)
260    }
261}
262
263#[cfg(feature = "std")]
264impl SerializeAs<OffsetDateTime> for Rfc3339 {
265    fn serialize_as<S>(datetime: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
266    where
267        S: Serializer,
268    {
269        datetime
270            .format(&Rfc3339)
271            .map_err(S::Error::custom)?
272            .serialize(serializer)
273    }
274}
275
276#[cfg(feature = "std")]
277impl<'de> DeserializeAs<'de, OffsetDateTime> for Rfc3339 {
278    fn deserialize_as<D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
279    where
280        D: Deserializer<'de>,
281    {
282        struct Helper;
283        impl Visitor<'_> for Helper {
284            type Value = OffsetDateTime;
285
286            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
287                formatter.write_str("a RFC3339-formatted `OffsetDateTime`")
288            }
289
290            fn visit_str<E: DeError>(self, value: &str) -> Result<Self::Value, E> {
291                Self::Value::parse(value, &Rfc3339).map_err(E::custom)
292            }
293        }
294
295        deserializer.deserialize_str(Helper)
296    }
297}
298
299#[cfg(feature = "std")]
300impl<const CONFIG: EncodedConfig> SerializeAs<OffsetDateTime> for Iso8601<CONFIG> {
301    fn serialize_as<S>(datetime: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
302    where
303        S: Serializer,
304    {
305        datetime
306            .format(&Iso8601::<CONFIG>)
307            .map_err(S::Error::custom)?
308            .serialize(serializer)
309    }
310}
311
312#[cfg(feature = "std")]
313impl<'de, const CONFIG: EncodedConfig> DeserializeAs<'de, OffsetDateTime> for Iso8601<CONFIG> {
314    fn deserialize_as<D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
315    where
316        D: Deserializer<'de>,
317    {
318        struct Helper<const CONFIG: EncodedConfig>;
319        impl<const CONFIG: EncodedConfig> Visitor<'_> for Helper<CONFIG> {
320            type Value = OffsetDateTime;
321
322            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
323                formatter.write_str("a ISO8601-formatted `OffsetDateTime`")
324            }
325
326            fn visit_str<E: DeError>(self, value: &str) -> Result<Self::Value, E> {
327                Self::Value::parse(value, &Iso8601::<CONFIG>).map_err(E::custom)
328            }
329        }
330
331        deserializer.deserialize_str(Helper::<CONFIG>)
332    }
333}