time/serde/
mod.rs

1//! Differential formats for serde.
2// This also includes the serde implementations for all types. This doesn't need to be externally
3// documented, though.
4
5// Types with guaranteed stable serde representations. Strings are avoided to allow for optimal
6// representations in various binary forms.
7
8/// Consume the next item in a sequence.
9macro_rules! item {
10    ($seq:expr, $name:literal) => {
11        $seq.next_element()?
12            .ok_or_else(|| <A::Error as serde::de::Error>::custom(concat!("expected ", $name)))
13    };
14}
15
16#[cfg(any(feature = "formatting", feature = "parsing"))]
17pub mod iso8601;
18#[cfg(any(feature = "formatting", feature = "parsing"))]
19pub mod rfc2822;
20#[cfg(any(feature = "formatting", feature = "parsing"))]
21pub mod rfc3339;
22pub mod timestamp;
23mod visitor;
24
25#[cfg(feature = "serde-human-readable")]
26use alloc::string::ToString;
27use core::marker::PhantomData;
28
29#[cfg(feature = "serde-human-readable")]
30use serde::ser::Error as _;
31use serde::{Deserialize, Deserializer, Serialize, Serializer};
32/// Generate a custom serializer and deserializer from a format string or an existing format.
33///
34/// The syntax accepted by this macro is the same as [`format_description::parse()`], which can
35/// be found in [the book](https://time-rs.github.io/book/api/format-description.html).
36///
37/// # Usage
38///
39/// Invoked as `serde::format_description!(mod_name, Date, FORMAT)` where `FORMAT` is either a
40/// `"<format string>"` or something that implements
41#[cfg_attr(
42    all(feature = "formatting", feature = "parsing"),
43    doc = "[`Formattable`](crate::formatting::Formattable) and \
44           [`Parsable`](crate::parsing::Parsable)."
45)]
46#[cfg_attr(
47    all(feature = "formatting", not(feature = "parsing")),
48    doc = "[`Formattable`](crate::formatting::Formattable)."
49)]
50#[cfg_attr(
51    all(not(feature = "formatting"), feature = "parsing"),
52    doc = "[`Parsable`](crate::parsing::Parsable)."
53)]
54/// This puts a module named `mod_name` in the current scope that can be used to format `Date`
55/// structs. A submodule (`mod_name::option`) is also generated for `Option<Date>`. Both
56/// modules are only visible in the current scope by default. To increase visibility, you can
57/// specify `pub`, `pub(crate)`, or similar before the module name:
58/// `serde::format_description!(pub mod_name, Date, FORMAT)`.
59///
60/// The returned `Option` will contain a deserialized value if present and `None` if the field
61/// is present but the value is `null` (or the equivalent in other formats). To return `None`
62/// when the field is not present, you should use `#[serde(default)]` on the field.
63///
64/// # Examples
65///
66/// Using a format string:
67///
68/// ```rust,no_run
69/// # use time::OffsetDateTime;
70#[cfg_attr(
71    all(feature = "formatting", feature = "parsing"),
72    doc = "use ::serde::{Serialize, Deserialize};"
73)]
74#[cfg_attr(
75    all(feature = "formatting", not(feature = "parsing")),
76    doc = "use ::serde::Serialize;"
77)]
78#[cfg_attr(
79    all(not(feature = "formatting"), feature = "parsing"),
80    doc = "use ::serde::Deserialize;"
81)]
82/// use time::serde;
83///
84/// // Makes a module `mod my_format { ... }`.
85/// serde::format_description!(my_format, OffsetDateTime, "hour=[hour], minute=[minute]");
86///
87/// # #[allow(dead_code)]
88#[cfg_attr(
89    all(feature = "formatting", feature = "parsing"),
90    doc = "#[derive(Serialize, Deserialize)]"
91)]
92#[cfg_attr(
93    all(feature = "formatting", not(feature = "parsing")),
94    doc = "#[derive(Serialize)]"
95)]
96#[cfg_attr(
97    all(not(feature = "formatting"), feature = "parsing"),
98    doc = "#[derive(Deserialize)]"
99)]
100/// struct SerializesWithCustom {
101///     #[serde(with = "my_format")]
102///     dt: OffsetDateTime,
103///     #[serde(with = "my_format::option")]
104///     maybe_dt: Option<OffsetDateTime>,
105/// }
106/// ```
107/// 
108/// Define the format separately to be used in multiple places:
109/// ```rust,no_run
110/// # use time::OffsetDateTime;
111#[cfg_attr(
112    all(feature = "formatting", feature = "parsing"),
113    doc = "use ::serde::{Serialize, Deserialize};"
114)]
115#[cfg_attr(
116    all(feature = "formatting", not(feature = "parsing")),
117    doc = "use ::serde::Serialize;"
118)]
119#[cfg_attr(
120    all(not(feature = "formatting"), feature = "parsing"),
121    doc = "use ::serde::Deserialize;"
122)]
123/// use time::serde;
124/// use time::format_description::BorrowedFormatItem;
125///
126/// const DATE_TIME_FORMAT: &[BorrowedFormatItem<'_>] = time::macros::format_description!(
127///     "hour=[hour], minute=[minute]"
128/// );
129///
130/// // Makes a module `mod my_format { ... }`.
131/// serde::format_description!(my_format, OffsetDateTime, DATE_TIME_FORMAT);
132///
133/// # #[allow(dead_code)]
134#[cfg_attr(
135    all(feature = "formatting", feature = "parsing"),
136    doc = "#[derive(Serialize, Deserialize)]"
137)]
138#[cfg_attr(
139    all(feature = "formatting", not(feature = "parsing")),
140    doc = "#[derive(Serialize)]"
141)]
142#[cfg_attr(
143    all(not(feature = "formatting"), feature = "parsing"),
144    doc = "#[derive(Deserialize)]"
145)]
146/// struct SerializesWithCustom {
147///     #[serde(with = "my_format")]
148///     dt: OffsetDateTime,
149///     #[serde(with = "my_format::option")]
150///     maybe_dt: Option<OffsetDateTime>,
151/// }
152///
153/// fn main() {
154///     # #[allow(unused_variables)]
155///     let str_ts = OffsetDateTime::now_utc().format(DATE_TIME_FORMAT).unwrap();
156/// }
157/// ```
158/// 
159/// Customize the configuration of ISO 8601 formatting/parsing:
160/// ```rust,no_run
161/// # use time::OffsetDateTime;
162#[cfg_attr(
163    all(feature = "formatting", feature = "parsing"),
164    doc = "use ::serde::{Serialize, Deserialize};"
165)]
166#[cfg_attr(
167    all(feature = "formatting", not(feature = "parsing")),
168    doc = "use ::serde::Serialize;"
169)]
170#[cfg_attr(
171    all(not(feature = "formatting"), feature = "parsing"),
172    doc = "use ::serde::Deserialize;"
173)]
174/// use time::serde;
175/// use time::format_description::well_known::{iso8601, Iso8601};
176///
177/// # #[allow(dead_code)]
178/// const CONFIG: iso8601::EncodedConfig = iso8601::Config::DEFAULT
179///     .set_year_is_six_digits(false)
180///     .encode();
181/// # #[allow(dead_code)]
182/// const FORMAT: Iso8601<CONFIG> = Iso8601::<CONFIG>;
183///
184/// // Makes a module `mod my_format { ... }`.
185/// serde::format_description!(my_format, OffsetDateTime, FORMAT);
186///
187/// # #[allow(dead_code)]
188#[cfg_attr(
189    all(feature = "formatting", feature = "parsing"),
190    doc = "#[derive(Serialize, Deserialize)]"
191)]
192#[cfg_attr(
193    all(feature = "formatting", not(feature = "parsing")),
194    doc = "#[derive(Serialize)]"
195)]
196#[cfg_attr(
197    all(not(feature = "formatting"), feature = "parsing"),
198    doc = "#[derive(Deserialize)]"
199)]
200/// struct SerializesWithCustom {
201///     #[serde(with = "my_format")]
202///     dt: OffsetDateTime,
203///     #[serde(with = "my_format::option")]
204///     maybe_dt: Option<OffsetDateTime>,
205/// }
206/// # fn main() {}
207/// ```
208/// 
209/// [`format_description::parse()`]: crate::format_description::parse()
210#[cfg(all(feature = "macros", any(feature = "formatting", feature = "parsing")))]
211pub use time_macros::serde_format_description as format_description;
212
213use self::visitor::Visitor;
214#[cfg(feature = "parsing")]
215use crate::format_description::{modifier, BorrowedFormatItem, Component};
216use crate::{
217    Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday,
218};
219
220/// The format used when serializing and deserializing a human-readable `Date`.
221#[cfg(feature = "parsing")]
222const DATE_FORMAT: &[BorrowedFormatItem<'_>] = &[
223    BorrowedFormatItem::Component(Component::Year(modifier::Year::default())),
224    BorrowedFormatItem::Literal(b"-"),
225    BorrowedFormatItem::Component(Component::Month(modifier::Month::default())),
226    BorrowedFormatItem::Literal(b"-"),
227    BorrowedFormatItem::Component(Component::Day(modifier::Day::default())),
228];
229
230impl Serialize for Date {
231    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
232        #[cfg(feature = "serde-human-readable")]
233        if serializer.is_human_readable() {
234            let Ok(s) = self.format(&DATE_FORMAT) else {
235                return Err(S::Error::custom("failed formatting `Date`"));
236            };
237            return serializer.serialize_str(&s);
238        }
239
240        (self.year(), self.ordinal()).serialize(serializer)
241    }
242}
243
244impl<'a> Deserialize<'a> for Date {
245    fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
246        if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
247            deserializer.deserialize_any(Visitor::<Self>(PhantomData))
248        } else {
249            deserializer.deserialize_tuple(2, Visitor::<Self>(PhantomData))
250        }
251    }
252}
253
254impl Serialize for Duration {
255    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
256        #[cfg(feature = "serde-human-readable")]
257        if serializer.is_human_readable() {
258            return serializer.collect_str(&format_args!(
259                "{}{}.{:>09}",
260                if self.is_negative() { "-" } else { "" },
261                self.whole_seconds().unsigned_abs(),
262                self.subsec_nanoseconds().abs(),
263            ));
264        }
265
266        (self.whole_seconds(), self.subsec_nanoseconds()).serialize(serializer)
267    }
268}
269
270impl<'a> Deserialize<'a> for Duration {
271    fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
272        if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
273            deserializer.deserialize_any(Visitor::<Self>(PhantomData))
274        } else {
275            deserializer.deserialize_tuple(2, Visitor::<Self>(PhantomData))
276        }
277    }
278}
279
280/// The format used when serializing and deserializing a human-readable `OffsetDateTime`.
281#[cfg(feature = "parsing")]
282const OFFSET_DATE_TIME_FORMAT: &[BorrowedFormatItem<'_>] = &[
283    BorrowedFormatItem::Compound(DATE_FORMAT),
284    BorrowedFormatItem::Literal(b" "),
285    BorrowedFormatItem::Compound(TIME_FORMAT),
286    BorrowedFormatItem::Literal(b" "),
287    BorrowedFormatItem::Compound(UTC_OFFSET_FORMAT),
288];
289
290impl Serialize for OffsetDateTime {
291    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
292        #[cfg(feature = "serde-human-readable")]
293        if serializer.is_human_readable() {
294            let Ok(s) = self.format(&OFFSET_DATE_TIME_FORMAT) else {
295                return Err(S::Error::custom("failed formatting `OffsetDateTime`"));
296            };
297            return serializer.serialize_str(&s);
298        }
299
300        (
301            self.year(),
302            self.ordinal(),
303            self.hour(),
304            self.minute(),
305            self.second(),
306            self.nanosecond(),
307            self.offset().whole_hours(),
308            self.offset().minutes_past_hour(),
309            self.offset().seconds_past_minute(),
310        )
311            .serialize(serializer)
312    }
313}
314
315impl<'a> Deserialize<'a> for OffsetDateTime {
316    fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
317        if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
318            deserializer.deserialize_any(Visitor::<Self>(PhantomData))
319        } else {
320            deserializer.deserialize_tuple(9, Visitor::<Self>(PhantomData))
321        }
322    }
323}
324
325/// The format used when serializing and deserializing a human-readable `PrimitiveDateTime`.
326#[cfg(feature = "parsing")]
327const PRIMITIVE_DATE_TIME_FORMAT: &[BorrowedFormatItem<'_>] = &[
328    BorrowedFormatItem::Compound(DATE_FORMAT),
329    BorrowedFormatItem::Literal(b" "),
330    BorrowedFormatItem::Compound(TIME_FORMAT),
331];
332
333impl Serialize for PrimitiveDateTime {
334    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
335        #[cfg(feature = "serde-human-readable")]
336        if serializer.is_human_readable() {
337            let Ok(s) = self.format(&PRIMITIVE_DATE_TIME_FORMAT) else {
338                return Err(S::Error::custom("failed formatting `PrimitiveDateTime`"));
339            };
340            return serializer.serialize_str(&s);
341        }
342
343        (
344            self.year(),
345            self.ordinal(),
346            self.hour(),
347            self.minute(),
348            self.second(),
349            self.nanosecond(),
350        )
351            .serialize(serializer)
352    }
353}
354
355impl<'a> Deserialize<'a> for PrimitiveDateTime {
356    fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
357        if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
358            deserializer.deserialize_any(Visitor::<Self>(PhantomData))
359        } else {
360            deserializer.deserialize_tuple(6, Visitor::<Self>(PhantomData))
361        }
362    }
363}
364
365/// The format used when serializing and deserializing a human-readable `UtcDateTime`.
366#[cfg(feature = "parsing")]
367const UTC_DATE_TIME_FORMAT: &[BorrowedFormatItem<'_>] = PRIMITIVE_DATE_TIME_FORMAT;
368
369impl Serialize for UtcDateTime {
370    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
371        #[cfg(feature = "serde-human-readable")]
372        if serializer.is_human_readable() {
373            let Ok(s) = self.format(&PRIMITIVE_DATE_TIME_FORMAT) else {
374                return Err(S::Error::custom("failed formatting `UtcDateTime`"));
375            };
376            return serializer.serialize_str(&s);
377        }
378
379        (
380            self.year(),
381            self.ordinal(),
382            self.hour(),
383            self.minute(),
384            self.second(),
385            self.nanosecond(),
386        )
387            .serialize(serializer)
388    }
389}
390
391impl<'a> Deserialize<'a> for UtcDateTime {
392    fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
393        if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
394            deserializer.deserialize_any(Visitor::<Self>(PhantomData))
395        } else {
396            deserializer.deserialize_tuple(6, Visitor::<Self>(PhantomData))
397        }
398    }
399}
400
401/// The format used when serializing and deserializing a human-readable `Time`.
402#[cfg(feature = "parsing")]
403const TIME_FORMAT: &[BorrowedFormatItem<'_>] = &[
404    BorrowedFormatItem::Component(Component::Hour(modifier::Hour::default())),
405    BorrowedFormatItem::Literal(b":"),
406    BorrowedFormatItem::Component(Component::Minute(modifier::Minute::default())),
407    BorrowedFormatItem::Literal(b":"),
408    BorrowedFormatItem::Component(Component::Second(modifier::Second::default())),
409    BorrowedFormatItem::Literal(b"."),
410    BorrowedFormatItem::Component(Component::Subsecond(modifier::Subsecond::default())),
411];
412
413impl Serialize for Time {
414    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
415        #[cfg(feature = "serde-human-readable")]
416        if serializer.is_human_readable() {
417            let Ok(s) = self.format(&TIME_FORMAT) else {
418                return Err(S::Error::custom("failed formatting `Time`"));
419            };
420            return serializer.serialize_str(&s);
421        }
422
423        (self.hour(), self.minute(), self.second(), self.nanosecond()).serialize(serializer)
424    }
425}
426
427impl<'a> Deserialize<'a> for Time {
428    fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
429        if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
430            deserializer.deserialize_any(Visitor::<Self>(PhantomData))
431        } else {
432            deserializer.deserialize_tuple(4, Visitor::<Self>(PhantomData))
433        }
434    }
435}
436
437// FIXME: turn these constants into `const { ... }` blocks once we can depend on Rust 1.79.
438#[cfg(feature = "parsing")]
439const UTC_OFFSET_HOUR: modifier::OffsetHour = {
440    let mut m = modifier::OffsetHour::default();
441    m.sign_is_mandatory = true;
442    m
443};
444#[cfg(feature = "parsing")]
445const UTC_OFFSET_MINUTE: modifier::OffsetMinute = modifier::OffsetMinute::default();
446#[cfg(feature = "parsing")]
447const UTC_OFFSET_SECOND: modifier::OffsetSecond = modifier::OffsetSecond::default();
448/// The format used when serializing and deserializing a human-readable `UtcOffset`.
449#[cfg(feature = "parsing")]
450const UTC_OFFSET_FORMAT: &[BorrowedFormatItem<'_>] = &[
451    BorrowedFormatItem::Component(Component::OffsetHour(UTC_OFFSET_HOUR)),
452    BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound(&[
453        BorrowedFormatItem::Literal(b":"),
454        BorrowedFormatItem::Component(Component::OffsetMinute(UTC_OFFSET_MINUTE)),
455        BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound(&[
456            BorrowedFormatItem::Literal(b":"),
457            BorrowedFormatItem::Component(Component::OffsetSecond(UTC_OFFSET_SECOND)),
458        ])),
459    ])),
460];
461
462impl Serialize for UtcOffset {
463    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
464        #[cfg(feature = "serde-human-readable")]
465        if serializer.is_human_readable() {
466            let Ok(s) = self.format(&UTC_OFFSET_FORMAT) else {
467                return Err(S::Error::custom("failed formatting `UtcOffset`"));
468            };
469            return serializer.serialize_str(&s);
470        }
471
472        (
473            self.whole_hours(),
474            self.minutes_past_hour(),
475            self.seconds_past_minute(),
476        )
477            .serialize(serializer)
478    }
479}
480
481impl<'a> Deserialize<'a> for UtcOffset {
482    fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
483        if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
484            deserializer.deserialize_any(Visitor::<Self>(PhantomData))
485        } else {
486            deserializer.deserialize_tuple(3, Visitor::<Self>(PhantomData))
487        }
488    }
489}
490
491impl Serialize for Weekday {
492    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
493        #[cfg(feature = "serde-human-readable")]
494        if serializer.is_human_readable() {
495            #[cfg(not(feature = "std"))]
496            use alloc::string::ToString;
497            return self.to_string().serialize(serializer);
498        }
499
500        self.number_from_monday().serialize(serializer)
501    }
502}
503
504impl<'a> Deserialize<'a> for Weekday {
505    fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
506        if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
507            deserializer.deserialize_any(Visitor::<Self>(PhantomData))
508        } else {
509            deserializer.deserialize_u8(Visitor::<Self>(PhantomData))
510        }
511    }
512}
513
514impl Serialize for Month {
515    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
516        #[cfg(feature = "serde-human-readable")]
517        if serializer.is_human_readable() {
518            #[cfg(not(feature = "std"))]
519            use alloc::string::String;
520            return self.to_string().serialize(serializer);
521        }
522
523        u8::from(*self).serialize(serializer)
524    }
525}
526
527impl<'a> Deserialize<'a> for Month {
528    fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
529        if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
530            deserializer.deserialize_any(Visitor::<Self>(PhantomData))
531        } else {
532            deserializer.deserialize_u8(Visitor::<Self>(PhantomData))
533        }
534    }
535}