From b175f9ca2ad7eac085406eb89443300fc95482a8 Mon Sep 17 00:00:00 2001 From: Pavel Pscheidl Date: Wed, 14 Sep 2022 22:05:25 +0200 Subject: [PATCH] fix: Negative UNIX timestamps accepted by `from_timestamp_millis` --- src/naive/datetime/mod.rs | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/naive/datetime/mod.rs b/src/naive/datetime/mod.rs index adae2062ff..c0f0def5c1 100644 --- a/src/naive/datetime/mod.rs +++ b/src/naive/datetime/mod.rs @@ -35,6 +35,11 @@ mod tests; /// touching that call when we are already sure that it WILL overflow... const MAX_SECS_BITS: usize = 44; +/// Number of nanoseconds in a millisecond +const NANOS_IN_MILLISECOND: u32 = 1_000_000; +/// Number of nanoseconds in a second +const NANOS_IN_SECOND: u32 = 1000 * NANOS_IN_MILLISECOND; + /// The minimum possible `NaiveDateTime`. #[deprecated(since = "0.4.20", note = "Use NaiveDateTime::MIN instead")] pub const MIN_DATETIME: NaiveDateTime = NaiveDateTime::MIN; @@ -126,7 +131,8 @@ impl NaiveDateTime { let datetime = NaiveDateTime::from_timestamp_opt(secs, nsecs); datetime.expect("invalid or out-of-range datetime") } - /// Creates a new [NaiveDateTime] from milliseconds since the UNIX Epoch. + + /// Creates a new [NaiveDateTime] from milliseconds since the UNIX epoch. /// /// The UNIX epoch starts on midnight, January 1, 1970, UTC. /// @@ -140,12 +146,24 @@ impl NaiveDateTime { /// let naive_datetime = NaiveDateTime::from_timestamp_millis(timestamp_millis); /// assert!(naive_datetime.is_some()); /// assert_eq!(timestamp_millis, naive_datetime.unwrap().timestamp_millis()); + /// + /// // Negative timestamps (before the UNIX epoch) are supported as well. + /// let timestamp_millis: i64 = -2208936075; //Mon Jan 01 1900 14:38:45 GMT+0000 + /// let naive_datetime = NaiveDateTime::from_timestamp_millis(timestamp_millis); + /// assert!(naive_datetime.is_some()); + /// assert_eq!(timestamp_millis, naive_datetime.unwrap().timestamp_millis()); /// ``` #[inline] pub fn from_timestamp_millis(millis: i64) -> Option { let secs = millis / 1000; - let nsecs = (millis % 1000) as u32 * 1_000_000; - NaiveDateTime::from_timestamp_opt(secs, nsecs) + + if millis < 0 { + let nsecs = (millis % 1000).abs() as u32 * NANOS_IN_MILLISECOND; + NaiveDateTime::from_timestamp_opt(secs - 1, NANOS_IN_SECOND - nsecs) + } else { + let nsecs = (millis % 1000) as u32 * NANOS_IN_MILLISECOND; + NaiveDateTime::from_timestamp_opt(secs, nsecs) + } } /// Makes a new `NaiveDateTime` corresponding to a UTC date and time,