Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RFC 2822 serde module #1477

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@
}
}

/// 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