From 8850123276c44b7ad002abd289f9d7ffd8867f7e Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Tue, 27 Feb 2024 14:01:49 +0100 Subject: [PATCH 1/3] Move `serde_from` and `SerdeError` to `crate::serde` inline module --- src/datetime/serde.rs | 25 +++++++++------- src/lib.rs | 52 +++++++++++++++++++++++++++++++++ src/naive/datetime/serde.rs | 57 +++---------------------------------- 3 files changed, 70 insertions(+), 64 deletions(-) diff --git a/src/datetime/serde.rs b/src/datetime/serde.rs index e9f5d7e3ef..0d6deba471 100644 --- a/src/datetime/serde.rs +++ b/src/datetime/serde.rs @@ -3,7 +3,6 @@ use serde::{de, ser}; use super::DateTime; use crate::format::{write_rfc3339, SecondsFormat}; -use crate::naive::datetime::serde::serde_from; #[cfg(feature = "clock")] use crate::offset::Local; use crate::offset::{FixedOffset, Offset, TimeZone, Utc}; @@ -148,10 +147,10 @@ pub mod ts_nanoseconds { use core::fmt; use serde::{de, ser}; - use crate::offset::TimeZone; - use crate::{DateTime, Utc}; + use crate::serde::serde_from; + use crate::{DateTime, TimeZone, Utc}; - use super::{serde_from, NanoSecondsTimestampVisitor}; + use super::NanoSecondsTimestampVisitor; /// Serialize a UTC datetime into an integer number of nanoseconds since the epoch /// @@ -448,9 +447,10 @@ pub mod ts_microseconds { use core::fmt; use serde::{de, ser}; - use super::{serde_from, MicroSecondsTimestampVisitor}; - use crate::offset::TimeZone; - use crate::{DateTime, Utc}; + use crate::serde::serde_from; + use crate::{DateTime, TimeZone, Utc}; + + use super::MicroSecondsTimestampVisitor; /// Serialize a UTC datetime into an integer number of microseconds since the epoch /// @@ -726,9 +726,10 @@ pub mod ts_milliseconds { use core::fmt; use serde::{de, ser}; - use super::{serde_from, MilliSecondsTimestampVisitor}; - use crate::offset::TimeZone; - use crate::{DateTime, Utc}; + use crate::serde::serde_from; + use crate::{DateTime, TimeZone, Utc}; + + use super::MilliSecondsTimestampVisitor; /// Serialize a UTC datetime into an integer number of milliseconds since the epoch /// @@ -1005,9 +1006,11 @@ pub mod ts_seconds { use core::fmt; use serde::{de, ser}; - use super::{serde_from, SecondsTimestampVisitor}; + use crate::serde::serde_from; use crate::{DateTime, LocalResult, TimeZone, 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. diff --git a/src/lib.rs b/src/lib.rs index 39f310dd22..82406a0d79 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -624,7 +624,59 @@ pub use naive::__BenchYearFlags; /// [2]: https://serde.rs/field-attrs.html#with #[cfg(feature = "serde")] pub mod serde { + use crate::offset::LocalResult; + use core::fmt; + use serde::de; + pub use super::datetime::serde::*; + + // lik? function to convert a LocalResult into a serde-ish Result + pub(crate) fn serde_from(me: LocalResult, ts: &V) -> Result + where + E: de::Error, + V: fmt::Display, + T: fmt::Display, + { + match me { + LocalResult::None => Err(E::custom(ne_timestamp(ts))), + LocalResult::Ambiguous(min, max) => { + Err(E::custom(SerdeError::Ambiguous { timestamp: ts, min, max })) + } + LocalResult::Single(val) => Ok(val), + } + } + + pub(crate) enum SerdeError { + NonExistent { timestamp: V }, + Ambiguous { timestamp: V, min: D, max: D }, + } + + /// Construct a [`SerdeError::NonExistent`] + pub(crate) fn ne_timestamp(ts: T) -> SerdeError { + SerdeError::NonExistent:: { timestamp: ts } + } + + impl fmt::Debug for SerdeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ChronoSerdeError({})", self) + } + } + + // impl core::error::Error for SerdeError {} + impl fmt::Display for SerdeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + SerdeError::NonExistent { timestamp } => { + write!(f, "value is not a legal timestamp: {}", timestamp) + } + SerdeError::Ambiguous { timestamp, min, max } => write!( + f, + "value is an ambiguous timestamp: {}, could be either of {}, {}", + timestamp, min, max + ), + } + } + } } /// Zero-copy serialization/deserialization with rkyv. diff --git a/src/naive/datetime/serde.rs b/src/naive/datetime/serde.rs index 16088e368c..0eb562ea89 100644 --- a/src/naive/datetime/serde.rs +++ b/src/naive/datetime/serde.rs @@ -2,7 +2,6 @@ use core::fmt; use serde::{de, ser}; use super::NaiveDateTime; -use crate::offset::LocalResult; /// Serialize a `NaiveDateTime` as an RFC 3339 string /// @@ -83,7 +82,7 @@ pub mod ts_nanoseconds { use core::fmt; use serde::{de, ser}; - use super::ne_timestamp; + use crate::serde::ne_timestamp; use crate::NaiveDateTime; /// Serialize a datetime into an integer number of nanoseconds since the epoch @@ -372,7 +371,7 @@ pub mod ts_microseconds { use core::fmt; use serde::{de, ser}; - use super::ne_timestamp; + use crate::serde::ne_timestamp; use crate::NaiveDateTime; /// Serialize a datetime into an integer number of microseconds since the epoch @@ -636,7 +635,7 @@ pub mod ts_milliseconds { use core::fmt; use serde::{de, ser}; - use super::ne_timestamp; + use crate::serde::ne_timestamp; use crate::NaiveDateTime; /// Serialize a datetime into an integer number of milliseconds since the epoch @@ -896,7 +895,7 @@ pub mod ts_seconds { use core::fmt; use serde::{de, ser}; - use super::ne_timestamp; + use crate::serde::ne_timestamp; use crate::NaiveDateTime; /// Serialize a datetime into an integer number of seconds since the epoch @@ -1109,54 +1108,6 @@ pub mod ts_seconds_option { } } -// lik? function to convert a LocalResult into a serde-ish Result -pub(crate) fn serde_from(me: LocalResult, ts: &V) -> Result -where - E: de::Error, - V: fmt::Display, - T: fmt::Display, -{ - match me { - LocalResult::None => Err(E::custom(ne_timestamp(ts))), - LocalResult::Ambiguous(min, max) => { - Err(E::custom(SerdeError::Ambiguous { timestamp: ts, min, max })) - } - LocalResult::Single(val) => Ok(val), - } -} - -enum SerdeError { - NonExistent { timestamp: V }, - Ambiguous { timestamp: V, min: D, max: D }, -} - -/// Construct a [`SerdeError::NonExistent`] -fn ne_timestamp(ts: T) -> SerdeError { - SerdeError::NonExistent:: { timestamp: ts } -} - -impl fmt::Debug for SerdeError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ChronoSerdeError({})", self) - } -} - -// impl core::error::Error for SerdeError {} -impl fmt::Display for SerdeError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - SerdeError::NonExistent { timestamp } => { - write!(f, "value is not a legal timestamp: {}", timestamp) - } - SerdeError::Ambiguous { timestamp, min, max } => write!( - f, - "value is an ambiguous timestamp: {}, could be either of {}, {}", - timestamp, min, max - ), - } - } -} - #[cfg(test)] mod tests { use crate::naive::datetime::{test_decodable_json, test_encodable_json}; From a7222fb4e139850dc5fc3424d97bca0b8daf972d Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 24 Feb 2024 20:52:45 +0100 Subject: [PATCH 2/3] Simplify `SerdeError` --- src/datetime/serde.rs | 72 ++++++++++++++++--------------------- src/lib.rs | 42 +++++----------------- src/naive/datetime/serde.rs | 28 +++++++-------- 3 files changed, 51 insertions(+), 91 deletions(-) diff --git a/src/datetime/serde.rs b/src/datetime/serde.rs index 0d6deba471..4ef9f95515 100644 --- a/src/datetime/serde.rs +++ b/src/datetime/serde.rs @@ -147,8 +147,8 @@ pub mod ts_nanoseconds { use core::fmt; use serde::{de, ser}; - use crate::serde::serde_from; - use crate::{DateTime, TimeZone, Utc}; + use crate::serde::invalid_ts; + use crate::{DateTime, Utc}; use super::NanoSecondsTimestampVisitor; @@ -239,13 +239,11 @@ pub mod ts_nanoseconds { where E: de::Error, { - serde_from( - Utc.timestamp_opt( - value.div_euclid(1_000_000_000), - (value.rem_euclid(1_000_000_000)) as u32, - ), - &value, + 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 @@ -253,10 +251,8 @@ pub mod ts_nanoseconds { where E: de::Error, { - serde_from( - Utc.timestamp_opt((value / 1_000_000_000) as i64, (value % 1_000_000_000) as u32), - &value, - ) + DateTime::from_timestamp((value / 1_000_000_000) as i64, (value % 1_000_000_000) as u32) + .ok_or_else(|| invalid_ts(value)) } } } @@ -447,8 +443,8 @@ pub mod ts_microseconds { use core::fmt; use serde::{de, ser}; - use crate::serde::serde_from; - use crate::{DateTime, TimeZone, Utc}; + use crate::serde::invalid_ts; + use crate::{DateTime, Utc}; use super::MicroSecondsTimestampVisitor; @@ -529,13 +525,11 @@ pub mod ts_microseconds { where E: de::Error, { - serde_from( - Utc.timestamp_opt( - value.div_euclid(1_000_000), - (value.rem_euclid(1_000_000) * 1_000) as u32, - ), - &value, + 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 @@ -543,10 +537,11 @@ pub mod ts_microseconds { where E: de::Error, { - serde_from( - Utc.timestamp_opt((value / 1_000_000) as i64, ((value % 1_000_000) * 1_000) as u32), - &value, + DateTime::from_timestamp( + (value / 1_000_000) as i64, + ((value % 1_000_000) * 1_000) as u32, ) + .ok_or_else(|| invalid_ts(value)) } } } @@ -726,8 +721,8 @@ pub mod ts_milliseconds { use core::fmt; use serde::{de, ser}; - use crate::serde::serde_from; - use crate::{DateTime, TimeZone, Utc}; + use crate::serde::invalid_ts; + use crate::{DateTime, Utc}; use super::MilliSecondsTimestampVisitor; @@ -808,7 +803,7 @@ pub mod ts_milliseconds { where E: de::Error, { - serde_from(Utc.timestamp_millis_opt(value), &value) + DateTime::from_timestamp_millis(value).ok_or_else(|| invalid_ts(value)) } /// Deserialize a timestamp in milliseconds since the epoch @@ -816,10 +811,8 @@ pub mod ts_milliseconds { where E: de::Error, { - serde_from( - Utc.timestamp_opt((value / 1000) as i64, ((value % 1000) * 1_000_000) as u32), - &value, - ) + DateTime::from_timestamp((value / 1000) as i64, ((value % 1000) * 1_000_000) as u32) + .ok_or_else(|| invalid_ts(value)) } } } @@ -1006,8 +999,8 @@ pub mod ts_seconds { use core::fmt; use serde::{de, ser}; - use crate::serde::serde_from; - use crate::{DateTime, LocalResult, TimeZone, Utc}; + use crate::serde::invalid_ts; + use crate::{DateTime, Utc}; use super::SecondsTimestampVisitor; @@ -1078,7 +1071,7 @@ pub mod ts_seconds { where E: de::Error, { - serde_from(Utc.timestamp_opt(value, 0), &value) + DateTime::from_timestamp(value, 0).ok_or_else(|| invalid_ts(value)) } /// Deserialize a timestamp in seconds since the epoch @@ -1086,14 +1079,11 @@ pub mod ts_seconds { where E: de::Error, { - serde_from( - if value > i64::MAX as u64 { - LocalResult::None - } else { - Utc.timestamp_opt(value as i64, 0) - }, - &value, - ) + if value > i64::MAX as u64 { + Err(invalid_ts(value)) + } else { + DateTime::from_timestamp(value as i64, 0).ok_or_else(|| invalid_ts(value)) + } } } } diff --git a/src/lib.rs b/src/lib.rs index 82406a0d79..030fa1067d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -624,56 +624,30 @@ pub use naive::__BenchYearFlags; /// [2]: https://serde.rs/field-attrs.html#with #[cfg(feature = "serde")] pub mod serde { - use crate::offset::LocalResult; use core::fmt; use serde::de; pub use super::datetime::serde::*; - // lik? function to convert a LocalResult into a serde-ish Result - pub(crate) fn serde_from(me: LocalResult, ts: &V) -> Result + /// Create a custom `de::Error` with `SerdeError::InvalidTimestamp`. + pub(crate) fn invalid_ts(value: T) -> E where E: de::Error, - V: fmt::Display, T: fmt::Display, { - match me { - LocalResult::None => Err(E::custom(ne_timestamp(ts))), - LocalResult::Ambiguous(min, max) => { - Err(E::custom(SerdeError::Ambiguous { timestamp: ts, min, max })) - } - LocalResult::Single(val) => Ok(val), - } - } - - pub(crate) enum SerdeError { - NonExistent { timestamp: V }, - Ambiguous { timestamp: V, min: D, max: D }, + E::custom(SerdeError::InvalidTimestamp(value)) } - /// Construct a [`SerdeError::NonExistent`] - pub(crate) fn ne_timestamp(ts: T) -> SerdeError { - SerdeError::NonExistent:: { timestamp: ts } - } - - impl fmt::Debug for SerdeError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ChronoSerdeError({})", self) - } + enum SerdeError { + InvalidTimestamp(T), } - // impl core::error::Error for SerdeError {} - impl fmt::Display for SerdeError { + impl fmt::Display for SerdeError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - SerdeError::NonExistent { timestamp } => { - write!(f, "value is not a legal timestamp: {}", timestamp) + SerdeError::InvalidTimestamp(ts) => { + write!(f, "value is not a legal timestamp: {}", ts) } - SerdeError::Ambiguous { timestamp, min, max } => write!( - f, - "value is an ambiguous timestamp: {}, could be either of {}, {}", - timestamp, min, max - ), } } } diff --git a/src/naive/datetime/serde.rs b/src/naive/datetime/serde.rs index 0eb562ea89..a72676a498 100644 --- a/src/naive/datetime/serde.rs +++ b/src/naive/datetime/serde.rs @@ -82,7 +82,7 @@ pub mod ts_nanoseconds { use core::fmt; use serde::{de, ser}; - use crate::serde::ne_timestamp; + use crate::serde::invalid_ts; use crate::NaiveDateTime; /// Serialize a datetime into an integer number of nanoseconds since the epoch @@ -175,7 +175,7 @@ pub mod ts_nanoseconds { value.div_euclid(1_000_000_000), (value.rem_euclid(1_000_000_000)) as u32, ) - .ok_or_else(|| E::custom(ne_timestamp(value))) + .ok_or_else(|| invalid_ts(value)) } fn visit_u64(self, value: u64) -> Result @@ -186,7 +186,7 @@ pub mod ts_nanoseconds { (value / 1_000_000_000) as i64, (value % 1_000_000_000) as u32, ) - .ok_or_else(|| E::custom(ne_timestamp(value))) + .ok_or_else(|| invalid_ts(value)) } } } @@ -371,7 +371,7 @@ pub mod ts_microseconds { use core::fmt; use serde::{de, ser}; - use crate::serde::ne_timestamp; + use crate::serde::invalid_ts; use crate::NaiveDateTime; /// Serialize a datetime into an integer number of microseconds since the epoch @@ -450,8 +450,7 @@ pub mod ts_microseconds { where E: de::Error, { - NaiveDateTime::from_timestamp_micros(value) - .ok_or_else(|| E::custom(ne_timestamp(value))) + NaiveDateTime::from_timestamp_micros(value).ok_or_else(|| invalid_ts(value)) } fn visit_u64(self, value: u64) -> Result @@ -462,7 +461,7 @@ pub mod ts_microseconds { (value / 1_000_000) as i64, ((value % 1_000_000) * 1_000) as u32, ) - .ok_or_else(|| E::custom(ne_timestamp(value))) + .ok_or_else(|| invalid_ts(value)) } } } @@ -635,7 +634,7 @@ pub mod ts_milliseconds { use core::fmt; use serde::{de, ser}; - use crate::serde::ne_timestamp; + use crate::serde::invalid_ts; use crate::NaiveDateTime; /// Serialize a datetime into an integer number of milliseconds since the epoch @@ -714,8 +713,7 @@ pub mod ts_milliseconds { where E: de::Error, { - NaiveDateTime::from_timestamp_millis(value) - .ok_or_else(|| E::custom(ne_timestamp(value))) + NaiveDateTime::from_timestamp_millis(value).ok_or_else(|| invalid_ts(value)) } fn visit_u64(self, value: u64) -> Result @@ -726,7 +724,7 @@ pub mod ts_milliseconds { (value / 1000) as i64, ((value % 1000) * 1_000_000) as u32, ) - .ok_or_else(|| E::custom(ne_timestamp(value))) + .ok_or_else(|| invalid_ts(value)) } } } @@ -895,7 +893,7 @@ pub mod ts_seconds { use core::fmt; use serde::{de, ser}; - use crate::serde::ne_timestamp; + use crate::serde::invalid_ts; use crate::NaiveDateTime; /// Serialize a datetime into an integer number of seconds since the epoch @@ -967,16 +965,14 @@ pub mod ts_seconds { where E: de::Error, { - NaiveDateTime::from_timestamp_opt(value, 0) - .ok_or_else(|| E::custom(ne_timestamp(value))) + NaiveDateTime::from_timestamp_opt(value, 0).ok_or_else(|| invalid_ts(value)) } fn visit_u64(self, value: u64) -> Result where E: de::Error, { - NaiveDateTime::from_timestamp_opt(value as i64, 0) - .ok_or_else(|| E::custom(ne_timestamp(value))) + NaiveDateTime::from_timestamp_opt(value as i64, 0).ok_or_else(|| invalid_ts(value)) } } } From bc03a0ffea46bfb9a84399cbff0a872998214fde Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 24 Feb 2024 22:02:19 +0100 Subject: [PATCH 3/3] Check for invalid cast --- src/naive/datetime/serde.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/naive/datetime/serde.rs b/src/naive/datetime/serde.rs index a72676a498..da4f416017 100644 --- a/src/naive/datetime/serde.rs +++ b/src/naive/datetime/serde.rs @@ -972,7 +972,11 @@ pub mod ts_seconds { where E: de::Error, { - NaiveDateTime::from_timestamp_opt(value as i64, 0).ok_or_else(|| invalid_ts(value)) + if value > i64::MAX as u64 { + Err(invalid_ts(value)) + } else { + NaiveDateTime::from_timestamp_opt(value as i64, 0).ok_or_else(|| invalid_ts(value)) + } } } }