From bab9c90253bd1bb8f3eef760c29ce73e86aba9f3 Mon Sep 17 00:00:00 2001 From: Ali Date: Wed, 15 Nov 2023 02:43:58 +0330 Subject: [PATCH 1/4] Add from_timestamp_nanos Add and implement from_timestamp_nanos and add unit test for it --- src/naive/datetime/mod.rs | 33 ++++++++++++++++++++++++ src/naive/datetime/tests.rs | 50 +++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/src/naive/datetime/mod.rs b/src/naive/datetime/mod.rs index 2f9b2db6d1..cdbf28ff5b 100644 --- a/src/naive/datetime/mod.rs +++ b/src/naive/datetime/mod.rs @@ -197,6 +197,39 @@ impl NaiveDateTime { NaiveDateTime::from_timestamp_opt(secs, nsecs) } + /// Creates a new [NaiveDateTime] from nanoseconds since the UNIX epoch. + /// + /// The UNIX epoch starts on midnight, January 1, 1970, UTC. + /// + /// # Errors + /// + /// Returns `None` if the number of nanoseconds would be out of range for a `NaiveDateTime` + /// (more than ca. 262,000 years away from common era) + /// + /// # Example + /// + /// ``` + /// use chrono::NaiveDateTime; + /// let timestamp_nanos: i64 = 1662921288_000_000_000; //Sunday, September 11, 2022 6:34:48 PM + /// let naive_datetime = NaiveDateTime::from_timestamp_nanos(timestamp_nanos); + /// assert!(naive_datetime.is_some()); + /// assert_eq!(timestamp_nanos, naive_datetime.unwrap().timestamp_nanos_opt().unwrap()); + /// + /// // Negative timestamps (before the UNIX epoch) are supported as well. + /// let timestamp_nanos: i64 = -2208936075_000_000_000; //Mon Jan 01 1900 14:38:45 GMT+0000 + /// let naive_datetime = NaiveDateTime::from_timestamp_nanos(timestamp_nanos); + /// assert!(naive_datetime.is_some()); + /// assert_eq!(timestamp_nanos, naive_datetime.unwrap().timestamp_nanos_opt().unwrap()); + /// ``` + #[inline] + #[must_use] + pub const fn from_timestamp_nanos(nanos: i64) -> Option { + let secs = nanos.div_euclid(1_000_000_000); + let nsecs = nanos.rem_euclid(1_000_000_000) as u32; + + NaiveDateTime::from_timestamp_opt(secs, nsecs) + } + /// Makes a new `NaiveDateTime` corresponding to a UTC date and time, /// from the number of non-leap seconds /// since the midnight UTC on January 1, 1970 (aka "UNIX timestamp") diff --git a/src/naive/datetime/tests.rs b/src/naive/datetime/tests.rs index 0dbbe7bc5e..7315c171ef 100644 --- a/src/naive/datetime/tests.rs +++ b/src/naive/datetime/tests.rs @@ -78,6 +78,56 @@ fn test_datetime_from_timestamp_micros() { } } +#[test] +fn test_datetime_from_timestamp_nanos() { + let valid_map = [ + (1662921288000000000, "2022-09-11 18:34:48.000000000"), + (1662921288123456000, "2022-09-11 18:34:48.123456000"), + (1662921288123456789, "2022-09-11 18:34:48.123456789"), + (1662921287890000000, "2022-09-11 18:34:47.890000000"), + (-2208936075000000000, "1900-01-01 14:38:45.000000000"), + (-5337182663000000000, "1800-11-15 01:15:37.000000000"), + (0, "1970-01-01 00:00:00.000000000"), + (119731017000000000, "1973-10-17 18:36:57.000000000"), + (1234567890000000000, "2009-02-13 23:31:30.000000000"), + (2034061609000000000, "2034-06-16 09:06:49.000000000"), + ]; + + for (timestamp_nanos, _formatted) in valid_map.iter().copied() { + let naive_datetime = NaiveDateTime::from_timestamp_nanos(timestamp_nanos).unwrap(); + assert_eq!(timestamp_nanos, naive_datetime.timestamp_nanos_opt().unwrap()); + #[cfg(feature = "alloc")] + assert_eq!(naive_datetime.format("%F %T%.9f").to_string(), _formatted); + } + + const A_BILLION: i64 = 1_000_000_000; + // Maximum datetime in nanoseconds + let maximum = "2262-04-11T23:47:16.854775804"; + let parsed: NaiveDateTime = maximum.parse().unwrap(); + let nanos = parsed.timestamp_nanos_opt().unwrap(); + let max_date_time_nanos = NaiveDateTime::from_timestamp_nanos(nanos).unwrap(); + let max_date_time_opt = NaiveDateTime::from_timestamp_opt(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap(); + assert_eq!(max_date_time_opt, max_date_time_nanos); + + // Minimum datetime in nanoseconds + let minimum = "1677-09-21T00:12:44.000000000"; + let parsed: NaiveDateTime = minimum.parse().unwrap(); + let nanos = parsed.timestamp_nanos_opt().unwrap(); + let min_date_time_opt = NaiveDateTime::from_timestamp_opt(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap(); + let min_date_time_nanos = NaiveDateTime::from_timestamp_nanos(nanos).unwrap(); + assert_eq!(min_date_time_opt, min_date_time_nanos); + + // Test that the result of `from_timestamp_nanos` compares equal to + // that of `from_timestamp_opt`. + let secs_test = [0, 1, 2, 1000, 1234, 12345678, -1, -2, -1000, -12345678]; + for secs in secs_test.iter().copied() { + assert_eq!( + NaiveDateTime::from_timestamp_nanos(secs * 1_000_000_000), + NaiveDateTime::from_timestamp_opt(secs, 0) + ); + } +} + #[test] fn test_datetime_from_timestamp() { let from_timestamp = |secs| NaiveDateTime::from_timestamp_opt(secs, 0); From 6d4051cd866ab977393e75591a3e1a9bfc445d61 Mon Sep 17 00:00:00 2001 From: Ali Date: Wed, 15 Nov 2023 22:44:39 +0330 Subject: [PATCH 2/4] Fix lint check error --- src/naive/datetime/mod.rs | 2 +- src/naive/datetime/tests.rs | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/naive/datetime/mod.rs b/src/naive/datetime/mod.rs index cdbf28ff5b..d678148ada 100644 --- a/src/naive/datetime/mod.rs +++ b/src/naive/datetime/mod.rs @@ -226,7 +226,7 @@ impl NaiveDateTime { pub const fn from_timestamp_nanos(nanos: i64) -> Option { let secs = nanos.div_euclid(1_000_000_000); let nsecs = nanos.rem_euclid(1_000_000_000) as u32; - + NaiveDateTime::from_timestamp_opt(secs, nsecs) } diff --git a/src/naive/datetime/tests.rs b/src/naive/datetime/tests.rs index 7315c171ef..6375a9d351 100644 --- a/src/naive/datetime/tests.rs +++ b/src/naive/datetime/tests.rs @@ -105,17 +105,18 @@ fn test_datetime_from_timestamp_nanos() { let maximum = "2262-04-11T23:47:16.854775804"; let parsed: NaiveDateTime = maximum.parse().unwrap(); let nanos = parsed.timestamp_nanos_opt().unwrap(); - let max_date_time_nanos = NaiveDateTime::from_timestamp_nanos(nanos).unwrap(); - let max_date_time_opt = NaiveDateTime::from_timestamp_opt(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap(); - assert_eq!(max_date_time_opt, max_date_time_nanos); - + assert_eq!( + NaiveDateTime::from_timestamp_nanos(nanos).unwrap(), + NaiveDateTime::from_timestamp_opt(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap() + ); // Minimum datetime in nanoseconds let minimum = "1677-09-21T00:12:44.000000000"; let parsed: NaiveDateTime = minimum.parse().unwrap(); let nanos = parsed.timestamp_nanos_opt().unwrap(); - let min_date_time_opt = NaiveDateTime::from_timestamp_opt(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap(); - let min_date_time_nanos = NaiveDateTime::from_timestamp_nanos(nanos).unwrap(); - assert_eq!(min_date_time_opt, min_date_time_nanos); + assert_eq!( + NaiveDateTime::from_timestamp_nanos(nanos).unwrap(), + NaiveDateTime::from_timestamp_opt(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap() + ); // Test that the result of `from_timestamp_nanos` compares equal to // that of `from_timestamp_opt`. From de4513ea75c17651cf2be124d09cbfcbad9bd169 Mon Sep 17 00:00:00 2001 From: Ali Date: Mon, 20 Nov 2023 20:10:24 +0330 Subject: [PATCH 3/4] Add pub(crate) for NANOS_PER_SEC --- src/duration.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/duration.rs b/src/duration.rs index f00b03663e..63fa66fd49 100644 --- a/src/duration.rs +++ b/src/duration.rs @@ -24,7 +24,7 @@ const NANOS_PER_MICRO: i32 = 1000; /// The number of nanoseconds in a millisecond. const NANOS_PER_MILLI: i32 = 1_000_000; /// The number of nanoseconds in seconds. -const NANOS_PER_SEC: i32 = 1_000_000_000; +pub(crate) const NANOS_PER_SEC: i32 = 1_000_000_000; /// The number of microseconds per second. const MICROS_PER_SEC: i64 = 1_000_000; /// The number of milliseconds per second. From 82a80c9d967dcf880ecd215c532c1fe7a5542aa0 Mon Sep 17 00:00:00 2001 From: Ali Date: Mon, 20 Nov 2023 20:10:55 +0330 Subject: [PATCH 4/4] Replace number with constant variable --- src/naive/datetime/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/naive/datetime/mod.rs b/src/naive/datetime/mod.rs index d678148ada..d127a3676f 100644 --- a/src/naive/datetime/mod.rs +++ b/src/naive/datetime/mod.rs @@ -13,7 +13,7 @@ use core::{fmt, str}; #[cfg(feature = "rkyv")] use rkyv::{Archive, Deserialize, Serialize}; -use crate::duration::Duration as OldDuration; +use crate::duration::{Duration as OldDuration, NANOS_PER_SEC}; #[cfg(feature = "alloc")] use crate::format::DelayedFormat; use crate::format::{parse, parse_and_remainder, ParseError, ParseResult, Parsed, StrftimeItems}; @@ -224,8 +224,8 @@ impl NaiveDateTime { #[inline] #[must_use] pub const fn from_timestamp_nanos(nanos: i64) -> Option { - let secs = nanos.div_euclid(1_000_000_000); - let nsecs = nanos.rem_euclid(1_000_000_000) as u32; + let secs = nanos.div_euclid(NANOS_PER_SEC as i64); + let nsecs = nanos.rem_euclid(NANOS_PER_SEC as i64) as u32; NaiveDateTime::from_timestamp_opt(secs, nsecs) }