diff --git a/src/datetime/serde.rs b/src/datetime/serde.rs index 4ef9f95515..3147b4f6df 100644 --- a/src/datetime/serde.rs +++ b/src/datetime/serde.rs @@ -1213,6 +1213,92 @@ pub mod ts_seconds_option { } } +/// Ser/de to/from RFC 2822 strings +/// +/// Intended for use with `serde`'s `with` attribute. +/// +/// # Example: +/// +/// ```rust +/// use chrono::{DateTime, NaiveDate, FixedOffset, TimeZone}; +/// use serde_derive::{Deserialize, Serialize}; +/// +/// #[derive(Deserialize, Serialize)] +/// struct Example { +/// #[serde(with = "chrono::serde::rfc2822")] +/// time: DateTime +/// } +/// let offset = 3600; +/// let actual_time = NaiveDate::from_ymd_opt(2018, 5, 17).unwrap() +/// .and_hms_opt(02, 04, 59).unwrap() +/// .and_local_timezone(TimeZone::from_offset(&FixedOffset::east_opt(offset).unwrap())).unwrap(); +/// let to_serialize = Example { +/// time: actual_time.clone(), +/// }; +/// +/// let serialized = serde_json::to_string(&to_serialize).unwrap(); +/// assert_eq!(serialized, r#"{"time":"Thu, 17 May 2018 02:04:59 +0100"}"#); +/// +/// let deserialized: Example = serde_json::from_str(&serialized).unwrap(); +/// assert_eq!(deserialized.time, actual_time); +/// ``` +pub mod rfc2822 { + use crate::format::write_rfc2822; + use crate::{DateTime, FixedOffset, Offset, TimeZone}; + use core::fmt; + use serde::{de, ser}; + + /// Serialize a datetime into an RFC 2822 formatted string, e.g. "01 Jun 2016 14:31:46 -0700" + /// + /// Intended for use with `serde`s `serialize_with` attribute. + pub fn serialize(dt: &DateTime, serializer: S) -> Result + where + S: ser::Serializer, + { + struct FormatRfc2822<'a, Tz: TimeZone> { + inner: &'a DateTime, + } + + impl<'a, Tz: TimeZone> fmt::Display for FormatRfc2822<'a, Tz> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let naive = self.inner.naive_local(); + let offset = self.inner.offset.fix(); + write_rfc2822(f, naive, offset) + } + } + + serializer.collect_str(&FormatRfc2822 { inner: dt }) + } + + #[derive(Debug)] + struct Rfc2822Visitor; + + /// Deserialize a [`DateTime`] from an RFC 2822 datetime + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: de::Deserializer<'de>, + { + deserializer.deserialize_str(Rfc2822Visitor) + } + + impl<'de> de::Visitor<'de> for Rfc2822Visitor { + type Value = DateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an RFC 2822 formatted datetime string") + } + + fn visit_str(self, date_string: &str) -> Result + where + E: de::Error, + { + DateTime::parse_from_rfc2822(date_string).map_err(E::custom) + } + } +} + #[cfg(test)] mod tests { #[cfg(feature = "clock")] diff --git a/src/format/formatting.rs b/src/format/formatting.rs index f3448f94fe..b881bae015 100644 --- a/src/format/formatting.rs +++ b/src/format/formatting.rs @@ -18,7 +18,7 @@ use crate::{Datelike, FixedOffset, NaiveDateTime, Timelike}; #[cfg(feature = "alloc")] use crate::{NaiveDate, NaiveTime, Weekday}; -#[cfg(feature = "alloc")] +#[cfg(any(feature = "alloc", feature = "serde"))] use super::locales; #[cfg(all(feature = "unstable-locales", feature = "alloc"))] use super::Locale; @@ -26,7 +26,7 @@ use super::Locale; use super::{Colons, OffsetFormat, OffsetPrecision, Pad}; #[cfg(feature = "alloc")] use super::{Fixed, InternalFixed, InternalInternal, Item, Numeric}; -#[cfg(feature = "alloc")] +#[cfg(any(feature = "alloc", feature = "serde"))] use locales::*; /// A *temporary* object which can be used as an argument to `format!` or others. @@ -595,7 +595,7 @@ pub(crate) fn write_rfc3339( .format(w, off) } -#[cfg(feature = "alloc")] +#[cfg(any(feature = "alloc", feature = "serde"))] /// write datetimes like `Tue, 1 Jul 2003 10:52:37 +0200`, same as `%a, %d %b %Y %H:%M:%S %z` pub(crate) fn write_rfc2822( w: &mut impl Write, diff --git a/src/format/mod.rs b/src/format/mod.rs index 75bc02ee31..c4518fec1c 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -54,7 +54,7 @@ pub mod strftime; pub(crate) mod locales; pub(crate) use formatting::write_hundreds; -#[cfg(feature = "alloc")] +#[cfg(any(feature = "alloc", feature = "serde"))] pub(crate) use formatting::write_rfc2822; #[cfg(any(feature = "alloc", feature = "serde", feature = "rustc-serialize"))] pub(crate) use formatting::write_rfc3339;