diff --git a/src/datetime/serde.rs b/src/datetime/serde.rs index 4ef9f95515..77d12e3cda 100644 --- a/src/datetime/serde.rs +++ b/src/datetime/serde.rs @@ -6,6 +6,8 @@ use crate::format::{write_rfc3339, SecondsFormat}; #[cfg(feature = "clock")] use crate::offset::Local; use crate::offset::{FixedOffset, Offset, TimeZone, Utc}; +use crate::serde::invalid_ts; +use crate::{infallible, serde_module}; #[doc(hidden)] #[derive(Debug)] @@ -113,1104 +115,183 @@ impl<'de> de::Deserialize<'de> for DateTime { } } -/// Ser/de to/from timestamps in nanoseconds -/// -/// Intended for use with `serde`'s `with` attribute. -/// -/// # Example: -/// -/// ```rust -/// # use chrono::{DateTime, Utc, NaiveDate}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::serde::ts_nanoseconds; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_nanoseconds")] -/// time: DateTime, -/// } -/// -/// let time = NaiveDate::from_ymd_opt(2018, 5, 17) -/// .unwrap() -/// .and_hms_nano_opt(02, 04, 59, 918355733) -/// .unwrap() -/// .and_local_timezone(Utc) -/// .unwrap(); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_nanoseconds { - use core::fmt; - use serde::{de, ser}; - - use crate::serde::invalid_ts; - use crate::{DateTime, Utc}; - - use super::NanoSecondsTimestampVisitor; - - /// Serialize a UTC datetime into an integer number of nanoseconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Errors - /// - /// An `i64` with nanosecond precision can span a range of ~584 years. This function returns an - /// error on an out of range `DateTime`. - /// - /// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:44.0 and - /// 2262-04-11T23:47:16.854775804. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, Utc, NaiveDate}; - /// # use serde_derive::Serialize; - /// use chrono::serde::ts_nanoseconds::serialize as to_nano_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_nano_ts")] - /// time: DateTime, - /// } - /// - /// let my_s = S { - /// time: NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_nano_opt(02, 04, 59, 918355733) - /// .unwrap() - /// .and_local_timezone(Utc) - /// .unwrap(), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(dt: &DateTime, serializer: S) -> Result - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.timestamp_nanos_opt().ok_or(ser::Error::custom( - "value out of range for a timestamp with nanosecond precision", - ))?) - } - - /// Deserialize a [`DateTime`] from a nanosecond timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, TimeZone, Utc}; - /// # use serde_derive::Deserialize; - /// use chrono::serde::ts_nanoseconds::deserialize as from_nano_ts; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_nano_ts")] - /// time: DateTime, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?; - /// assert_eq!(my_s, S { time: Utc.timestamp_opt(1526522699, 918355733).unwrap() }); - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// assert_eq!(my_s, S { time: Utc.timestamp_opt(-1, 999_999_999).unwrap() }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(NanoSecondsTimestampVisitor) - } - - impl<'de> de::Visitor<'de> for NanoSecondsTimestampVisitor { - type Value = DateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in nanoseconds") - } - - /// Deserialize a timestamp in nanoseconds since the epoch - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp( - value.div_euclid(1_000_000_000), - (value.rem_euclid(1_000_000_000)) as u32, - ) - .ok_or_else(|| invalid_ts(value)) - } - - /// Deserialize a timestamp in nanoseconds since the epoch - fn visit_u64(self, value: u64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp((value / 1_000_000_000) as i64, (value % 1_000_000_000) as u32) - .ok_or_else(|| invalid_ts(value)) - } - } +serde_module! { + type: DateTime, + module: ts_nanoseconds, + module_opt: ts_nanoseconds_option, + doc: r##" +# Errors + +An `i64` with nanosecond precision can span a range of ~584 years. Serializing a `DateTime` beyond +that range returns an error. + +The dates that can be represented as nanoseconds are between 1677-09-21T00:12:44.0 and +2262-04-11T23:47:16.854775804. + +# Example + +```rust +# use chrono::{DateTime, Utc, NaiveDate}; +# use serde_derive::{Deserialize, Serialize}; +use chrono::serde::ts_nanoseconds; +#[derive(Deserialize, Serialize)] +struct S { + #[serde(with = "ts_nanoseconds")] + time: DateTime } -/// Ser/de to/from optional timestamps in nanoseconds -/// -/// Intended for use with `serde`'s `with` attribute. -/// -/// # Example: -/// -/// ```rust -/// # use chrono::{DateTime, Utc, NaiveDate}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::serde::ts_nanoseconds_option; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_nanoseconds_option")] -/// time: Option>, -/// } -/// -/// let time = Some( -/// NaiveDate::from_ymd_opt(2018, 5, 17) -/// .unwrap() -/// .and_hms_nano_opt(02, 04, 59, 918355733) -/// .unwrap() -/// .and_local_timezone(Utc) -/// .unwrap(), -/// ); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_nanoseconds_option { - use core::fmt; - use serde::{de, ser}; - - use crate::{DateTime, Utc}; - - use super::NanoSecondsTimestampVisitor; - - /// Serialize a UTC datetime into an integer number of nanoseconds since the epoch or none - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Errors - /// - /// An `i64` with nanosecond precision can span a range of ~584 years. This function returns an - /// error on an out of range `DateTime`. - /// - /// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:44.0 and - /// 2262-04-11T23:47:16.854775804. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, Utc, NaiveDate}; - /// # use serde_derive::Serialize; - /// use chrono::serde::ts_nanoseconds_option::serialize as to_nano_tsopt; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_nano_tsopt")] - /// time: Option>, - /// } - /// - /// let my_s = S { - /// time: Some( - /// NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_nano_opt(02, 04, 59, 918355733) - /// .unwrap() - /// .and_local_timezone(Utc) - /// .unwrap(), - /// ), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(opt: &Option>, serializer: S) -> Result - where - S: ser::Serializer, - { - match *opt { - Some(ref dt) => serializer.serialize_some(&dt.timestamp_nanos_opt().ok_or( - ser::Error::custom("value out of range for a timestamp with nanosecond precision"), - )?), - None => serializer.serialize_none(), - } - } - - /// Deserialize a `DateTime` from a nanosecond timestamp or none - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, TimeZone, Utc}; - /// # use serde_derive::Deserialize; - /// use chrono::serde::ts_nanoseconds_option::deserialize as from_nano_tsopt; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_nano_tsopt")] - /// time: Option>, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?; - /// assert_eq!(my_s, S { time: Utc.timestamp_opt(1526522699, 918355733).single() }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result>, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_option(OptionNanoSecondsTimestampVisitor) - } - - struct OptionNanoSecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for OptionNanoSecondsTimestampVisitor { - type Value = Option>; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in nanoseconds or none") - } - - /// Deserialize a timestamp in nanoseconds since the epoch - fn visit_some(self, d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(NanoSecondsTimestampVisitor).map(Some) - } - - /// Deserialize a timestamp in nanoseconds since the epoch - fn visit_none(self) -> Result - where - E: de::Error, - { - Ok(None) - } - - /// Deserialize a timestamp in nanoseconds since the epoch - fn visit_unit(self) -> Result - where - E: de::Error, - { - Ok(None) - } - } +let time = NaiveDate::from_ymd_opt(2018, 5, 17) + .unwrap() + .and_hms_nano_opt(02, 04, 59, 918355733) + .unwrap() + .and_utc(); +let my_s = S { + time: time.clone(), +}; + +let as_string = serde_json::to_string(&my_s)?; +assert_eq!(as_string, r#"{"time":1526522699918355733}"#); +let my_s: S = serde_json::from_str(&as_string)?; +assert_eq!(my_s.time, time); +# Ok::<(), serde_json::Error>(()) +```"##, + serialize_i64: |dt| DateTime::timestamp_nanos_opt(dt) + .ok_or("value out of range for a timestamp with nanosecond precision"), + deserialize_i64: NanoSecondsTimestampVisitor, + expecting: "a unix timestamp in nanoseconds", + visit_i64(i64): |value: i64| DateTime::from_timestamp( + value.div_euclid(1_000_000_000), + (value.rem_euclid(1_000_000_000)) as u32, + ) + .ok_or_else(|| invalid_ts(value)), + visit_u64(u64): |value: u64| DateTime::from_timestamp((value / 1_000_000_000) as i64, (value % 1_000_000_000) as u32) + .ok_or_else(|| invalid_ts(value)), } -/// Ser/de to/from timestamps in microseconds -/// -/// Intended for use with `serde`'s `with` attribute. -/// -/// # Example: -/// -/// ```rust -/// # use chrono::{DateTime, Utc, NaiveDate}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::serde::ts_microseconds; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_microseconds")] -/// time: DateTime, -/// } -/// -/// let time = NaiveDate::from_ymd_opt(2018, 5, 17) -/// .unwrap() -/// .and_hms_micro_opt(02, 04, 59, 918355) -/// .unwrap() -/// .and_local_timezone(Utc) -/// .unwrap(); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918355}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_microseconds { - use core::fmt; - use serde::{de, ser}; - - use crate::serde::invalid_ts; - use crate::{DateTime, Utc}; - - use super::MicroSecondsTimestampVisitor; - - /// Serialize a UTC datetime into an integer number of microseconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, Utc, NaiveDate}; - /// # use serde_derive::Serialize; - /// use chrono::serde::ts_microseconds::serialize as to_micro_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_micro_ts")] - /// time: DateTime, - /// } - /// - /// let my_s = S { - /// time: NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_micro_opt(02, 04, 59, 918355) - /// .unwrap() - /// .and_local_timezone(Utc) - /// .unwrap(), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(dt: &DateTime, serializer: S) -> Result - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.timestamp_micros()) - } - - /// Deserialize a `DateTime` from a microsecond timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, TimeZone, Utc}; - /// # use serde_derive::Deserialize; - /// use chrono::serde::ts_microseconds::deserialize as from_micro_ts; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_micro_ts")] - /// time: DateTime, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355 }"#)?; - /// assert_eq!(my_s, S { time: Utc.timestamp_opt(1526522699, 918355000).unwrap() }); - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// assert_eq!(my_s, S { time: Utc.timestamp_opt(-1, 999_999_000).unwrap() }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(MicroSecondsTimestampVisitor) - } - - impl<'de> de::Visitor<'de> for MicroSecondsTimestampVisitor { - type Value = DateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in microseconds") - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp( - value.div_euclid(1_000_000), - (value.rem_euclid(1_000_000) * 1000) as u32, - ) - .ok_or_else(|| invalid_ts(value)) - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_u64(self, value: u64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp( - (value / 1_000_000) as i64, - ((value % 1_000_000) * 1_000) as u32, - ) - .ok_or_else(|| invalid_ts(value)) - } - } +serde_module! { + type: DateTime, + module: ts_microseconds, + module_opt: ts_microseconds_option, + doc: r##" +# Example + +```rust +# use chrono::{DateTime, Utc, NaiveDate}; +# use serde_derive::{Deserialize, Serialize}; +use chrono::serde::ts_microseconds; +#[derive(Deserialize, Serialize)] +struct S { + #[serde(with = "ts_microseconds")] + time: DateTime } -/// Ser/de to/from optional timestamps in microseconds -/// -/// Intended for use with `serde`'s `with` attribute. -/// -/// # Example: -/// -/// ```rust -/// # use chrono::{DateTime, Utc, NaiveDate}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::serde::ts_microseconds_option; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_microseconds_option")] -/// time: Option>, -/// } -/// -/// let time = Some( -/// NaiveDate::from_ymd_opt(2018, 5, 17) -/// .unwrap() -/// .and_hms_micro_opt(02, 04, 59, 918355) -/// .unwrap() -/// .and_local_timezone(Utc) -/// .unwrap(), -/// ); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918355}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_microseconds_option { - use core::fmt; - use serde::{de, ser}; - - use super::MicroSecondsTimestampVisitor; - use crate::{DateTime, Utc}; - - /// Serialize a UTC datetime into an integer number of microseconds since the epoch or none - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, Utc, NaiveDate}; - /// # use serde_derive::Serialize; - /// use chrono::serde::ts_microseconds_option::serialize as to_micro_tsopt; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_micro_tsopt")] - /// time: Option>, - /// } - /// - /// let my_s = S { - /// time: Some( - /// NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_micro_opt(02, 04, 59, 918355) - /// .unwrap() - /// .and_local_timezone(Utc) - /// .unwrap(), - /// ), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(opt: &Option>, serializer: S) -> Result - where - S: ser::Serializer, - { - match *opt { - Some(ref dt) => serializer.serialize_some(&dt.timestamp_micros()), - None => serializer.serialize_none(), - } - } - - /// Deserialize a `DateTime` from a microsecond timestamp or none - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, TimeZone, Utc}; - /// # use serde_derive::Deserialize; - /// use chrono::serde::ts_microseconds_option::deserialize as from_micro_tsopt; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_micro_tsopt")] - /// time: Option>, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355 }"#)?; - /// assert_eq!(my_s, S { time: Utc.timestamp_opt(1526522699, 918355000).single() }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result>, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_option(OptionMicroSecondsTimestampVisitor) - } - - struct OptionMicroSecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for OptionMicroSecondsTimestampVisitor { - type Value = Option>; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in microseconds or none") - } - - /// Deserialize a timestamp in microseconds since the epoch - fn visit_some(self, d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(MicroSecondsTimestampVisitor).map(Some) - } - - /// Deserialize a timestamp in microseconds since the epoch - fn visit_none(self) -> Result - where - E: de::Error, - { - Ok(None) - } - - /// Deserialize a timestamp in microseconds since the epoch - fn visit_unit(self) -> Result - where - E: de::Error, - { - Ok(None) - } - } +let time = NaiveDate::from_ymd_opt(2018, 5, 17) + .unwrap() + .and_hms_micro_opt(02, 04, 59, 918355) + .unwrap() + .and_utc(); +let my_s = S { + time: time.clone(), +}; + +let as_string = serde_json::to_string(&my_s)?; +assert_eq!(as_string, r#"{"time":1526522699918355}"#); +let my_s: S = serde_json::from_str(&as_string)?; +assert_eq!(my_s.time, time); +# Ok::<(), serde_json::Error>(()) +```"##, + serialize_i64: infallible!(DateTime::timestamp_micros), + deserialize_i64: MicroSecondsTimestampVisitor, + expecting: "a unix timestamp in microseconds", + visit_i64(i64): |value: i64| DateTime::from_timestamp( + value.div_euclid(1_000_000), + (value.rem_euclid(1_000_000) * 1000) as u32, + ) + .ok_or_else(|| invalid_ts(value)), + visit_u64(u64): |value: u64| DateTime::from_timestamp( + (value / 1_000_000) as i64, + ((value % 1_000_000) * 1_000) as u32, + ) + .ok_or_else(|| invalid_ts(value)), } -/// Ser/de to/from timestamps in milliseconds -/// -/// Intended for use with `serde`s `with` attribute. -/// -/// # Example -/// -/// ```rust -/// # use chrono::{DateTime, Utc, NaiveDate}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::serde::ts_milliseconds; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_milliseconds")] -/// time: DateTime, -/// } -/// -/// let time = NaiveDate::from_ymd_opt(2018, 5, 17) -/// .unwrap() -/// .and_hms_milli_opt(02, 04, 59, 918) -/// .unwrap() -/// .and_local_timezone(Utc) -/// .unwrap(); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_milliseconds { - use core::fmt; - use serde::{de, ser}; - - use crate::serde::invalid_ts; - use crate::{DateTime, Utc}; - - use super::MilliSecondsTimestampVisitor; - - /// Serialize a UTC datetime into an integer number of milliseconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, Utc, NaiveDate}; - /// # use serde_derive::Serialize; - /// use chrono::serde::ts_milliseconds::serialize as to_milli_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_milli_ts")] - /// time: DateTime, - /// } - /// - /// let my_s = S { - /// time: NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_milli_opt(02, 04, 59, 918) - /// .unwrap() - /// .and_local_timezone(Utc) - /// .unwrap(), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(dt: &DateTime, serializer: S) -> Result - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.timestamp_millis()) - } - - /// Deserialize a `DateTime` from a millisecond timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, TimeZone, Utc}; - /// # use serde_derive::Deserialize; - /// use chrono::serde::ts_milliseconds::deserialize as from_milli_ts; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_milli_ts")] - /// time: DateTime, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?; - /// assert_eq!(my_s, S { time: Utc.timestamp_opt(1526522699, 918000000).unwrap() }); - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// assert_eq!(my_s, S { time: Utc.timestamp_opt(-1, 999_000_000).unwrap() }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(MilliSecondsTimestampVisitor).map(|dt| dt.with_timezone(&Utc)) - } - - impl<'de> de::Visitor<'de> for MilliSecondsTimestampVisitor { - type Value = DateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in milliseconds") - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp_millis(value).ok_or_else(|| invalid_ts(value)) - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_u64(self, value: u64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp((value / 1000) as i64, ((value % 1000) * 1_000_000) as u32) - .ok_or_else(|| invalid_ts(value)) - } - } +serde_module! { + type: DateTime, + module: ts_milliseconds, + module_opt: ts_milliseconds_option, + doc: r##" +# Example + +```rust +# use chrono::{DateTime, Utc, NaiveDate}; +# use serde_derive::{Deserialize, Serialize}; +use chrono::serde::ts_milliseconds; +#[derive(Deserialize, Serialize)] +struct S { + #[serde(with = "ts_milliseconds")] + time: DateTime } -/// Ser/de to/from optional timestamps in milliseconds -/// -/// Intended for use with `serde`s `with` attribute. -/// -/// # Example -/// -/// ```rust -/// # use chrono::{DateTime, Utc, NaiveDate}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::serde::ts_milliseconds_option; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_milliseconds_option")] -/// time: Option>, -/// } -/// -/// let time = Some( -/// NaiveDate::from_ymd_opt(2018, 5, 17) -/// .unwrap() -/// .and_hms_milli_opt(02, 04, 59, 918) -/// .unwrap() -/// .and_local_timezone(Utc) -/// .unwrap(), -/// ); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_milliseconds_option { - use core::fmt; - use serde::{de, ser}; - - use super::MilliSecondsTimestampVisitor; - use crate::{DateTime, Utc}; - - /// Serialize a UTC datetime into an integer number of milliseconds since the epoch or none - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, Utc, NaiveDate}; - /// # use serde_derive::Serialize; - /// use chrono::serde::ts_milliseconds_option::serialize as to_milli_tsopt; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_milli_tsopt")] - /// time: Option>, - /// } - /// - /// let my_s = S { - /// time: Some( - /// NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_milli_opt(02, 04, 59, 918) - /// .unwrap() - /// .and_local_timezone(Utc) - /// .unwrap(), - /// ), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(opt: &Option>, serializer: S) -> Result - where - S: ser::Serializer, - { - match *opt { - Some(ref dt) => serializer.serialize_some(&dt.timestamp_millis()), - None => serializer.serialize_none(), - } - } - - /// Deserialize a `DateTime` from a millisecond timestamp or none - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{TimeZone, DateTime, Utc}; - /// # use serde_derive::Deserialize; - /// use chrono::serde::ts_milliseconds_option::deserialize as from_milli_tsopt; - /// - /// #[derive(Deserialize, PartialEq, Debug)] - /// #[serde(untagged)] - /// enum E { - /// V(T), - /// } - /// - /// #[derive(Deserialize, PartialEq, Debug)] - /// struct S { - /// #[serde(default, deserialize_with = "from_milli_tsopt")] - /// time: Option>, - /// } - /// - /// let my_s: E = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?; - /// assert_eq!(my_s, E::V(S { time: Some(Utc.timestamp_opt(1526522699, 918000000).unwrap()) })); - /// let s: E = serde_json::from_str(r#"{ "time": null }"#)?; - /// assert_eq!(s, E::V(S { time: None })); - /// let t: E = serde_json::from_str(r#"{}"#)?; - /// assert_eq!(t, E::V(S { time: None })); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result>, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_option(OptionMilliSecondsTimestampVisitor) - .map(|opt| opt.map(|dt| dt.with_timezone(&Utc))) - } - - struct OptionMilliSecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for OptionMilliSecondsTimestampVisitor { - type Value = Option>; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in milliseconds or none") - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_some(self, d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(MilliSecondsTimestampVisitor).map(Some) - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_none(self) -> Result - where - E: de::Error, - { - Ok(None) - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_unit(self) -> Result - where - E: de::Error, - { - Ok(None) - } - } +let time = NaiveDate::from_ymd_opt(2018, 5, 17) + .unwrap() + .and_hms_milli_opt(02, 04, 59, 918) + .unwrap() + .and_utc(); +let my_s = S { + time: time.clone(), +}; + +let as_string = serde_json::to_string(&my_s)?; +assert_eq!(as_string, r#"{"time":1526522699918}"#); +let my_s: S = serde_json::from_str(&as_string)?; +assert_eq!(my_s.time, time); +# Ok::<(), serde_json::Error>(()) +```"##, + serialize_i64: infallible!(DateTime::timestamp_millis), + deserialize_i64: MilliSecondsTimestampVisitor, + expecting: "a unix timestamp in milliseconds", + visit_i64(i64): |value: i64| DateTime::from_timestamp_millis(value).ok_or_else(|| invalid_ts(value)), + visit_u64(u64): |value: u64| DateTime::from_timestamp((value / 1000) as i64, ((value % 1000) * 1_000_000) as u32) + .ok_or_else(|| invalid_ts(value)), } -/// Ser/de to/from timestamps in seconds -/// -/// Intended for use with `serde`'s `with` attribute. -/// -/// # Example: -/// -/// ```rust -/// # use chrono::{TimeZone, DateTime, Utc}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::serde::ts_seconds; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_seconds")] -/// time: DateTime, -/// } -/// -/// let time = Utc.with_ymd_and_hms(2015, 5, 15, 10, 0, 0).unwrap(); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1431684000}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_seconds { - use core::fmt; - use serde::{de, ser}; - - use crate::serde::invalid_ts; - use crate::{DateTime, Utc}; - - use super::SecondsTimestampVisitor; - - /// Serialize a UTC datetime into an integer number of seconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{TimeZone, DateTime, Utc}; - /// # use serde_derive::Serialize; - /// use chrono::serde::ts_seconds::serialize as to_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_ts")] - /// time: DateTime, - /// } - /// - /// let my_s = S { time: Utc.with_ymd_and_hms(2015, 5, 15, 10, 0, 0).unwrap() }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1431684000}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(dt: &DateTime, serializer: S) -> Result - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.timestamp()) - } - - /// Deserialize a `DateTime` from a seconds timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, TimeZone, Utc}; - /// # use serde_derive::Deserialize; - /// use chrono::serde::ts_seconds::deserialize as from_ts; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_ts")] - /// time: DateTime, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?; - /// assert_eq!(my_s, S { time: Utc.timestamp_opt(1431684000, 0).unwrap() }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(SecondsTimestampVisitor) - } - - impl<'de> de::Visitor<'de> for SecondsTimestampVisitor { - type Value = DateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in seconds") - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp(value, 0).ok_or_else(|| invalid_ts(value)) - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_u64(self, value: u64) -> Result - where - E: de::Error, - { - if value > i64::MAX as u64 { - Err(invalid_ts(value)) - } else { - DateTime::from_timestamp(value as i64, 0).ok_or_else(|| invalid_ts(value)) - } - } - } +serde_module! { + type: DateTime, + module: ts_seconds, + module_opt: ts_seconds_option, + doc: r##" +# Example + +```rust +# use chrono::{TimeZone, DateTime, Utc}; +# use serde_derive::{Deserialize, Serialize}; +use chrono::serde::ts_seconds; +#[derive(Deserialize, Serialize)] +struct S { + #[serde(with = "ts_seconds")] + time: DateTime } -/// Ser/de to/from optional timestamps in seconds -/// -/// Intended for use with `serde`'s `with` attribute. -/// -/// # Example: -/// -/// ```rust -/// # use chrono::{TimeZone, DateTime, Utc}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::serde::ts_seconds_option; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_seconds_option")] -/// time: Option>, -/// } -/// -/// let time = Some(Utc.with_ymd_and_hms(2015, 5, 15, 10, 0, 0).unwrap()); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1431684000}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_seconds_option { - use core::fmt; - use serde::{de, ser}; - - use super::SecondsTimestampVisitor; - use crate::{DateTime, Utc}; - - /// Serialize a UTC datetime into an integer number of seconds since the epoch or none - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{TimeZone, DateTime, Utc}; - /// # use serde_derive::Serialize; - /// use chrono::serde::ts_seconds_option::serialize as to_tsopt; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_tsopt")] - /// time: Option>, - /// } - /// - /// let my_s = S { time: Some(Utc.with_ymd_and_hms(2015, 5, 15, 10, 0, 0).unwrap()) }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1431684000}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(opt: &Option>, serializer: S) -> Result - where - S: ser::Serializer, - { - match *opt { - Some(ref dt) => serializer.serialize_some(&dt.timestamp()), - None => serializer.serialize_none(), - } - } - - /// Deserialize a `DateTime` from a seconds timestamp or none - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, TimeZone, Utc}; - /// # use serde_derive::Deserialize; - /// use chrono::serde::ts_seconds_option::deserialize as from_tsopt; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_tsopt")] - /// time: Option>, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?; - /// assert_eq!(my_s, S { time: Utc.timestamp_opt(1431684000, 0).single() }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result>, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_option(OptionSecondsTimestampVisitor) - } - - struct OptionSecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for OptionSecondsTimestampVisitor { - type Value = Option>; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in seconds or none") - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_some(self, d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(SecondsTimestampVisitor).map(Some) - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_none(self) -> Result - where - E: de::Error, - { - Ok(None) - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_unit(self) -> Result - where - E: de::Error, - { - Ok(None) - } - } +let time = Utc.with_ymd_and_hms(2015, 5, 15, 10, 0, 0).unwrap(); +let my_s = S { + time: time.clone(), +}; + +let as_string = serde_json::to_string(&my_s)?; +assert_eq!(as_string, r#"{"time":1431684000}"#); +let my_s: S = serde_json::from_str(&as_string)?; +assert_eq!(my_s.time, time); +# Ok::<(), serde_json::Error>(()) +```"##, + serialize_i64: infallible!(DateTime::timestamp), + deserialize_i64: SecondsTimestampVisitor, + expecting: "a unix timestamp in seconds", + visit_i64(i64): |value: i64| DateTime::from_timestamp(value, 0).ok_or_else(|| invalid_ts(value)), + visit_u64(u64): |value: u64| if value > i64::MAX as u64 { + Err(invalid_ts(value)) + } else { + DateTime::from_timestamp(value as i64, 0).ok_or_else(|| invalid_ts(value)) + }, } #[cfg(test)] diff --git a/src/lib.rs b/src/lib.rs index ad2312b877..b4258e8f08 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -618,45 +618,8 @@ pub use traits::{Datelike, Timelike}; #[doc(hidden)] pub use naive::__BenchYearFlags; -/// Serialization/Deserialization with serde. -/// -/// This module provides default implementations for `DateTime` using the [RFC 3339][1] format and various -/// alternatives for use with serde's [`with` annotation][2]. -/// -/// *Available on crate feature 'serde' only.* -/// -/// [1]: https://tools.ietf.org/html/rfc3339 -/// [2]: https://serde.rs/field-attrs.html#with #[cfg(feature = "serde")] -pub mod serde { - use core::fmt; - use serde::de; - - pub use super::datetime::serde::*; - - /// Create a custom `de::Error` with `SerdeError::InvalidTimestamp`. - pub(crate) fn invalid_ts(value: T) -> E - where - E: de::Error, - T: fmt::Display, - { - E::custom(SerdeError::InvalidTimestamp(value)) - } - - enum SerdeError { - InvalidTimestamp(T), - } - - impl fmt::Display for SerdeError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - SerdeError::InvalidTimestamp(ts) => { - write!(f, "value is not a legal timestamp: {}", ts) - } - } - } - } -} +pub mod serde; /// Zero-copy serialization/deserialization with rkyv. /// diff --git a/src/naive/datetime/serde.rs b/src/naive/datetime/serde.rs index de8abd7dc5..690c0d1c2d 100644 --- a/src/naive/datetime/serde.rs +++ b/src/naive/datetime/serde.rs @@ -2,6 +2,9 @@ use core::fmt; use serde::{de, ser}; use super::NaiveDateTime; +use crate::serde::invalid_ts; +use crate::DateTime; +use crate::{infallible, serde_module}; /// Serialize a `NaiveDateTime` as an RFC 3339 string /// @@ -52,1080 +55,183 @@ impl<'de> de::Deserialize<'de> for NaiveDateTime { } } -/// Used to serialize/deserialize from nanosecond-precision timestamps -/// -/// # Example: -/// -/// ```rust -/// # use chrono::{NaiveDate, NaiveDateTime}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::naive::serde::ts_nanoseconds; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_nanoseconds")] -/// time: NaiveDateTime, -/// } -/// -/// let time = NaiveDate::from_ymd_opt(2018, 5, 17) -/// .unwrap() -/// .and_hms_nano_opt(02, 04, 59, 918355733) -/// .unwrap(); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_nanoseconds { - use core::fmt; - use serde::{de, ser}; - - use crate::serde::invalid_ts; - use crate::{DateTime, NaiveDateTime}; - - /// Serialize a datetime into an integer number of nanoseconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Errors - /// - /// An `i64` with nanosecond precision can span a range of ~584 years. This function returns an - /// error on an out of range `DateTime`. - /// - /// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:44.0 and - /// 2262-04-11T23:47:16.854775804. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{NaiveDate, NaiveDateTime}; - /// # use serde_derive::Serialize; - /// use chrono::naive::serde::ts_nanoseconds::serialize as to_nano_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_nano_ts")] - /// time: NaiveDateTime, - /// } - /// - /// let my_s = S { - /// time: NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_nano_opt(02, 04, 59, 918355733) - /// .unwrap(), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(dt: &NaiveDateTime, serializer: S) -> Result - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.and_utc().timestamp_nanos_opt().ok_or(ser::Error::custom( - "value out of range for a timestamp with nanosecond precision", - ))?) - } - - /// Deserialize a `NaiveDateTime` from a nanoseconds timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, NaiveDateTime}; - /// # use serde_derive::Deserialize; - /// use chrono::naive::serde::ts_nanoseconds::deserialize as from_nano_ts; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_nano_ts")] - /// time: NaiveDateTime, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?; - /// let expected = DateTime::from_timestamp(1526522699, 918355733).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: expected }); - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// let expected = DateTime::from_timestamp(-1, 999_999_999).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: expected }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(NanoSecondsTimestampVisitor) - } +struct NanoSecondsTimestampVisitor; - pub(super) struct NanoSecondsTimestampVisitor; +serde_module! { + type: NaiveDateTime, + module: ts_nanoseconds, + module_opt: ts_nanoseconds_option, + doc: r##" +# Errors - impl<'de> de::Visitor<'de> for NanoSecondsTimestampVisitor { - type Value = NaiveDateTime; +An `i64` with nanosecond precision can span a range of ~584 years. Serializing a `NaiveDateTime` +beyond that range returns an error. - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp") - } +The dates that can be represented as nanoseconds are between 1677-09-21T00:12:44.0 and +2262-04-11T23:47:16.854775804. - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp( - value.div_euclid(1_000_000_000), - (value.rem_euclid(1_000_000_000)) as u32, - ) - .map(|dt| dt.naive_utc()) - .ok_or_else(|| invalid_ts(value)) - } +# Example - fn visit_u64(self, value: u64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp((value / 1_000_000_000) as i64, (value % 1_000_000_000) as u32) - .map(|dt| dt.naive_utc()) - .ok_or_else(|| invalid_ts(value)) - } - } +```rust +# use chrono::{NaiveDate, NaiveDateTime}; +# use serde_derive::{Deserialize, Serialize}; +use chrono::naive::serde::ts_nanoseconds; +#[derive(Deserialize, Serialize)] +struct S { + #[serde(with = "ts_nanoseconds")] + time: NaiveDateTime } -/// Ser/de to/from optional timestamps in nanoseconds -/// -/// Intended for use with `serde`'s `with` attribute. -/// -/// # Example: -/// -/// ```rust -/// # use chrono::naive::{NaiveDate, NaiveDateTime}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::naive::serde::ts_nanoseconds_option; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_nanoseconds_option")] -/// time: Option, -/// } -/// -/// let time = Some( -/// NaiveDate::from_ymd_opt(2018, 5, 17) -/// .unwrap() -/// .and_hms_nano_opt(02, 04, 59, 918355733) -/// .unwrap(), -/// ); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_nanoseconds_option { - use core::fmt; - use serde::{de, ser}; - - use super::ts_nanoseconds::NanoSecondsTimestampVisitor; - use crate::NaiveDateTime; - - /// Serialize a datetime into an integer number of nanoseconds since the epoch or none - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Errors - /// - /// An `i64` with nanosecond precision can span a range of ~584 years. This function returns an - /// error on an out of range `DateTime`. - /// - /// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:44.0 and - /// 2262-04-11T23:47:16.854775804. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::naive::{NaiveDate, NaiveDateTime}; - /// # use serde_derive::Serialize; - /// use chrono::naive::serde::ts_nanoseconds_option::serialize as to_nano_tsopt; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_nano_tsopt")] - /// time: Option, - /// } - /// - /// let my_s = S { - /// time: Some( - /// NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_nano_opt(02, 04, 59, 918355733) - /// .unwrap(), - /// ), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(opt: &Option, serializer: S) -> Result - where - S: ser::Serializer, - { - match *opt { - Some(ref dt) => serializer.serialize_some(&dt.and_utc().timestamp_nanos_opt().ok_or( - ser::Error::custom("value out of range for a timestamp with nanosecond precision"), - )?), - None => serializer.serialize_none(), - } - } - - /// Deserialize a `NaiveDateTime` from a nanosecond timestamp or none - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, NaiveDateTime}; - /// # use serde_derive::Deserialize; - /// use chrono::naive::serde::ts_nanoseconds_option::deserialize as from_nano_tsopt; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_nano_tsopt")] - /// time: Option, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?; - /// let expected = DateTime::from_timestamp(1526522699, 918355733).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: Some(expected) }); - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// let expected = DateTime::from_timestamp(-1, 999_999_999).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: Some(expected) }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_option(OptionNanoSecondsTimestampVisitor) - } - - struct OptionNanoSecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for OptionNanoSecondsTimestampVisitor { - type Value = Option; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in nanoseconds or none") - } - - /// Deserialize a timestamp in nanoseconds since the epoch - fn visit_some(self, d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(NanoSecondsTimestampVisitor).map(Some) - } - - /// Deserialize a timestamp in nanoseconds since the epoch - fn visit_none(self) -> Result - where - E: de::Error, - { - Ok(None) - } - - /// Deserialize a timestamp in nanoseconds since the epoch - fn visit_unit(self) -> Result - where - E: de::Error, - { - Ok(None) - } - } +let time = NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_nano_opt(02, 04, 59, 918355733).unwrap(); +let my_s = S { + time: time.clone(), +}; + +let as_string = serde_json::to_string(&my_s)?; +assert_eq!(as_string, r#"{"time":1526522699918355733}"#); +let my_s: S = serde_json::from_str(&as_string)?; +assert_eq!(my_s.time, time); +# Ok::<(), serde_json::Error>(()) +```"##, + serialize_i64: |dt| NaiveDateTime::and_utc(dt).timestamp_nanos_opt() + .ok_or("value out of range for a timestamp with nanosecond precision"), + deserialize_i64: NanoSecondsTimestampVisitor, + expecting: "a unix timestamp in nanoseconds", + visit_i64(i64): |value: i64| DateTime::from_timestamp( + value.div_euclid(1_000_000_000), + (value.rem_euclid(1_000_000_000)) as u32, + ) + .map(|dt| dt.naive_utc()) + .ok_or_else(|| invalid_ts(value)), + visit_u64(u64): |value: u64| DateTime::from_timestamp((value / 1_000_000_000) as i64, (value % 1_000_000_000) as u32) + .map(|dt| dt.naive_utc()) + .ok_or_else(|| invalid_ts(value)), } -/// Used to serialize/deserialize from microsecond-precision timestamps -/// -/// # Example: -/// -/// ```rust -/// # use chrono::{NaiveDate, NaiveDateTime}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::naive::serde::ts_microseconds; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_microseconds")] -/// time: NaiveDateTime, -/// } -/// -/// let time = NaiveDate::from_ymd_opt(2018, 5, 17) -/// .unwrap() -/// .and_hms_micro_opt(02, 04, 59, 918355) -/// .unwrap(); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918355}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_microseconds { - use core::fmt; - use serde::{de, ser}; - - use crate::serde::invalid_ts; - use crate::{DateTime, NaiveDateTime}; - - /// Serialize a datetime into an integer number of microseconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{NaiveDate, NaiveDateTime}; - /// # use serde_derive::Serialize; - /// use chrono::naive::serde::ts_microseconds::serialize as to_micro_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_micro_ts")] - /// time: NaiveDateTime, - /// } - /// - /// let my_s = S { - /// time: NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_micro_opt(02, 04, 59, 918355) - /// .unwrap(), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(dt: &NaiveDateTime, serializer: S) -> Result - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.and_utc().timestamp_micros()) - } - - /// Deserialize a `NaiveDateTime` from a microseconds timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, NaiveDateTime}; - /// # use serde_derive::Deserialize; - /// use chrono::naive::serde::ts_microseconds::deserialize as from_micro_ts; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_micro_ts")] - /// time: NaiveDateTime, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355 }"#)?; - /// let expected = DateTime::from_timestamp(1526522699, 918355000).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: expected }); - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// let expected = DateTime::from_timestamp(-1, 999_999_000).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: expected }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(MicroSecondsTimestampVisitor) - } - - pub(super) struct MicroSecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for MicroSecondsTimestampVisitor { - type Value = NaiveDateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp") - } - - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp_micros(value) - .map(|dt| dt.naive_utc()) - .ok_or_else(|| invalid_ts(value)) - } - - fn visit_u64(self, value: u64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp( - (value / 1_000_000) as i64, - ((value % 1_000_000) * 1_000) as u32, - ) - .map(|dt| dt.naive_utc()) - .ok_or_else(|| invalid_ts(value)) - } - } +struct MicroSecondsTimestampVisitor; + +serde_module! { + type: NaiveDateTime, + module: ts_microseconds, + module_opt: ts_microseconds_option, + doc: r##" +# Example + +```rust +# use chrono::{NaiveDate, NaiveDateTime}; +# use serde_derive::{Deserialize, Serialize}; +use chrono::naive::serde::ts_microseconds; +#[derive(Deserialize, Serialize)] +struct S { + #[serde(with = "ts_microseconds")] + time: NaiveDateTime } -/// Ser/de to/from optional timestamps in microseconds -/// -/// Intended for use with `serde`'s `with` attribute. -/// -/// # Example: -/// -/// ```rust -/// # use chrono::naive::{NaiveDate, NaiveDateTime}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::naive::serde::ts_microseconds_option; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_microseconds_option")] -/// time: Option, -/// } -/// -/// let time = Some( -/// NaiveDate::from_ymd_opt(2018, 5, 17) -/// .unwrap() -/// .and_hms_micro_opt(02, 04, 59, 918355) -/// .unwrap(), -/// ); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918355}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_microseconds_option { - use core::fmt; - use serde::{de, ser}; - - use super::ts_microseconds::MicroSecondsTimestampVisitor; - use crate::NaiveDateTime; - - /// Serialize a datetime into an integer number of microseconds since the epoch or none - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::naive::{NaiveDate, NaiveDateTime}; - /// # use serde_derive::Serialize; - /// use chrono::naive::serde::ts_microseconds_option::serialize as to_micro_tsopt; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_micro_tsopt")] - /// time: Option, - /// } - /// - /// let my_s = S { - /// time: Some( - /// NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_micro_opt(02, 04, 59, 918355) - /// .unwrap(), - /// ), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(opt: &Option, serializer: S) -> Result - where - S: ser::Serializer, - { - match *opt { - Some(ref dt) => serializer.serialize_some(&dt.and_utc().timestamp_micros()), - None => serializer.serialize_none(), - } - } - - /// Deserialize a `NaiveDateTime` from a nanosecond timestamp or none - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, NaiveDateTime}; - /// # use serde_derive::Deserialize; - /// use chrono::naive::serde::ts_microseconds_option::deserialize as from_micro_tsopt; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_micro_tsopt")] - /// time: Option, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355 }"#)?; - /// let expected = DateTime::from_timestamp(1526522699, 918355000).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: Some(expected) }); - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// let expected = DateTime::from_timestamp(-1, 999_999_000).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: Some(expected) }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_option(OptionMicroSecondsTimestampVisitor) - } - - struct OptionMicroSecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for OptionMicroSecondsTimestampVisitor { - type Value = Option; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in microseconds or none") - } - - /// Deserialize a timestamp in microseconds since the epoch - fn visit_some(self, d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(MicroSecondsTimestampVisitor).map(Some) - } - - /// Deserialize a timestamp in microseconds since the epoch - fn visit_none(self) -> Result - where - E: de::Error, - { - Ok(None) - } - - /// Deserialize a timestamp in microseconds since the epoch - fn visit_unit(self) -> Result - where - E: de::Error, - { - Ok(None) - } - } +let time = NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_micro_opt(02, 04, 59, 918355).unwrap(); +let my_s = S { + time: time.clone(), +}; + +let as_string = serde_json::to_string(&my_s)?; +assert_eq!(as_string, r#"{"time":1526522699918355}"#); +let my_s: S = serde_json::from_str(&as_string)?; +assert_eq!(my_s.time, time); +# Ok::<(), serde_json::Error>(()) +```"##, + serialize_i64: infallible!(|dt| NaiveDateTime::and_utc(dt).timestamp_micros()), + deserialize_i64: MicroSecondsTimestampVisitor, + expecting: "a unix timestamp in microseconds", + visit_i64(i64): |value: i64| DateTime::from_timestamp_micros(value) + .map(|dt| dt.naive_utc()) + .ok_or_else(|| invalid_ts(value)), + visit_u64(u64): |value: u64| DateTime::from_timestamp( + (value / 1_000_000) as i64, + ((value % 1_000_000) * 1_000) as u32, + ) + .map(|dt| dt.naive_utc()) + .ok_or_else(|| invalid_ts(value)), } -/// Used to serialize/deserialize from millisecond-precision timestamps -/// -/// # Example: -/// -/// ```rust -/// # use chrono::{NaiveDate, NaiveDateTime}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::naive::serde::ts_milliseconds; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_milliseconds")] -/// time: NaiveDateTime, -/// } -/// -/// let time = -/// NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_milli_opt(02, 04, 59, 918).unwrap(); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_milliseconds { - use core::fmt; - use serde::{de, ser}; - - use crate::serde::invalid_ts; - use crate::{DateTime, NaiveDateTime}; - - /// Serialize a datetime into an integer number of milliseconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{NaiveDate, NaiveDateTime}; - /// # use serde_derive::Serialize; - /// use chrono::naive::serde::ts_milliseconds::serialize as to_milli_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_milli_ts")] - /// time: NaiveDateTime, - /// } - /// - /// let my_s = S { - /// time: NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_milli_opt(02, 04, 59, 918) - /// .unwrap(), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(dt: &NaiveDateTime, serializer: S) -> Result - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.and_utc().timestamp_millis()) - } - - /// Deserialize a `NaiveDateTime` from a milliseconds timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, NaiveDateTime}; - /// # use serde_derive::Deserialize; - /// use chrono::naive::serde::ts_milliseconds::deserialize as from_milli_ts; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_milli_ts")] - /// time: NaiveDateTime, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?; - /// let expected = DateTime::from_timestamp(1526522699, 918000000).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: expected }); - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// let expected = DateTime::from_timestamp(-1, 999_000_000).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: expected }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(MilliSecondsTimestampVisitor) - } - - pub(super) struct MilliSecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for MilliSecondsTimestampVisitor { - type Value = NaiveDateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp") - } - - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp_millis(value) - .map(|dt| dt.naive_utc()) - .ok_or_else(|| invalid_ts(value)) - } - - fn visit_u64(self, value: u64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp((value / 1000) as i64, ((value % 1000) * 1_000_000) as u32) - .map(|dt| dt.naive_utc()) - .ok_or_else(|| invalid_ts(value)) - } - } +struct MilliSecondsTimestampVisitor; + +serde_module! { + type: NaiveDateTime, + module: ts_milliseconds, + module_opt: ts_milliseconds_option, + doc: r##" +# Example + +```rust +# use chrono::{NaiveDate, NaiveDateTime}; +# use serde_derive::{Deserialize, Serialize}; +use chrono::naive::serde::ts_milliseconds; +#[derive(Deserialize, Serialize)] +struct S { + #[serde(with = "ts_milliseconds")] + time: NaiveDateTime } -/// Ser/de to/from optional timestamps in milliseconds -/// -/// Intended for use with `serde`'s `with` attribute. -/// -/// # Example: -/// -/// ```rust -/// # use chrono::naive::{NaiveDate, NaiveDateTime}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::naive::serde::ts_milliseconds_option; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_milliseconds_option")] -/// time: Option, -/// } -/// -/// let time = Some( -/// NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_milli_opt(02, 04, 59, 918).unwrap(), -/// ); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_milliseconds_option { - use core::fmt; - use serde::{de, ser}; - - use super::ts_milliseconds::MilliSecondsTimestampVisitor; - use crate::NaiveDateTime; - - /// Serialize a datetime into an integer number of milliseconds since the epoch or none - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::naive::{NaiveDate, NaiveDateTime}; - /// # use serde_derive::Serialize; - /// use chrono::naive::serde::ts_milliseconds_option::serialize as to_milli_tsopt; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_milli_tsopt")] - /// time: Option, - /// } - /// - /// let my_s = S { - /// time: Some( - /// NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_milli_opt(02, 04, 59, 918) - /// .unwrap(), - /// ), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(opt: &Option, serializer: S) -> Result - where - S: ser::Serializer, - { - match *opt { - Some(ref dt) => serializer.serialize_some(&dt.and_utc().timestamp_millis()), - None => serializer.serialize_none(), - } - } - - /// Deserialize a `NaiveDateTime` from a millisecond timestamp or none - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, NaiveDateTime}; - /// # use serde_derive::Deserialize; - /// use chrono::naive::serde::ts_milliseconds_option::deserialize as from_milli_tsopt; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_milli_tsopt")] - /// time: Option, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?; - /// let expected = DateTime::from_timestamp(1526522699, 918000000).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: Some(expected) }); - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// let expected = DateTime::from_timestamp(-1, 999_000_000).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: Some(expected) }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_option(OptionMilliSecondsTimestampVisitor) - } - - struct OptionMilliSecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for OptionMilliSecondsTimestampVisitor { - type Value = Option; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in milliseconds or none") - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_some(self, d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(MilliSecondsTimestampVisitor).map(Some) - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_none(self) -> Result - where - E: de::Error, - { - Ok(None) - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_unit(self) -> Result - where - E: de::Error, - { - Ok(None) - } - } +let time = NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_milli_opt(02, 04, 59, 918).unwrap(); +let my_s = S { + time: time.clone(), +}; + +let as_string = serde_json::to_string(&my_s)?; +assert_eq!(as_string, r#"{"time":1526522699918}"#); +let my_s: S = serde_json::from_str(&as_string)?; +assert_eq!(my_s.time, time); +# Ok::<(), serde_json::Error>(()) +```"##, + serialize_i64: infallible!(|dt| NaiveDateTime::and_utc(dt).timestamp_millis()), + deserialize_i64: MilliSecondsTimestampVisitor, + expecting: "a unix timestamp in milliseconds", + visit_i64(i64): |value: i64| DateTime::from_timestamp_millis(value) + .map(|dt| dt.naive_utc()) + .ok_or_else(|| invalid_ts(value)), + visit_u64(u64): |value: u64| DateTime::from_timestamp((value / 1000) as i64, ((value % 1000) * 1_000_000) as u32) + .map(|dt| dt.naive_utc()) + .ok_or_else(|| invalid_ts(value)), } -/// Used to serialize/deserialize from second-precision timestamps -/// -/// # Example: -/// -/// ```rust -/// # use chrono::{NaiveDate, NaiveDateTime}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::naive::serde::ts_seconds; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_seconds")] -/// time: NaiveDateTime, -/// } -/// -/// let time = NaiveDate::from_ymd_opt(2015, 5, 15).unwrap().and_hms_opt(10, 0, 0).unwrap(); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1431684000}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_seconds { - use core::fmt; - use serde::{de, ser}; - - use crate::serde::invalid_ts; - use crate::{DateTime, NaiveDateTime}; - - /// Serialize a datetime into an integer number of seconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{NaiveDate, NaiveDateTime}; - /// # use serde_derive::Serialize; - /// use chrono::naive::serde::ts_seconds::serialize as to_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_ts")] - /// time: NaiveDateTime, - /// } - /// - /// let my_s = - /// S { time: NaiveDate::from_ymd_opt(2015, 5, 15).unwrap().and_hms_opt(10, 0, 0).unwrap() }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1431684000}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(dt: &NaiveDateTime, serializer: S) -> Result - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.and_utc().timestamp()) - } - - /// Deserialize a `NaiveDateTime` from a seconds timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, NaiveDateTime}; - /// # use serde_derive::Deserialize; - /// use chrono::naive::serde::ts_seconds::deserialize as from_ts; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_ts")] - /// time: NaiveDateTime, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?; - /// let expected = DateTime::from_timestamp(1431684000, 0).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: expected }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(SecondsTimestampVisitor) - } - - pub(super) struct SecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for SecondsTimestampVisitor { - type Value = NaiveDateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp") - } - - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp(value, 0) - .map(|dt| dt.naive_utc()) - .ok_or_else(|| invalid_ts(value)) - } - - fn visit_u64(self, value: u64) -> Result - where - E: de::Error, - { - if value > i64::MAX as u64 { - Err(invalid_ts(value)) - } else { - DateTime::from_timestamp(value as i64, 0) - .map(|dt| dt.naive_utc()) - .ok_or_else(|| invalid_ts(value)) - } - } - } +struct SecondsTimestampVisitor; + +serde_module! { + type: NaiveDateTime, + module: ts_seconds, + module_opt: ts_seconds_option, + doc: r##" +# Example + +```rust +# use chrono::{NaiveDate, NaiveDateTime}; +# use serde_derive::{Deserialize, Serialize}; +use chrono::naive::serde::ts_seconds; +#[derive(Deserialize, Serialize)] +struct S { + #[serde(with = "ts_seconds")] + time: NaiveDateTime } -/// Ser/de to/from optional timestamps in seconds -/// -/// Intended for use with `serde`'s `with` attribute. -/// -/// # Example: -/// -/// ```rust -/// # use chrono::naive::{NaiveDate, NaiveDateTime}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::naive::serde::ts_seconds_option; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_seconds_option")] -/// time: Option, -/// } -/// -/// let time = Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_opt(02, 04, 59).unwrap()); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_seconds_option { - use core::fmt; - use serde::{de, ser}; - - use super::ts_seconds::SecondsTimestampVisitor; - use crate::NaiveDateTime; - - /// Serialize a datetime into an integer number of seconds since the epoch or none - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::naive::{NaiveDate, NaiveDateTime}; - /// # use serde_derive::Serialize; - /// use chrono::naive::serde::ts_seconds_option::serialize as to_tsopt; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_tsopt")] - /// time: Option, - /// } - /// - /// let expected = - /// NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_opt(02, 04, 59).unwrap(); - /// let my_s = S { time: Some(expected) }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(opt: &Option, serializer: S) -> Result - where - S: ser::Serializer, - { - match *opt { - Some(ref dt) => serializer.serialize_some(&dt.and_utc().timestamp()), - None => serializer.serialize_none(), - } - } - - /// Deserialize a `NaiveDateTime` from a second timestamp or none - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, NaiveDateTime}; - /// # use serde_derive::Deserialize; - /// use chrono::naive::serde::ts_seconds_option::deserialize as from_tsopt; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_tsopt")] - /// time: Option, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?; - /// let expected = DateTime::from_timestamp(1431684000, 0).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: Some(expected) }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_option(OptionSecondsTimestampVisitor) - } - - struct OptionSecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for OptionSecondsTimestampVisitor { - type Value = Option; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in seconds or none") - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_some(self, d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(SecondsTimestampVisitor).map(Some) - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_none(self) -> Result - where - E: de::Error, - { - Ok(None) - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_unit(self) -> Result - where - E: de::Error, - { - Ok(None) - } - } +let time = NaiveDate::from_ymd_opt(2015, 5, 15).unwrap().and_hms_opt(10, 0, 0).unwrap(); +let my_s = S { + time: time.clone(), +}; + +let as_string = serde_json::to_string(&my_s)?; +assert_eq!(as_string, r#"{"time":1431684000}"#); +let my_s: S = serde_json::from_str(&as_string)?; +assert_eq!(my_s.time, time); +# Ok::<(), serde_json::Error>(()) +```"##, + serialize_i64: infallible!(|dt| NaiveDateTime::and_utc(dt).timestamp()), + deserialize_i64: SecondsTimestampVisitor, + expecting: "a unix timestamp", + visit_i64(i64): |value: i64| DateTime::from_timestamp(value, 0) + .map(|dt| dt.naive_utc()) + .ok_or_else(|| invalid_ts(value)), + visit_u64(u64): |value: u64| DateTime::from_timestamp(value as i64, 0) + .map(|dt| dt.naive_utc()) + .ok_or_else(|| invalid_ts(value)), } #[cfg(test)] diff --git a/src/naive/mod.rs b/src/naive/mod.rs index 4b33a5594d..6e5422f334 100644 --- a/src/naive/mod.rs +++ b/src/naive/mod.rs @@ -141,14 +141,18 @@ impl Days { } } -/// Serialization/Deserialization of naive types in alternate formats +/// Serialization/Deserialization of naive types in alternate formats. /// -/// The various modules in here are intended to be used with serde's [`with` -/// annotation][1] to serialize as something other than the default [RFC -/// 3339][2] format. +/// The various modules in here are intended to be used to serialize as something other than the +/// default ISO 8601 format. /// -/// [1]: https://serde.rs/attributes.html#field-attributes -/// [2]: https://tools.ietf.org/html/rfc3339 +/// They can be used by combining the module name with serde's [`with` annotation]. +/// Alternatively the individual `serialize` and `deserialize` functions in each module can be used +/// with serde's [`serialize_with`] and [`deserialize_with`] annotations. +/// +/// [`with` annotation]: https://serde.rs/attributes.html#field-attributes +/// [`serialize_with`]: https://serde.rs/field-attrs.html#serialize_with +/// [`deserialize_with`]: https://serde.rs/field-attrs.html#deserialize_with #[cfg(feature = "serde")] pub mod serde { pub use super::datetime::serde::*; diff --git a/src/serde.rs b/src/serde.rs new file mode 100644 index 0000000000..23e2560af0 --- /dev/null +++ b/src/serde.rs @@ -0,0 +1,173 @@ +//! Serialization/Deserialization with serde. +//! +//! This module provides default implementations for [`DateTime`] using the [RFC 3339] format and +//! various alternatives. +//! +//! The alternatives are for use with serde's [`with` annotation] combined with the module name. +//! Alternatively the individual `serialize` and `deserialize` functions in each module can be used +//! with serde's [`serialize_with`] and [`deserialize_with`] annotations. +//! +//! *Available on crate feature 'serde' only.* +//! +//! [`DateTime`]: crate::DateTime +//! [RFC 3339]: https://tools.ietf.org/html/rfc3339 +//! [`with` annotation]: https://serde.rs/field-attrs.html#with +//! [`serialize_with`]: https://serde.rs/field-attrs.html#serialize_with +//! [`deserialize_with`]: https://serde.rs/field-attrs.html#deserialize_with +use core::fmt; +use serde::de; + +pub use crate::datetime::serde::*; + +/// Create a custom `de::Error` with `SerdeError::InvalidTimestamp`. +pub(crate) fn invalid_ts(value: T) -> E +where + E: de::Error, + T: fmt::Display, +{ + E::custom(SerdeError::InvalidTimestamp(value)) +} + +enum SerdeError { + InvalidTimestamp(T), +} + +impl fmt::Display for SerdeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + SerdeError::InvalidTimestamp(ts) => { + write!(f, "value is not a legal timestamp: {}", ts) + } + } + } +} + +/// Macro to take care of the boilerplate of a module for use with serde's `with` attribute. +/// This creates two modules: one for the regular (de)serialization and one for an optional type. +#[doc(hidden)] +#[macro_export] +macro_rules! serde_module { + ( + type: $type:ty, + module: $module:ident, + module_opt: $module_opt:ident, + doc: $doc:literal, + $serialize_to:ident: $serialize_fn:expr, + $deserialize_from:ident: $visitor:ident, + expecting: $expecting:literal, + $($visit:ident($visit_ty:ty): $visit_fn:expr,)+ + ) => { + #[doc=concat!("Serialize/deserialize a `", stringify!($type), "` to/from ", $expecting, ".")] + /// + /// Intended for use with the [`#[serde(with = "module")]` annotation]( + /// https://serde.rs/field-attrs.html#with). + #[doc=$doc] + #[allow(clippy::redundant_closure_call)] + pub mod $module { + use serde::{de, ser}; + use core::fmt; + use super::*; + + #[doc=concat!("Serialize a `", stringify!($type), "` to ", $expecting, ".")] + pub fn serialize(dt: &$type, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer + .$serialize_to($serialize_fn(dt).map_err(ser::Error::custom)?) + } + + #[doc=concat!("Deserialize ", $expecting, " to a `", stringify!($type), "`.")] + pub fn deserialize<'de, D>(d: D) -> Result<$type, D::Error> + where + D: de::Deserializer<'de>, + { + d.$deserialize_from($visitor) + } + + impl<'de> de::Visitor<'de> for $visitor { + type Value = $type; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str($expecting) + } + + $(fn $visit(self, value: $visit_ty) -> Result + where + E: de::Error, + { + $visit_fn(value) + })+ + } + } + + #[doc=concat!("Serialize/deserialize an `Option<", stringify!($type), ">` to/from ", $expecting, ".")] + /// + /// Intended for use with `serde`'s `with` attribute. + #[allow(clippy::redundant_closure_call)] + pub mod $module_opt { + use serde::{de, ser}; + use core::fmt; + use super::*; + + #[doc=concat!("Serialize an `Option<", stringify!($type), ">` to ", $expecting, ".")] + pub fn serialize(opt: &Option<$type>, serializer: S) -> Result + where + S: ser::Serializer, + { + match *opt { + Some(ref dt) => serializer.serialize_some(&$serialize_fn(dt).map_err(ser::Error::custom)?), + None => serializer.serialize_none(), + } + } + + #[doc=concat!("Deserialize ", $expecting, " to an `Option<", stringify!($type), ">`.")] + pub fn deserialize<'de, D>(d: D) -> Result, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_option(OptionVisitor) + } + + struct OptionVisitor; + + impl<'de> de::Visitor<'de> for OptionVisitor { + type Value = Option<$type>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(concat!($expecting, " or none")) + } + + fn visit_some(self, d: D) -> Result + where + D: de::Deserializer<'de>, + { + d.$deserialize_from($visitor).map(Some) + } + + fn visit_none(self) -> Result + where + E: de::Error, + { + Ok(None) + } + + fn visit_unit(self) -> Result + where + E: de::Error, + { + Ok(None) + } + } + } + }; +} + +/// Wrap a function as a closure that returns `Result<_, Infallible>` +#[macro_export] +#[doc(hidden)] +macro_rules! infallible { + ($fn:expr) => { + |val| Ok::<_, core::convert::Infallible>($fn(val)) + }; +}