From d96efa4246d19dfdfbf739a64597d9241caed068 Mon Sep 17 00:00:00 2001 From: tosti007 Date: Thu, 16 Jan 2025 16:14:44 +0100 Subject: [PATCH 1/9] Add nanoseconds parsing --- src/datetime/serde.rs | 260 ++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 15 ++- 2 files changed, 274 insertions(+), 1 deletion(-) diff --git a/src/datetime/serde.rs b/src/datetime/serde.rs index 1492fe3f0..5c3cee840 100644 --- a/src/datetime/serde.rs +++ b/src/datetime/serde.rs @@ -23,6 +23,10 @@ pub struct MicroSecondsTimestampVisitor; #[derive(Debug)] pub struct MilliSecondsTimestampVisitor; +#[doc(hidden)] +#[derive(Debug)] +pub struct NanoSecondsTimedeltaVisitor; + /// Serialize to an RFC 3339 formatted string /// /// As an extension to RFC 3339 this can serialize `DateTime`s outside the range of 0-9999 years @@ -1206,6 +1210,262 @@ pub mod ts_seconds_option { } } +/// Ser/de to/from timedeltas in nanoseconds +/// +/// Intended for use with `serde`'s `with` attribute. +/// +/// # Example: +/// +/// ```rust +/// # use chrono::TimeDelta; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::serde::td_nanoseconds; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "td_nanoseconds")] +/// delta: TimeDelta, +/// } +/// +/// let time = TimeDelta::nanoseconds(2018517); +/// let my_s = S { delta: delta.clone() }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"delta":2018517}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.delta, delta); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod td_nanoseconds { + use core::fmt; + use serde::{de, ser}; + + use crate::TimeDelta; + use crate::serde::invalid_td; + + use super::NanoSecondsTimedeltaVisitor; + + /// Serialize a [`TimeDelta`] into an integer number of nanoseconds. + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Errors + /// + /// This function returns an error on an out of range `TimeDelta`. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::TimeDelta; + /// # use serde_derive::Serialize; + /// use chrono::serde::td_nanoseconds::serialize as to_nano_td; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_nano_td")] + /// delta: TimeDelta, + /// } + /// + /// let my_s = S { + /// delta: TimeDelta::nanoseconds(2018517), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"delta":2018517}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize(td: &TimeDelta, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_i64(td.num_nanoseconds().ok_or(ser::Error::custom( + "value out of range for a timedelta with nanosecond precision", + ))?) + } + + /// Deserialize a [`TimeDelta`] from a nanosecond integer number. + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::TimeDelta; + /// # use serde_derive::Deserialize; + /// use chrono::serde::td_nanoseconds::deserialize as from_nano_td; + /// #[derive(Debug, PartialEq, Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_nano_td")] + /// delta: TimeDelta, + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "delta": 2018517 }"#)?; + /// assert_eq!(my_s, S { delta: TimeDelta::nanoseconds(2018517) }); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(NanoSecondsTimedeltaVisitor) + } + + impl de::Visitor<'_> for NanoSecondsTimedeltaVisitor { + type Value = TimeDelta; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a timedelta in nanoseconds") + } + + /// Deserialize a timedelta in nanoseconds + fn visit_i64(self, value: i64) -> Result + where + E: de::Error, + { + Ok(TimeDelta::nanoseconds(value)) + } + + /// Deserialize a timedelta in nanoseconds + fn visit_u64(self, value: u64) -> Result + where + E: de::Error, + { + Ok(TimeDelta::nanoseconds(value.try_into().map_err(|_| invalid_td(value))?)) + } + } +} + +/// Ser/de to/from optional timedeltas in nanoseconds +/// +/// Intended for use with `serde`'s `with` attribute. +/// +/// # Example: +/// +/// ```rust +/// # use chrono::Delta; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::serde::td_nanoseconds_option; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "td_nanoseconds_option")] +/// delta: Option, +/// } +/// +/// let delta = Some(TimeDelta::nanoseconds(201817)); +/// let my_s = S { delta: delta.clone() }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"delta":201817}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.delta, delta); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod td_nanoseconds_option { + use core::fmt; + use serde::{de, ser}; + + use crate::TimeDelta; + + use super::NanoSecondsTimedeltaVisitor; + + /// Serialize a UTC datetime into an integer number of nanoseconds or none + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Errors + /// + /// This function returns an error on an out of range `DateTime`. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::TimeDelta; + /// # use serde_derive::Serialize; + /// use chrono::serde::td_nanoseconds_option::serialize as to_nano_tdopt; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_nano_tdopt")] + /// delta: Option, + /// } + /// + /// let my_s = S { + /// delta: Some(TimeDelta::nanoseconds(2018517)), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":2018517}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize(opt: &Option, serializer: S) -> Result + where + S: ser::Serializer, + { + match *opt { + Some(ref td) => serializer.serialize_some(&td.num_nanoseconds().ok_or( + ser::Error::custom("value out of range for a timedelta 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::TimeDelta; + /// # use serde_derive::Deserialize; + /// use chrono::serde::td_nanoseconds_option::deserialize as from_nano_tdopt; + /// #[derive(Debug, PartialEq, Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_nano_tdopt")] + /// delta: Option, + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "delta": 2018517 }"#)?; + /// assert_eq!(my_s, S { delta: TimeDelta::nanoseconds(2018517) }); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_option(OptionNanoSecondsTimedeltaVisitor) + } + + struct OptionNanoSecondsTimedeltaVisitor; + + impl<'de> de::Visitor<'de> for OptionNanoSecondsTimedeltaVisitor { + type Value = Option; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a timedelta in nanoseconds or none") + } + + /// Deserialize a timedelta in nanoseconds + fn visit_some(self, d: D) -> Result + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(NanoSecondsTimedeltaVisitor).map(Some) + } + + /// Deserialize a timedelta in nanoseconds + fn visit_none(self) -> Result + where + E: de::Error, + { + Ok(None) + } + + /// Deserialize a timedelta in nanoseconds + fn visit_unit(self) -> Result + where + E: de::Error, + { + Ok(None) + } + } +} + #[cfg(test)] mod tests { #[cfg(feature = "clock")] diff --git a/src/lib.rs b/src/lib.rs index 80a70d464..07539eae8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -626,16 +626,29 @@ pub mod serde { E::custom(SerdeError::InvalidTimestamp(value)) } + /// Create a custom `de::Error` with `SerdeError::InvalidTimestamp`. + pub(crate) fn invalid_td(value: T) -> E + where + E: de::Error, + T: fmt::Display, + { + E::custom(SerdeError::InvalidTimedelta(value)) + } + enum SerdeError { InvalidTimestamp(T), + InvalidTimedelta(T), } impl fmt::Display for SerdeError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - SerdeError::InvalidTimestamp(ts) => { + Self::InvalidTimestamp(ts) => { write!(f, "value is not a legal timestamp: {}", ts) } + Self::InvalidTimedelta(td) => { + write!(f, "value is not a legal timedelta: {}", td) + } } } } From 3a0ecd19a099756aa773265af2c9edab0ce4e97b Mon Sep 17 00:00:00 2001 From: tosti007 Date: Thu, 16 Jan 2025 16:28:45 +0100 Subject: [PATCH 2/9] Copy-paste microseconds parsing --- src/datetime/serde.rs | 260 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) diff --git a/src/datetime/serde.rs b/src/datetime/serde.rs index 5c3cee840..d4ee326c5 100644 --- a/src/datetime/serde.rs +++ b/src/datetime/serde.rs @@ -27,6 +27,10 @@ pub struct MilliSecondsTimestampVisitor; #[derive(Debug)] pub struct NanoSecondsTimedeltaVisitor; +#[doc(hidden)] +#[derive(Debug)] +pub struct MicroSecondsTimedeltaVisitor; + /// Serialize to an RFC 3339 formatted string /// /// As an extension to RFC 3339 this can serialize `DateTime`s outside the range of 0-9999 years @@ -1466,6 +1470,262 @@ pub mod td_nanoseconds_option { } } +/// Ser/de to/from timedeltas in microseconds +/// +/// Intended for use with `serde`'s `with` attribute. +/// +/// # Example: +/// +/// ```rust +/// # use chrono::TimeDelta; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::serde::td_microseconds; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "td_microseconds")] +/// delta: TimeDelta, +/// } +/// +/// let time = TimeDelta::microseconds(2018517); +/// let my_s = S { delta: delta.clone() }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"delta":2018517}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.delta, delta); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod td_microseconds { + use core::fmt; + use serde::{de, ser}; + + use crate::TimeDelta; + use crate::serde::invalid_td; + + use super::MicroSecondsTimedeltaVisitor; + + /// Serialize a [`TimeDelta`] into an integer number of microseconds. + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Errors + /// + /// This function returns an error on an out of range `TimeDelta`. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::TimeDelta; + /// # use serde_derive::Serialize; + /// use chrono::serde::td_microseconds::serialize as to_micro_td; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_micro_td")] + /// delta: TimeDelta, + /// } + /// + /// let my_s = S { + /// delta: TimeDelta::microseconds(2018517), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"delta":2018517}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize(td: &TimeDelta, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_i64(td.num_microseconds().ok_or(ser::Error::custom( + "value out of range for a timedelta with microsecond precision", + ))?) + } + + /// Deserialize a [`TimeDelta`] from a microsecond integer number. + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::TimeDelta; + /// # use serde_derive::Deserialize; + /// use chrono::serde::td_microseconds::deserialize as from_micro_td; + /// #[derive(Debug, PartialEq, Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_micro_td")] + /// delta: TimeDelta, + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "delta": 2018517 }"#)?; + /// assert_eq!(my_s, S { delta: TimeDelta::microseconds(2018517) }); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(MicroSecondsTimedeltaVisitor) + } + + impl de::Visitor<'_> for MicroSecondsTimedeltaVisitor { + type Value = TimeDelta; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a timedelta in microseconds") + } + + /// Deserialize a timedelta in microseconds + fn visit_i64(self, value: i64) -> Result + where + E: de::Error, + { + Ok(TimeDelta::microseconds(value)) + } + + /// Deserialize a timedelta in microseconds + fn visit_u64(self, value: u64) -> Result + where + E: de::Error, + { + Ok(TimeDelta::microseconds(value.try_into().map_err(|_| invalid_td(value))?)) + } + } +} + +/// Ser/de to/from optional timedeltas in microseconds +/// +/// Intended for use with `serde`'s `with` attribute. +/// +/// # Example: +/// +/// ```rust +/// # use chrono::Delta; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::serde::td_microseconds_option; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "td_microseconds_option")] +/// delta: Option, +/// } +/// +/// let delta = Some(TimeDelta::microseconds(201817)); +/// let my_s = S { delta: delta.clone() }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"delta":201817}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.delta, delta); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod td_microseconds_option { + use core::fmt; + use serde::{de, ser}; + + use crate::TimeDelta; + + use super::MicroSecondsTimedeltaVisitor; + + /// Serialize a UTC datetime into an integer number of microseconds or none + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Errors + /// + /// This function returns an error on an out of range `DateTime`. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::TimeDelta; + /// # use serde_derive::Serialize; + /// use chrono::serde::td_microseconds_option::serialize as to_micro_tdopt; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_micro_tdopt")] + /// delta: Option, + /// } + /// + /// let my_s = S { + /// delta: Some(TimeDelta::microseconds(2018517)), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":2018517}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize(opt: &Option, serializer: S) -> Result + where + S: ser::Serializer, + { + match *opt { + Some(ref td) => serializer.serialize_some(&td.num_microseconds().ok_or( + ser::Error::custom("value out of range for a timedelta with microsecond precision"), + )?), + 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::TimeDelta; + /// # use serde_derive::Deserialize; + /// use chrono::serde::td_microseconds_option::deserialize as from_micro_tdopt; + /// #[derive(Debug, PartialEq, Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_micro_tdopt")] + /// delta: Option, + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "delta": 2018517 }"#)?; + /// assert_eq!(my_s, S { delta: TimeDelta::microseconds(2018517) }); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_option(OptionmicroSecondsTimedeltaVisitor) + } + + struct OptionmicroSecondsTimedeltaVisitor; + + impl<'de> de::Visitor<'de> for OptionmicroSecondsTimedeltaVisitor { + type Value = Option; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a timedelta in microseconds or none") + } + + /// Deserialize a timedelta in microseconds + fn visit_some(self, d: D) -> Result + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(MicroSecondsTimedeltaVisitor).map(Some) + } + + /// Deserialize a timedelta in microseconds + fn visit_none(self) -> Result + where + E: de::Error, + { + Ok(None) + } + + /// Deserialize a timedelta in microseconds + fn visit_unit(self) -> Result + where + E: de::Error, + { + Ok(None) + } + } +} + #[cfg(test)] mod tests { #[cfg(feature = "clock")] From f87b359da4e2617652b0c70c788c4fa245003051 Mon Sep 17 00:00:00 2001 From: tosti007 Date: Thu, 16 Jan 2025 16:30:31 +0100 Subject: [PATCH 3/9] Copy-paste milliseconds parsing --- src/datetime/serde.rs | 256 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) diff --git a/src/datetime/serde.rs b/src/datetime/serde.rs index d4ee326c5..ee3302584 100644 --- a/src/datetime/serde.rs +++ b/src/datetime/serde.rs @@ -31,6 +31,10 @@ pub struct NanoSecondsTimedeltaVisitor; #[derive(Debug)] pub struct MicroSecondsTimedeltaVisitor; +#[doc(hidden)] +#[derive(Debug)] +pub struct MilliSecondsTimedeltaVisitor; + /// Serialize to an RFC 3339 formatted string /// /// As an extension to RFC 3339 this can serialize `DateTime`s outside the range of 0-9999 years @@ -1726,6 +1730,258 @@ pub mod td_microseconds_option { } } +/// Ser/de to/from timedeltas in milliseconds +/// +/// Intended for use with `serde`'s `with` attribute. +/// +/// # Example: +/// +/// ```rust +/// # use chrono::TimeDelta; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::serde::td_milliseconds; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "td_milliseconds")] +/// delta: TimeDelta, +/// } +/// +/// let time = TimeDelta::milliseconds(2018517); +/// let my_s = S { delta: delta.clone() }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"delta":2018517}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.delta, delta); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod td_milliseconds { + use core::fmt; + use serde::{de, ser}; + + use crate::TimeDelta; + use crate::serde::invalid_td; + + use super::MilliSecondsTimedeltaVisitor; + + /// Serialize a [`TimeDelta`] into an integer number of milliseconds. + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Errors + /// + /// This function returns an error on an out of range `TimeDelta`. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::TimeDelta; + /// # use serde_derive::Serialize; + /// use chrono::serde::td_milliseconds::serialize as to_milli_td; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_milli_td")] + /// delta: TimeDelta, + /// } + /// + /// let my_s = S { + /// delta: TimeDelta::milliseconds(2018517), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"delta":2018517}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize(td: &TimeDelta, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_i64(td.num_milliseconds()) + } + + /// Deserialize a [`TimeDelta`] from a millisecond integer number. + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::TimeDelta; + /// # use serde_derive::Deserialize; + /// use chrono::serde::td_milliseconds::deserialize as from_milli_td; + /// #[derive(Debug, PartialEq, Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_milli_td")] + /// delta: TimeDelta, + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "delta": 2018517 }"#)?; + /// assert_eq!(my_s, S { delta: TimeDelta::milliseconds(2018517) }); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(MilliSecondsTimedeltaVisitor) + } + + impl de::Visitor<'_> for MilliSecondsTimedeltaVisitor { + type Value = TimeDelta; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a timedelta in milliseconds") + } + + /// Deserialize a timedelta in milliseconds + fn visit_i64(self, value: i64) -> Result + where + E: de::Error, + { + Ok(TimeDelta::milliseconds(value)) + } + + /// Deserialize a timedelta in milliseconds + fn visit_u64(self, value: u64) -> Result + where + E: de::Error, + { + Ok(TimeDelta::milliseconds(value.try_into().map_err(|_| invalid_td(value))?)) + } + } +} + +/// Ser/de to/from optional timedeltas in milliseconds +/// +/// Intended for use with `serde`'s `with` attribute. +/// +/// # Example: +/// +/// ```rust +/// # use chrono::Delta; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::serde::td_milliseconds_option; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "td_milliseconds_option")] +/// delta: Option, +/// } +/// +/// let delta = Some(TimeDelta::milliseconds(201817)); +/// let my_s = S { delta: delta.clone() }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"delta":201817}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.delta, delta); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod td_milliseconds_option { + use core::fmt; + use serde::{de, ser}; + + use crate::TimeDelta; + + use super::MilliSecondsTimedeltaVisitor; + + /// Serialize a UTC datetime into an integer number of milliseconds or none + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Errors + /// + /// This function returns an error on an out of range `DateTime`. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::TimeDelta; + /// # use serde_derive::Serialize; + /// use chrono::serde::td_milliseconds_option::serialize as to_milli_tdopt; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_milli_tdopt")] + /// delta: Option, + /// } + /// + /// let my_s = S { + /// delta: Some(TimeDelta::milliseconds(2018517)), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":2018517}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize(opt: &Option, serializer: S) -> Result + where + S: ser::Serializer, + { + match *opt { + Some(ref td) => serializer.serialize_some(&td.num_milliseconds()), + 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::TimeDelta; + /// # use serde_derive::Deserialize; + /// use chrono::serde::td_milliseconds_option::deserialize as from_milli_tdopt; + /// #[derive(Debug, PartialEq, Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_milli_tdopt")] + /// delta: Option, + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "delta": 2018517 }"#)?; + /// assert_eq!(my_s, S { delta: TimeDelta::milliseconds(2018517) }); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_option(OptionmilliSecondsTimedeltaVisitor) + } + + struct OptionmilliSecondsTimedeltaVisitor; + + impl<'de> de::Visitor<'de> for OptionmilliSecondsTimedeltaVisitor { + type Value = Option; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a timedelta in milliseconds or none") + } + + /// Deserialize a timedelta in milliseconds + fn visit_some(self, d: D) -> Result + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(MilliSecondsTimedeltaVisitor).map(Some) + } + + /// Deserialize a timedelta in milliseconds + fn visit_none(self) -> Result + where + E: de::Error, + { + Ok(None) + } + + /// Deserialize a timedelta in milliseconds + fn visit_unit(self) -> Result + where + E: de::Error, + { + Ok(None) + } + } +} + #[cfg(test)] mod tests { #[cfg(feature = "clock")] From a9531cf85dbd28a9d7648b790a96aec0d8b7dc84 Mon Sep 17 00:00:00 2001 From: tosti007 Date: Thu, 16 Jan 2025 16:31:22 +0100 Subject: [PATCH 4/9] Copy-paste seconds parsing --- src/datetime/serde.rs | 255 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) diff --git a/src/datetime/serde.rs b/src/datetime/serde.rs index ee3302584..1a1156a00 100644 --- a/src/datetime/serde.rs +++ b/src/datetime/serde.rs @@ -23,6 +23,10 @@ pub struct MicroSecondsTimestampVisitor; #[derive(Debug)] pub struct MilliSecondsTimestampVisitor; +#[doc(hidden)] +#[derive(Debug)] +pub struct SecondsTimedeltaVisitor; + #[doc(hidden)] #[derive(Debug)] pub struct NanoSecondsTimedeltaVisitor; @@ -1982,6 +1986,257 @@ pub mod td_milliseconds_option { } } +/// Ser/de to/from timedeltas in seconds +/// +/// Intended for use with `serde`'s `with` attribute. +/// +/// # Example: +/// +/// ```rust +/// # use chrono::TimeDelta; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::serde::td_seconds; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "td_seconds")] +/// delta: TimeDelta, +/// } +/// +/// let time = TimeDelta::seconds(2018517); +/// let my_s = S { delta: delta.clone() }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"delta":2018517}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.delta, delta); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod td_seconds { + use core::fmt; + use serde::{de, ser}; + + use crate::TimeDelta; + use crate::serde::invalid_td; + + use super::SecondsTimedeltaVisitor; + + /// Serialize a [`TimeDelta`] into an integer number of seconds. + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Errors + /// + /// This function returns an error on an out of range `TimeDelta`. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::TimeDelta; + /// # use serde_derive::Serialize; + /// use chrono::serde::td_seconds::serialize as to__td; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to__td")] + /// delta: TimeDelta, + /// } + /// + /// let my_s = S { + /// delta: TimeDelta::seconds(2018517), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"delta":2018517}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize(td: &TimeDelta, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_i64(td.num_seconds()) + } + + /// Deserialize a [`TimeDelta`] from a second integer number. + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::TimeDelta; + /// # use serde_derive::Deserialize; + /// use chrono::serde::td_seconds::deserialize as from__td; + /// #[derive(Debug, PartialEq, Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from__td")] + /// delta: TimeDelta, + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "delta": 2018517 }"#)?; + /// assert_eq!(my_s, S { delta: TimeDelta::seconds(2018517) }); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(SecondsTimedeltaVisitor) + } + + impl de::Visitor<'_> for SecondsTimedeltaVisitor { + type Value = TimeDelta; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a timedelta in seconds") + } + + /// Deserialize a timedelta in seconds + fn visit_i64(self, value: i64) -> Result + where + E: de::Error, + { + Ok(TimeDelta::seconds(value)) + } + + /// Deserialize a timedelta in seconds + fn visit_u64(self, value: u64) -> Result + where + E: de::Error, + { + Ok(TimeDelta::seconds(value.try_into().map_err(|_| invalid_td(value))?)) + } + } +} + +/// Ser/de to/from optional timedeltas in seconds +/// +/// Intended for use with `serde`'s `with` attribute. +/// +/// # Example: +/// +/// ```rust +/// # use chrono::Delta; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::serde::td_seconds_option; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "td_seconds_option")] +/// delta: Option, +/// } +/// +/// let delta = Some(TimeDelta::seconds(201817)); +/// let my_s = S { delta: delta.clone() }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"delta":201817}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.delta, delta); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod td_seconds_option { + use core::fmt; + use serde::{de, ser}; + + use crate::TimeDelta; + + use super::SecondsTimedeltaVisitor; + + /// Serialize a UTC datetime into an integer number of seconds or none + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Errors + /// + /// This function returns an error on an out of range `DateTime`. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::TimeDelta; + /// # use serde_derive::Serialize; + /// use chrono::serde::td_seconds_option::serialize as to__tdopt; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to__tdopt")] + /// delta: Option, + /// } + /// + /// let my_s = S { + /// delta: Some(TimeDelta::seconds(2018517)), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":2018517}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize(opt: &Option, serializer: S) -> Result + where + S: ser::Serializer, + { + match *opt { + Some(ref td) => serializer.serialize_some(&td.num_seconds()), + None => serializer.serialize_none(), + } + } + + /// Deserialize a `DateTime` from a second timestamp or none + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::TimeDelta; + /// # use serde_derive::Deserialize; + /// use chrono::serde::td_seconds_option::deserialize as from__tdopt; + /// #[derive(Debug, PartialEq, Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from__tdopt")] + /// delta: Option, + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "delta": 2018517 }"#)?; + /// assert_eq!(my_s, S { delta: TimeDelta::seconds(2018517) }); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_option(OptionSecondsTimedeltaVisitor) + } + + struct OptionSecondsTimedeltaVisitor; + + impl<'de> de::Visitor<'de> for OptionSecondsTimedeltaVisitor { + type Value = Option; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a timedelta in seconds or none") + } + + /// Deserialize a timedelta in seconds + fn visit_some(self, d: D) -> Result + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(SecondsTimedeltaVisitor).map(Some) + } + + /// Deserialize a timedelta in seconds + fn visit_none(self) -> Result + where + E: de::Error, + { + Ok(None) + } + + /// Deserialize a timedelta in seconds + fn visit_unit(self) -> Result + where + E: de::Error, + { + Ok(None) + } + } +} #[cfg(test)] mod tests { #[cfg(feature = "clock")] From fad634bf4c7f001f74eab3d6553acfd847d10aa2 Mon Sep 17 00:00:00 2001 From: tosti007 Date: Mon, 20 Jan 2025 12:35:26 +0100 Subject: [PATCH 5/9] Uppercase Timedelta's D --- src/datetime/serde.rs | 72 +++++++++++++++++++++---------------------- src/lib.rs | 6 ++-- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/datetime/serde.rs b/src/datetime/serde.rs index 1a1156a00..b747730d8 100644 --- a/src/datetime/serde.rs +++ b/src/datetime/serde.rs @@ -25,19 +25,19 @@ pub struct MilliSecondsTimestampVisitor; #[doc(hidden)] #[derive(Debug)] -pub struct SecondsTimedeltaVisitor; +pub struct SecondsTimeDeltaVisitor; #[doc(hidden)] #[derive(Debug)] -pub struct NanoSecondsTimedeltaVisitor; +pub struct NanoSecondsTimeDeltaVisitor; #[doc(hidden)] #[derive(Debug)] -pub struct MicroSecondsTimedeltaVisitor; +pub struct MicroSecondsTimeDeltaVisitor; #[doc(hidden)] #[derive(Debug)] -pub struct MilliSecondsTimedeltaVisitor; +pub struct MilliSecondsTimeDeltaVisitor; /// Serialize to an RFC 3339 formatted string /// @@ -1254,7 +1254,7 @@ pub mod td_nanoseconds { use crate::TimeDelta; use crate::serde::invalid_td; - use super::NanoSecondsTimedeltaVisitor; + use super::NanoSecondsTimeDeltaVisitor; /// Serialize a [`TimeDelta`] into an integer number of nanoseconds. /// @@ -1316,10 +1316,10 @@ pub mod td_nanoseconds { where D: de::Deserializer<'de>, { - d.deserialize_i64(NanoSecondsTimedeltaVisitor) + d.deserialize_i64(NanoSecondsTimeDeltaVisitor) } - impl de::Visitor<'_> for NanoSecondsTimedeltaVisitor { + impl de::Visitor<'_> for NanoSecondsTimeDeltaVisitor { type Value = TimeDelta; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -1375,7 +1375,7 @@ pub mod td_nanoseconds_option { use crate::TimeDelta; - use super::NanoSecondsTimedeltaVisitor; + use super::NanoSecondsTimeDeltaVisitor; /// Serialize a UTC datetime into an integer number of nanoseconds or none /// @@ -1440,12 +1440,12 @@ pub mod td_nanoseconds_option { where D: de::Deserializer<'de>, { - d.deserialize_option(OptionNanoSecondsTimedeltaVisitor) + d.deserialize_option(OptionNanoSecondsTimeDeltaVisitor) } - struct OptionNanoSecondsTimedeltaVisitor; + struct OptionNanoSecondsTimeDeltaVisitor; - impl<'de> de::Visitor<'de> for OptionNanoSecondsTimedeltaVisitor { + impl<'de> de::Visitor<'de> for OptionNanoSecondsTimeDeltaVisitor { type Value = Option; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -1457,7 +1457,7 @@ pub mod td_nanoseconds_option { where D: de::Deserializer<'de>, { - d.deserialize_i64(NanoSecondsTimedeltaVisitor).map(Some) + d.deserialize_i64(NanoSecondsTimeDeltaVisitor).map(Some) } /// Deserialize a timedelta in nanoseconds @@ -1510,7 +1510,7 @@ pub mod td_microseconds { use crate::TimeDelta; use crate::serde::invalid_td; - use super::MicroSecondsTimedeltaVisitor; + use super::MicroSecondsTimeDeltaVisitor; /// Serialize a [`TimeDelta`] into an integer number of microseconds. /// @@ -1572,10 +1572,10 @@ pub mod td_microseconds { where D: de::Deserializer<'de>, { - d.deserialize_i64(MicroSecondsTimedeltaVisitor) + d.deserialize_i64(MicroSecondsTimeDeltaVisitor) } - impl de::Visitor<'_> for MicroSecondsTimedeltaVisitor { + impl de::Visitor<'_> for MicroSecondsTimeDeltaVisitor { type Value = TimeDelta; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -1631,7 +1631,7 @@ pub mod td_microseconds_option { use crate::TimeDelta; - use super::MicroSecondsTimedeltaVisitor; + use super::MicroSecondsTimeDeltaVisitor; /// Serialize a UTC datetime into an integer number of microseconds or none /// @@ -1696,12 +1696,12 @@ pub mod td_microseconds_option { where D: de::Deserializer<'de>, { - d.deserialize_option(OptionmicroSecondsTimedeltaVisitor) + d.deserialize_option(OptionmicroSecondsTimeDeltaVisitor) } - struct OptionmicroSecondsTimedeltaVisitor; + struct OptionmicroSecondsTimeDeltaVisitor; - impl<'de> de::Visitor<'de> for OptionmicroSecondsTimedeltaVisitor { + impl<'de> de::Visitor<'de> for OptionmicroSecondsTimeDeltaVisitor { type Value = Option; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -1713,7 +1713,7 @@ pub mod td_microseconds_option { where D: de::Deserializer<'de>, { - d.deserialize_i64(MicroSecondsTimedeltaVisitor).map(Some) + d.deserialize_i64(MicroSecondsTimeDeltaVisitor).map(Some) } /// Deserialize a timedelta in microseconds @@ -1766,7 +1766,7 @@ pub mod td_milliseconds { use crate::TimeDelta; use crate::serde::invalid_td; - use super::MilliSecondsTimedeltaVisitor; + use super::MilliSecondsTimeDeltaVisitor; /// Serialize a [`TimeDelta`] into an integer number of milliseconds. /// @@ -1826,10 +1826,10 @@ pub mod td_milliseconds { where D: de::Deserializer<'de>, { - d.deserialize_i64(MilliSecondsTimedeltaVisitor) + d.deserialize_i64(MilliSecondsTimeDeltaVisitor) } - impl de::Visitor<'_> for MilliSecondsTimedeltaVisitor { + impl de::Visitor<'_> for MilliSecondsTimeDeltaVisitor { type Value = TimeDelta; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -1885,7 +1885,7 @@ pub mod td_milliseconds_option { use crate::TimeDelta; - use super::MilliSecondsTimedeltaVisitor; + use super::MilliSecondsTimeDeltaVisitor; /// Serialize a UTC datetime into an integer number of milliseconds or none /// @@ -1948,12 +1948,12 @@ pub mod td_milliseconds_option { where D: de::Deserializer<'de>, { - d.deserialize_option(OptionmilliSecondsTimedeltaVisitor) + d.deserialize_option(OptionmilliSecondsTimeDeltaVisitor) } - struct OptionmilliSecondsTimedeltaVisitor; + struct OptionmilliSecondsTimeDeltaVisitor; - impl<'de> de::Visitor<'de> for OptionmilliSecondsTimedeltaVisitor { + impl<'de> de::Visitor<'de> for OptionmilliSecondsTimeDeltaVisitor { type Value = Option; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -1965,7 +1965,7 @@ pub mod td_milliseconds_option { where D: de::Deserializer<'de>, { - d.deserialize_i64(MilliSecondsTimedeltaVisitor).map(Some) + d.deserialize_i64(MilliSecondsTimeDeltaVisitor).map(Some) } /// Deserialize a timedelta in milliseconds @@ -2018,7 +2018,7 @@ pub mod td_seconds { use crate::TimeDelta; use crate::serde::invalid_td; - use super::SecondsTimedeltaVisitor; + use super::SecondsTimeDeltaVisitor; /// Serialize a [`TimeDelta`] into an integer number of seconds. /// @@ -2078,10 +2078,10 @@ pub mod td_seconds { where D: de::Deserializer<'de>, { - d.deserialize_i64(SecondsTimedeltaVisitor) + d.deserialize_i64(SecondsTimeDeltaVisitor) } - impl de::Visitor<'_> for SecondsTimedeltaVisitor { + impl de::Visitor<'_> for SecondsTimeDeltaVisitor { type Value = TimeDelta; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -2137,7 +2137,7 @@ pub mod td_seconds_option { use crate::TimeDelta; - use super::SecondsTimedeltaVisitor; + use super::SecondsTimeDeltaVisitor; /// Serialize a UTC datetime into an integer number of seconds or none /// @@ -2200,12 +2200,12 @@ pub mod td_seconds_option { where D: de::Deserializer<'de>, { - d.deserialize_option(OptionSecondsTimedeltaVisitor) + d.deserialize_option(OptionSecondsTimeDeltaVisitor) } - struct OptionSecondsTimedeltaVisitor; + struct OptionSecondsTimeDeltaVisitor; - impl<'de> de::Visitor<'de> for OptionSecondsTimedeltaVisitor { + impl<'de> de::Visitor<'de> for OptionSecondsTimeDeltaVisitor { type Value = Option; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -2217,7 +2217,7 @@ pub mod td_seconds_option { where D: de::Deserializer<'de>, { - d.deserialize_i64(SecondsTimedeltaVisitor).map(Some) + d.deserialize_i64(SecondsTimeDeltaVisitor).map(Some) } /// Deserialize a timedelta in seconds diff --git a/src/lib.rs b/src/lib.rs index 07539eae8..03755da22 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -632,12 +632,12 @@ pub mod serde { E: de::Error, T: fmt::Display, { - E::custom(SerdeError::InvalidTimedelta(value)) + E::custom(SerdeError::InvalidTimeDelta(value)) } enum SerdeError { InvalidTimestamp(T), - InvalidTimedelta(T), + InvalidTimeDelta(T), } impl fmt::Display for SerdeError { @@ -646,7 +646,7 @@ pub mod serde { Self::InvalidTimestamp(ts) => { write!(f, "value is not a legal timestamp: {}", ts) } - Self::InvalidTimedelta(td) => { + Self::InvalidTimeDelta(td) => { write!(f, "value is not a legal timedelta: {}", td) } } From 9abf81ee918557b948976e8043ee7e700acc629f Mon Sep 17 00:00:00 2001 From: tosti007 Date: Mon, 20 Jan 2025 12:35:44 +0100 Subject: [PATCH 6/9] Rename invalid_td to invalid_time_delta --- src/datetime/serde.rs | 16 ++++++++-------- src/lib.rs | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/datetime/serde.rs b/src/datetime/serde.rs index b747730d8..908cae4f4 100644 --- a/src/datetime/serde.rs +++ b/src/datetime/serde.rs @@ -1252,7 +1252,7 @@ pub mod td_nanoseconds { use serde::{de, ser}; use crate::TimeDelta; - use crate::serde::invalid_td; + use crate::serde::invalid_time_delta; use super::NanoSecondsTimeDeltaVisitor; @@ -1339,7 +1339,7 @@ pub mod td_nanoseconds { where E: de::Error, { - Ok(TimeDelta::nanoseconds(value.try_into().map_err(|_| invalid_td(value))?)) + Ok(TimeDelta::nanoseconds(value.try_into().map_err(|_| invalid_time_delta(value))?)) } } } @@ -1508,7 +1508,7 @@ pub mod td_microseconds { use serde::{de, ser}; use crate::TimeDelta; - use crate::serde::invalid_td; + use crate::serde::invalid_time_delta; use super::MicroSecondsTimeDeltaVisitor; @@ -1595,7 +1595,7 @@ pub mod td_microseconds { where E: de::Error, { - Ok(TimeDelta::microseconds(value.try_into().map_err(|_| invalid_td(value))?)) + Ok(TimeDelta::microseconds(value.try_into().map_err(|_| invalid_time_delta(value))?)) } } } @@ -1764,7 +1764,7 @@ pub mod td_milliseconds { use serde::{de, ser}; use crate::TimeDelta; - use crate::serde::invalid_td; + use crate::serde::invalid_time_delta; use super::MilliSecondsTimeDeltaVisitor; @@ -1849,7 +1849,7 @@ pub mod td_milliseconds { where E: de::Error, { - Ok(TimeDelta::milliseconds(value.try_into().map_err(|_| invalid_td(value))?)) + Ok(TimeDelta::milliseconds(value.try_into().map_err(|_| invalid_time_delta(value))?)) } } } @@ -2016,7 +2016,7 @@ pub mod td_seconds { use serde::{de, ser}; use crate::TimeDelta; - use crate::serde::invalid_td; + use crate::serde::invalid_time_delta; use super::SecondsTimeDeltaVisitor; @@ -2101,7 +2101,7 @@ pub mod td_seconds { where E: de::Error, { - Ok(TimeDelta::seconds(value.try_into().map_err(|_| invalid_td(value))?)) + Ok(TimeDelta::seconds(value.try_into().map_err(|_| invalid_time_delta(value))?)) } } } diff --git a/src/lib.rs b/src/lib.rs index 03755da22..cb846695e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -627,7 +627,7 @@ pub mod serde { } /// Create a custom `de::Error` with `SerdeError::InvalidTimestamp`. - pub(crate) fn invalid_td(value: T) -> E + pub(crate) fn invalid_time_delta(value: T) -> E where E: de::Error, T: fmt::Display, From 86e4e3862c34370eaa912162b19fe22e145735f7 Mon Sep 17 00:00:00 2001 From: tosti007 Date: Thu, 22 May 2025 09:44:42 +0200 Subject: [PATCH 7/9] Update error capitalization and doc comment type links --- src/datetime/serde.rs | 88 +++++++++++++++++++++---------------------- src/lib.rs | 2 +- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/datetime/serde.rs b/src/datetime/serde.rs index 908cae4f4..f4c4d8d1d 100644 --- a/src/datetime/serde.rs +++ b/src/datetime/serde.rs @@ -1222,7 +1222,7 @@ pub mod ts_seconds_option { } } -/// Ser/de to/from timedeltas in nanoseconds +/// Ser/de to/from [`crate::TimeDelta`]s in nanoseconds /// /// Intended for use with `serde`'s `with` attribute. /// @@ -1262,7 +1262,7 @@ pub mod td_nanoseconds { /// /// # Errors /// - /// This function returns an error on an out of range `TimeDelta`. + /// This function returns an error on an out of range [`TimeDelta`]. /// /// # Example: /// @@ -1288,7 +1288,7 @@ pub mod td_nanoseconds { S: ser::Serializer, { serializer.serialize_i64(td.num_nanoseconds().ok_or(ser::Error::custom( - "value out of range for a timedelta with nanosecond precision", + "value out of range for a TimeDelta with nanosecond precision", ))?) } @@ -1323,10 +1323,10 @@ pub mod td_nanoseconds { type Value = TimeDelta; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a timedelta in nanoseconds") + formatter.write_str("a TimeDelta in nanoseconds") } - /// Deserialize a timedelta in nanoseconds + /// Deserialize a [`TimeDelta`] in nanoseconds fn visit_i64(self, value: i64) -> Result where E: de::Error, @@ -1334,7 +1334,7 @@ pub mod td_nanoseconds { Ok(TimeDelta::nanoseconds(value)) } - /// Deserialize a timedelta in nanoseconds + /// Deserialize a [`TimeDelta`] in nanoseconds fn visit_u64(self, value: u64) -> Result where E: de::Error, @@ -1344,7 +1344,7 @@ pub mod td_nanoseconds { } } -/// Ser/de to/from optional timedeltas in nanoseconds +/// Ser/de to/from optional [`crate::TimeDelta`]s in nanoseconds /// /// Intended for use with `serde`'s `with` attribute. /// @@ -1410,7 +1410,7 @@ pub mod td_nanoseconds_option { { match *opt { Some(ref td) => serializer.serialize_some(&td.num_nanoseconds().ok_or( - ser::Error::custom("value out of range for a timedelta with nanosecond precision"), + ser::Error::custom("value out of range for a TimeDelta with nanosecond precision"), )?), None => serializer.serialize_none(), } @@ -1449,10 +1449,10 @@ pub mod td_nanoseconds_option { type Value = Option; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a timedelta in nanoseconds or none") + formatter.write_str("a TimeDelta in nanoseconds or none") } - /// Deserialize a timedelta in nanoseconds + /// Deserialize a [`TimeDelta`] in nanoseconds fn visit_some(self, d: D) -> Result where D: de::Deserializer<'de>, @@ -1460,7 +1460,7 @@ pub mod td_nanoseconds_option { d.deserialize_i64(NanoSecondsTimeDeltaVisitor).map(Some) } - /// Deserialize a timedelta in nanoseconds + /// Deserialize a [`TimeDelta`] in nanoseconds fn visit_none(self) -> Result where E: de::Error, @@ -1468,7 +1468,7 @@ pub mod td_nanoseconds_option { Ok(None) } - /// Deserialize a timedelta in nanoseconds + /// Deserialize a [`TimeDelta`] in nanoseconds fn visit_unit(self) -> Result where E: de::Error, @@ -1478,7 +1478,7 @@ pub mod td_nanoseconds_option { } } -/// Ser/de to/from timedeltas in microseconds +/// Ser/de to/from [`crate::TimeDelta`]s in microseconds /// /// Intended for use with `serde`'s `with` attribute. /// @@ -1518,7 +1518,7 @@ pub mod td_microseconds { /// /// # Errors /// - /// This function returns an error on an out of range `TimeDelta`. + /// This function returns an error on an out of range [`TimeDelta`]. /// /// # Example: /// @@ -1544,7 +1544,7 @@ pub mod td_microseconds { S: ser::Serializer, { serializer.serialize_i64(td.num_microseconds().ok_or(ser::Error::custom( - "value out of range for a timedelta with microsecond precision", + "value out of range for a TimeDelta with microsecond precision", ))?) } @@ -1579,10 +1579,10 @@ pub mod td_microseconds { type Value = TimeDelta; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a timedelta in microseconds") + formatter.write_str("a TimeDelta in microseconds") } - /// Deserialize a timedelta in microseconds + /// Deserialize a [`TimeDelta`] in microseconds fn visit_i64(self, value: i64) -> Result where E: de::Error, @@ -1590,7 +1590,7 @@ pub mod td_microseconds { Ok(TimeDelta::microseconds(value)) } - /// Deserialize a timedelta in microseconds + /// Deserialize a [`TimeDelta`] in microseconds fn visit_u64(self, value: u64) -> Result where E: de::Error, @@ -1600,7 +1600,7 @@ pub mod td_microseconds { } } -/// Ser/de to/from optional timedeltas in microseconds +/// Ser/de to/from optional [`crate::TimeDelta`]s in microseconds /// /// Intended for use with `serde`'s `with` attribute. /// @@ -1666,7 +1666,7 @@ pub mod td_microseconds_option { { match *opt { Some(ref td) => serializer.serialize_some(&td.num_microseconds().ok_or( - ser::Error::custom("value out of range for a timedelta with microsecond precision"), + ser::Error::custom("value out of range for a TimeDelta with microsecond precision"), )?), None => serializer.serialize_none(), } @@ -1705,10 +1705,10 @@ pub mod td_microseconds_option { type Value = Option; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a timedelta in microseconds or none") + formatter.write_str("a TimeDelta in microseconds or none") } - /// Deserialize a timedelta in microseconds + /// Deserialize a [`TimeDelta`] in microseconds fn visit_some(self, d: D) -> Result where D: de::Deserializer<'de>, @@ -1716,7 +1716,7 @@ pub mod td_microseconds_option { d.deserialize_i64(MicroSecondsTimeDeltaVisitor).map(Some) } - /// Deserialize a timedelta in microseconds + /// Deserialize a [`TimeDelta`] in microseconds fn visit_none(self) -> Result where E: de::Error, @@ -1724,7 +1724,7 @@ pub mod td_microseconds_option { Ok(None) } - /// Deserialize a timedelta in microseconds + /// Deserialize a [`TimeDelta`] in microseconds fn visit_unit(self) -> Result where E: de::Error, @@ -1734,7 +1734,7 @@ pub mod td_microseconds_option { } } -/// Ser/de to/from timedeltas in milliseconds +/// Ser/de to/from [`crate::TimeDelta`]s in milliseconds /// /// Intended for use with `serde`'s `with` attribute. /// @@ -1774,7 +1774,7 @@ pub mod td_milliseconds { /// /// # Errors /// - /// This function returns an error on an out of range `TimeDelta`. + /// This function returns an error on an out of range [`TimeDelta`]. /// /// # Example: /// @@ -1833,10 +1833,10 @@ pub mod td_milliseconds { type Value = TimeDelta; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a timedelta in milliseconds") + formatter.write_str("a TimeDelta in milliseconds") } - /// Deserialize a timedelta in milliseconds + /// Deserialize a [`TimeDelta`] in milliseconds fn visit_i64(self, value: i64) -> Result where E: de::Error, @@ -1844,7 +1844,7 @@ pub mod td_milliseconds { Ok(TimeDelta::milliseconds(value)) } - /// Deserialize a timedelta in milliseconds + /// Deserialize a [`TimeDelta`] in milliseconds fn visit_u64(self, value: u64) -> Result where E: de::Error, @@ -1854,7 +1854,7 @@ pub mod td_milliseconds { } } -/// Ser/de to/from optional timedeltas in milliseconds +/// Ser/de to/from optional [`crate::TimeDelta`]s in milliseconds /// /// Intended for use with `serde`'s `with` attribute. /// @@ -1957,10 +1957,10 @@ pub mod td_milliseconds_option { type Value = Option; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a timedelta in milliseconds or none") + formatter.write_str("a TimeDelta in milliseconds or none") } - /// Deserialize a timedelta in milliseconds + /// Deserialize a [`TimeDelta`] in milliseconds fn visit_some(self, d: D) -> Result where D: de::Deserializer<'de>, @@ -1968,7 +1968,7 @@ pub mod td_milliseconds_option { d.deserialize_i64(MilliSecondsTimeDeltaVisitor).map(Some) } - /// Deserialize a timedelta in milliseconds + /// Deserialize a [`TimeDelta`] in milliseconds fn visit_none(self) -> Result where E: de::Error, @@ -1976,7 +1976,7 @@ pub mod td_milliseconds_option { Ok(None) } - /// Deserialize a timedelta in milliseconds + /// Deserialize a [`TimeDelta`] in milliseconds fn visit_unit(self) -> Result where E: de::Error, @@ -1986,7 +1986,7 @@ pub mod td_milliseconds_option { } } -/// Ser/de to/from timedeltas in seconds +/// Ser/de to/from [`crate::TimeDelta`]s in seconds /// /// Intended for use with `serde`'s `with` attribute. /// @@ -2026,7 +2026,7 @@ pub mod td_seconds { /// /// # Errors /// - /// This function returns an error on an out of range `TimeDelta`. + /// This function returns an error on an out of range [`TimeDelta`]. /// /// # Example: /// @@ -2085,10 +2085,10 @@ pub mod td_seconds { type Value = TimeDelta; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a timedelta in seconds") + formatter.write_str("a TimeDelta in seconds") } - /// Deserialize a timedelta in seconds + /// Deserialize a [`TimeDelta`] in seconds fn visit_i64(self, value: i64) -> Result where E: de::Error, @@ -2096,7 +2096,7 @@ pub mod td_seconds { Ok(TimeDelta::seconds(value)) } - /// Deserialize a timedelta in seconds + /// Deserialize a [`TimeDelta`] in seconds fn visit_u64(self, value: u64) -> Result where E: de::Error, @@ -2106,7 +2106,7 @@ pub mod td_seconds { } } -/// Ser/de to/from optional timedeltas in seconds +/// Ser/de to/from optional [`crate::TimeDelta`]s in seconds /// /// Intended for use with `serde`'s `with` attribute. /// @@ -2209,10 +2209,10 @@ pub mod td_seconds_option { type Value = Option; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a timedelta in seconds or none") + formatter.write_str("a TimeDelta in seconds or none") } - /// Deserialize a timedelta in seconds + /// Deserialize a [`TimeDelta`] in seconds fn visit_some(self, d: D) -> Result where D: de::Deserializer<'de>, @@ -2220,7 +2220,7 @@ pub mod td_seconds_option { d.deserialize_i64(SecondsTimeDeltaVisitor).map(Some) } - /// Deserialize a timedelta in seconds + /// Deserialize a [`TimeDelta`] in seconds fn visit_none(self) -> Result where E: de::Error, @@ -2228,7 +2228,7 @@ pub mod td_seconds_option { Ok(None) } - /// Deserialize a timedelta in seconds + /// Deserialize a [`TimeDelta`] in seconds fn visit_unit(self) -> Result where E: de::Error, diff --git a/src/lib.rs b/src/lib.rs index cb846695e..23166a8f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -647,7 +647,7 @@ pub mod serde { write!(f, "value is not a legal timestamp: {}", ts) } Self::InvalidTimeDelta(td) => { - write!(f, "value is not a legal timedelta: {}", td) + write!(f, "value is not a legal TimeDelta: {}", td) } } } From ce66b33b71b2a82f0c61e22ba56990599fa7bec3 Mon Sep 17 00:00:00 2001 From: tosti007 Date: Mon, 26 May 2025 08:53:31 +0200 Subject: [PATCH 8/9] Fixup test cases --- src/datetime/serde.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/datetime/serde.rs b/src/datetime/serde.rs index f4c4d8d1d..ce0f2a576 100644 --- a/src/datetime/serde.rs +++ b/src/datetime/serde.rs @@ -1238,7 +1238,7 @@ pub mod ts_seconds_option { /// delta: TimeDelta, /// } /// -/// let time = TimeDelta::nanoseconds(2018517); +/// let delta = TimeDelta::nanoseconds(2018517); /// let my_s = S { delta: delta.clone() }; /// /// let as_string = serde_json::to_string(&my_s)?; @@ -1351,7 +1351,7 @@ pub mod td_nanoseconds { /// # Example: /// /// ```rust -/// # use chrono::Delta; +/// # use chrono::TimeDelta; /// # use serde_derive::{Deserialize, Serialize}; /// use chrono::serde::td_nanoseconds_option; /// #[derive(Deserialize, Serialize)] @@ -1401,7 +1401,7 @@ pub mod td_nanoseconds_option { /// delta: Some(TimeDelta::nanoseconds(2018517)), /// }; /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":2018517}"#); + /// assert_eq!(as_string, r#"{"delta":2018517}"#); /// # Ok::<(), serde_json::Error>(()) /// ``` pub fn serialize(opt: &Option, serializer: S) -> Result @@ -1433,7 +1433,7 @@ pub mod td_nanoseconds_option { /// } /// /// let my_s: S = serde_json::from_str(r#"{ "delta": 2018517 }"#)?; - /// assert_eq!(my_s, S { delta: TimeDelta::nanoseconds(2018517) }); + /// assert_eq!(my_s, S { delta: Some(TimeDelta::nanoseconds(2018517)) }); /// # Ok::<(), serde_json::Error>(()) /// ``` pub fn deserialize<'de, D>(d: D) -> Result, D::Error> @@ -1494,7 +1494,7 @@ pub mod td_nanoseconds_option { /// delta: TimeDelta, /// } /// -/// let time = TimeDelta::microseconds(2018517); +/// let delta = TimeDelta::microseconds(2018517); /// let my_s = S { delta: delta.clone() }; /// /// let as_string = serde_json::to_string(&my_s)?; @@ -1607,7 +1607,7 @@ pub mod td_microseconds { /// # Example: /// /// ```rust -/// # use chrono::Delta; +/// # use chrono::TimeDelta; /// # use serde_derive::{Deserialize, Serialize}; /// use chrono::serde::td_microseconds_option; /// #[derive(Deserialize, Serialize)] @@ -1657,7 +1657,7 @@ pub mod td_microseconds_option { /// delta: Some(TimeDelta::microseconds(2018517)), /// }; /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":2018517}"#); + /// assert_eq!(as_string, r#"{"delta":2018517}"#); /// # Ok::<(), serde_json::Error>(()) /// ``` pub fn serialize(opt: &Option, serializer: S) -> Result @@ -1689,7 +1689,7 @@ pub mod td_microseconds_option { /// } /// /// let my_s: S = serde_json::from_str(r#"{ "delta": 2018517 }"#)?; - /// assert_eq!(my_s, S { delta: TimeDelta::microseconds(2018517) }); + /// assert_eq!(my_s, S { delta: Some(TimeDelta::microseconds(2018517)) }); /// # Ok::<(), serde_json::Error>(()) /// ``` pub fn deserialize<'de, D>(d: D) -> Result, D::Error> @@ -1750,7 +1750,7 @@ pub mod td_microseconds_option { /// delta: TimeDelta, /// } /// -/// let time = TimeDelta::milliseconds(2018517); +/// let delta = TimeDelta::milliseconds(2018517); /// let my_s = S { delta: delta.clone() }; /// /// let as_string = serde_json::to_string(&my_s)?; @@ -1861,7 +1861,7 @@ pub mod td_milliseconds { /// # Example: /// /// ```rust -/// # use chrono::Delta; +/// # use chrono::TimeDelta; /// # use serde_derive::{Deserialize, Serialize}; /// use chrono::serde::td_milliseconds_option; /// #[derive(Deserialize, Serialize)] @@ -1911,7 +1911,7 @@ pub mod td_milliseconds_option { /// delta: Some(TimeDelta::milliseconds(2018517)), /// }; /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":2018517}"#); + /// assert_eq!(as_string, r#"{"delta":2018517}"#); /// # Ok::<(), serde_json::Error>(()) /// ``` pub fn serialize(opt: &Option, serializer: S) -> Result @@ -1941,7 +1941,7 @@ pub mod td_milliseconds_option { /// } /// /// let my_s: S = serde_json::from_str(r#"{ "delta": 2018517 }"#)?; - /// assert_eq!(my_s, S { delta: TimeDelta::milliseconds(2018517) }); + /// assert_eq!(my_s, S { delta: Some(TimeDelta::milliseconds(2018517)) }); /// # Ok::<(), serde_json::Error>(()) /// ``` pub fn deserialize<'de, D>(d: D) -> Result, D::Error> @@ -2002,7 +2002,7 @@ pub mod td_milliseconds_option { /// delta: TimeDelta, /// } /// -/// let time = TimeDelta::seconds(2018517); +/// let delta = TimeDelta::seconds(2018517); /// let my_s = S { delta: delta.clone() }; /// /// let as_string = serde_json::to_string(&my_s)?; @@ -2113,7 +2113,7 @@ pub mod td_seconds { /// # Example: /// /// ```rust -/// # use chrono::Delta; +/// # use chrono::TimeDelta; /// # use serde_derive::{Deserialize, Serialize}; /// use chrono::serde::td_seconds_option; /// #[derive(Deserialize, Serialize)] @@ -2163,7 +2163,7 @@ pub mod td_seconds_option { /// delta: Some(TimeDelta::seconds(2018517)), /// }; /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":2018517}"#); + /// assert_eq!(as_string, r#"{"delta":2018517}"#); /// # Ok::<(), serde_json::Error>(()) /// ``` pub fn serialize(opt: &Option, serializer: S) -> Result @@ -2193,7 +2193,7 @@ pub mod td_seconds_option { /// } /// /// let my_s: S = serde_json::from_str(r#"{ "delta": 2018517 }"#)?; - /// assert_eq!(my_s, S { delta: TimeDelta::seconds(2018517) }); + /// assert_eq!(my_s, S { delta: Some(TimeDelta::seconds(2018517)) }); /// # Ok::<(), serde_json::Error>(()) /// ``` pub fn deserialize<'de, D>(d: D) -> Result, D::Error> From 51ee664949878899efd6021249aee1b6eaba6da5 Mon Sep 17 00:00:00 2001 From: tosti007 Date: Wed, 4 Jun 2025 13:20:43 +0200 Subject: [PATCH 9/9] Add tests --- src/datetime/serde.rs | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/datetime/serde.rs b/src/datetime/serde.rs index ce0f2a576..480341e64 100644 --- a/src/datetime/serde.rs +++ b/src/datetime/serde.rs @@ -2241,7 +2241,7 @@ pub mod td_seconds_option { mod tests { #[cfg(feature = "clock")] use crate::Local; - use crate::{DateTime, FixedOffset, TimeZone, Utc}; + use crate::{DateTime, FixedOffset, TimeDelta, TimeZone, Utc}; use core::fmt; #[test] @@ -2387,4 +2387,35 @@ mod tests { assert_eq!(dt, decoded); assert_eq!(dt.offset().fix(), *decoded.offset()); } + + #[test] + fn test_serde_td_nanoseconds() { + let delta1 = TimeDelta::nanoseconds(2018517); + + let mut bytes = Vec::new(); + let mut serializer = serde_json::Serializer::new(&mut bytes); + + assert!(super::td_nanoseconds::serialize(&delta1, &mut serializer).is_ok()); + + let delta2 = + super::td_nanoseconds::deserialize(&mut serde_json::Deserializer::from_slice(&bytes)); + + assert_eq!(Some(delta1), delta2.ok()); + } + + #[test] + fn test_serde_td_microseconds_zero() { + let delta = + super::td_microseconds::deserialize(&mut serde_json::Deserializer::from_str("0")); + + assert_eq!(Some(TimeDelta::zero()), delta.ok()); + } + + #[test] + fn test_serde_td_seconds_option_null() { + let delta = + super::td_seconds_option::deserialize(&mut serde_json::Deserializer::from_str("null")); + + assert_eq!(Some(None), delta.ok()); + } }