Skip to content

Commit

Permalink
Add RFC 2822 serde module
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisranderson authored and pitdicker committed Mar 1, 2024
1 parent 0aa46dd commit b950bc4
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 4 deletions.
86 changes: 86 additions & 0 deletions src/datetime/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<FixedOffset>
/// }
/// 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<S>(dt: &DateTime<FixedOffset>, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
struct FormatRfc2822<'a, Tz: TimeZone> {
inner: &'a DateTime<Tz>,
}

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<DateTime<FixedOffset>, D::Error>
where
D: de::Deserializer<'de>,
{
deserializer.deserialize_str(Rfc2822Visitor)
}

impl<'de> de::Visitor<'de> for Rfc2822Visitor {
type Value = DateTime<FixedOffset>;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("an RFC 2822 formatted datetime string")
}

Check warning on line 1291 in src/datetime/serde.rs

View check run for this annotation

Codecov / codecov/patch

src/datetime/serde.rs#L1289-L1291

Added lines #L1289 - L1291 were not covered by tests

fn visit_str<E>(self, date_string: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
DateTime::parse_from_rfc2822(date_string).map_err(E::custom)
}
}
}

#[cfg(test)]
mod tests {
#[cfg(feature = "clock")]
Expand Down
6 changes: 3 additions & 3 deletions src/format/formatting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ 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;
#[cfg(any(feature = "alloc", feature = "serde", feature = "rustc-serialize"))]
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.
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/format/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit b950bc4

Please sign in to comment.