diff --git a/src/datetime/mod.rs b/src/datetime/mod.rs index 2701cd1776..24e7e9f16a 100644 --- a/src/datetime/mod.rs +++ b/src/datetime/mod.rs @@ -253,6 +253,23 @@ impl DateTime { self.datetime.timestamp_micros() } + /// Returns the number of non-leap-nanoseconds since January 1, 1970 UTC. + /// + /// # Panics + /// + /// An `i64` with nanosecond precision can span a range of ~584 years. This function panics 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. + #[deprecated(since = "0.4.31", note = "use `timestamp_nanos_opt()` instead")] + #[inline] + #[must_use] + pub fn timestamp_nanos(&self) -> i64 { + self.timestamp_nanos_opt() + .expect("value can not be represented in a timestamp with nanosecond precision.") + } + /// Returns the number of non-leap-nanoseconds since January 1, 1970 UTC. /// /// # Panics @@ -269,15 +286,15 @@ impl DateTime { /// use chrono::{Utc, NaiveDate}; /// /// let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_nano_opt(0, 0, 1, 444).unwrap().and_local_timezone(Utc).unwrap(); - /// assert_eq!(dt.timestamp_nanos(), 1_000_000_444); + /// assert_eq!(dt.timestamp_nanos_opt(), Some(1_000_000_444)); /// /// let dt = NaiveDate::from_ymd_opt(2001, 9, 9).unwrap().and_hms_nano_opt(1, 46, 40, 555).unwrap().and_local_timezone(Utc).unwrap(); - /// assert_eq!(dt.timestamp_nanos(), 1_000_000_000_000_000_555); + /// assert_eq!(dt.timestamp_nanos_opt(), Some(1_000_000_000_000_000_555)); /// ``` #[inline] #[must_use] - pub fn timestamp_nanos(&self) -> i64 { - self.datetime.timestamp_nanos() + pub fn timestamp_nanos_opt(&self) -> Option { + self.datetime.timestamp_nanos_opt() } /// Returns the number of milliseconds since the last second boundary. diff --git a/src/datetime/serde.rs b/src/datetime/serde.rs index 61f7f68840..4044eb586b 100644 --- a/src/datetime/serde.rs +++ b/src/datetime/serde.rs @@ -157,6 +157,14 @@ pub mod ts_nanoseconds { /// /// 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 @@ -180,7 +188,9 @@ pub mod ts_nanoseconds { where S: ser::Serializer, { - serializer.serialize_i64(dt.timestamp_nanos()) + 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 @@ -286,6 +296,14 @@ pub mod ts_nanoseconds_option { /// /// 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 @@ -310,7 +328,9 @@ pub mod ts_nanoseconds_option { S: ser::Serializer, { match *opt { - Some(ref dt) => serializer.serialize_some(&dt.timestamp_nanos()), + 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(), } } diff --git a/src/naive/datetime/mod.rs b/src/naive/datetime/mod.rs index 09268aeffd..b2f7d63bf0 100644 --- a/src/naive/datetime/mod.rs +++ b/src/naive/datetime/mod.rs @@ -460,6 +460,26 @@ impl NaiveDateTime { /// /// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:44.0 and /// 2262-04-11T23:47:16.854775804. + #[deprecated(since = "0.4.31", note = "use `timestamp_nanos_opt()` instead")] + #[inline] + #[must_use] + pub fn timestamp_nanos(&self) -> i64 { + self.timestamp_nanos_opt() + .expect("value can not be represented in a timestamp with nanosecond precision.") + } + + /// Returns the number of non-leap *nanoseconds* since midnight on January 1, 1970. + /// + /// Note that this does *not* account for the timezone! + /// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch. + /// + /// # Errors + /// + /// An `i64` with nanosecond precision can span a range of ~584 years. This function returns + /// `None` on an out of range `NaiveDateTime`. + /// + /// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:44.0 and + /// 2262-04-11T23:47:16.854775804. /// /// # Example /// @@ -467,12 +487,12 @@ impl NaiveDateTime { /// use chrono::{NaiveDate, NaiveDateTime}; /// /// let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_nano_opt(0, 0, 1, 444).unwrap(); - /// assert_eq!(dt.timestamp_nanos(), 1_000_000_444); + /// assert_eq!(dt.timestamp_nanos_opt(), Some(1_000_000_444)); /// /// let dt = NaiveDate::from_ymd_opt(2001, 9, 9).unwrap().and_hms_nano_opt(1, 46, 40, 555).unwrap(); /// /// const A_BILLION: i64 = 1_000_000_000; - /// let nanos = dt.timestamp_nanos(); + /// let nanos = dt.timestamp_nanos_opt().unwrap(); /// assert_eq!(nanos, 1_000_000_000_000_000_555); /// assert_eq!( /// Some(dt), @@ -481,11 +501,8 @@ impl NaiveDateTime { /// ``` #[inline] #[must_use] - pub fn timestamp_nanos(&self) -> i64 { - self.timestamp() - .checked_mul(1_000_000_000) - .and_then(|ns| ns.checked_add(i64::from(self.timestamp_subsec_nanos()))) - .expect("value can not be represented in a timestamp with nanosecond precision.") + pub fn timestamp_nanos_opt(&self) -> Option { + self.timestamp().checked_mul(1_000_000_000)?.checked_add(self.time.nanosecond() as i64) } /// Returns the number of milliseconds since the last whole non-leap second. diff --git a/src/naive/datetime/serde.rs b/src/naive/datetime/serde.rs index 49dfb8390d..86d8b20f4c 100644 --- a/src/naive/datetime/serde.rs +++ b/src/naive/datetime/serde.rs @@ -91,6 +91,14 @@ pub mod ts_nanoseconds { /// /// 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 @@ -114,7 +122,9 @@ pub mod ts_nanoseconds { where S: ser::Serializer, { - serializer.serialize_i64(dt.timestamp_nanos()) + serializer.serialize_i64(dt.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 @@ -218,6 +228,14 @@ pub mod ts_nanoseconds_option { /// /// 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 @@ -242,7 +260,9 @@ pub mod ts_nanoseconds_option { S: ser::Serializer, { match *opt { - Some(ref dt) => serializer.serialize_some(&dt.timestamp_nanos()), + 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(), } } diff --git a/src/naive/datetime/tests.rs b/src/naive/datetime/tests.rs index 5ded98f385..39187de67e 100644 --- a/src/naive/datetime/tests.rs +++ b/src/naive/datetime/tests.rs @@ -386,7 +386,7 @@ fn test_nanosecond_range() { const A_BILLION: i64 = 1_000_000_000; let maximum = "2262-04-11T23:47:16.854775804"; let parsed: NaiveDateTime = maximum.parse().unwrap(); - let nanos = parsed.timestamp_nanos(); + let nanos = parsed.timestamp_nanos_opt().unwrap(); assert_eq!( parsed, NaiveDateTime::from_timestamp_opt(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap() @@ -394,29 +394,23 @@ fn test_nanosecond_range() { let minimum = "1677-09-21T00:12:44.000000000"; let parsed: NaiveDateTime = minimum.parse().unwrap(); - let nanos = parsed.timestamp_nanos(); + let nanos = parsed.timestamp_nanos_opt().unwrap(); assert_eq!( parsed, NaiveDateTime::from_timestamp_opt(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap() ); -} -#[test] -#[should_panic] -fn test_nanosecond_just_beyond_range() { + // Just beyond range let maximum = "2262-04-11T23:47:16.854775804"; let parsed: NaiveDateTime = maximum.parse().unwrap(); let beyond_max = parsed + OldDuration::milliseconds(300); - let _ = beyond_max.timestamp_nanos(); -} + assert!(beyond_max.timestamp_nanos_opt().is_none()); -#[test] -#[should_panic] -fn test_nanosecond_far_beyond_range() { + // Far beyond range let maximum = "2262-04-11T23:47:16.854775804"; let parsed: NaiveDateTime = maximum.parse().unwrap(); let beyond_max = parsed + OldDuration::days(365); - let _ = beyond_max.timestamp_nanos(); + assert!(beyond_max.timestamp_nanos_opt().is_none()); } #[test] diff --git a/src/round.rs b/src/round.rs index 9271353b77..c8e7b18f23 100644 --- a/src/round.rs +++ b/src/round.rs @@ -142,9 +142,6 @@ pub trait DurationRound: Sized { fn duration_trunc(self, duration: Duration) -> Result; } -/// The maximum number of seconds a DateTime can be to be represented as nanoseconds -const MAX_SECONDS_TIMESTAMP_FOR_NANOS: i64 = 9_223_372_036; - impl DurationRound for DateTime { type Err = RoundingError; @@ -181,10 +178,7 @@ where if span < 0 { return Err(RoundingError::DurationExceedsLimit); } - if naive.timestamp().abs() > MAX_SECONDS_TIMESTAMP_FOR_NANOS { - return Err(RoundingError::TimestampExceedsLimit); - } - let stamp = naive.timestamp_nanos(); + let stamp = naive.timestamp_nanos_opt().ok_or(RoundingError::TimestampExceedsLimit)?; if span > stamp.abs() { return Err(RoundingError::DurationExceedsTimestamp); } @@ -223,10 +217,7 @@ where if span < 0 { return Err(RoundingError::DurationExceedsLimit); } - if naive.timestamp().abs() > MAX_SECONDS_TIMESTAMP_FOR_NANOS { - return Err(RoundingError::TimestampExceedsLimit); - } - let stamp = naive.timestamp_nanos(); + let stamp = naive.timestamp_nanos_opt().ok_or(RoundingError::TimestampExceedsLimit)?; if span > stamp.abs() { return Err(RoundingError::DurationExceedsTimestamp); }