Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions src/datetime/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ pub struct MicroSecondsTimestampVisitor;
#[derive(Debug)]
pub struct MilliSecondsTimestampVisitor;

#[doc(hidden)]
#[derive(Debug)]
pub struct Rfc2822Visitor;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe you can make this private. The others are public for backwards compatibility.


/// Serialize into an ISO 8601 formatted string.
///
/// See [the `serde` module](./serde/index.html) for alternate
Expand Down Expand Up @@ -1168,6 +1172,77 @@ 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::serde::Rfc2822Visitor;
use crate::{DateTime, FixedOffset};
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,
{
serializer.serialize_str(&dt.to_rfc2822())

@pitdicker pitdicker Feb 5, 2024

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In format/formatting.rs there is a write_rfc2822 function that works without allocating. Using that will make this serializer work on no_std targets like the others (and make the CI pass).

You will have to adjust its #[cfg(feature = "alloc")] to also enable it with the serde feature.

}

/// 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 (email) formatted datetime string")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I would remove (email) .

}

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