From 7d7a164cdebfab82425566e4a79e734e8182ff92 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 29 Feb 2024 07:13:34 +0100 Subject: [PATCH 1/9] Add `DateTime::{from_timestamp_micros, from_timestamp_nanos}` --- src/datetime/mod.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/datetime/mod.rs b/src/datetime/mod.rs index 059f97d816..4243481f58 100644 --- a/src/datetime/mod.rs +++ b/src/datetime/mod.rs @@ -722,6 +722,78 @@ impl DateTime { Some(try_opt!(NaiveDateTime::from_timestamp_millis(millis)).and_utc()) } + /// Creates a new `DateTime` from the number of non-leap microseconds + /// since January 1, 1970 0:00:00.000 UTC (aka "UNIX timestamp"). + /// + /// This is guaranteed to round-trip with [`timestamp_micros`](DateTime::timestamp_micros). + /// + /// If you need to create a `DateTime` with a [`TimeZone`] different from [`Utc`], use + /// [`TimeZone::timestamp_micros`] or [`DateTime::with_timezone`]. + /// + /// # Errors + /// + /// Returns `None` if the number of microseconds would be out of range for a `NaiveDateTime` + /// (more than ca. 262,000 years away from common era) + /// + /// # Example + /// + /// ``` + /// use chrono::DateTime; + /// + /// let timestamp_micros: i64 = 1662921288000000; // Sun, 11 Sep 2022 18:34:48 UTC + /// let dt = DateTime::from_timestamp_micros(timestamp_micros); + /// assert!(dt.is_some()); + /// assert_eq!(timestamp_micros, dt.expect("invalid timestamp").timestamp_micros()); + /// + /// // Negative timestamps (before the UNIX epoch) are supported as well. + /// let timestamp_micros: i64 = -2208936075000000; // Mon, 1 Jan 1900 14:38:45 UTC + /// let dt = DateTime::from_timestamp_micros(timestamp_micros); + /// assert!(dt.is_some()); + /// assert_eq!(timestamp_micros, dt.expect("invalid timestamp").timestamp_micros()); + /// ``` + #[inline] + #[must_use] + pub const fn from_timestamp_micros(micros: i64) -> Option { + let secs = micros.div_euclid(1_000_000); + let nsecs = micros.rem_euclid(1_000_000) as u32 * 1000; + Self::from_timestamp(secs, nsecs) + } + + /// Creates a new [`DateTime`] from the number of non-leap microseconds + /// since January 1, 1970 0:00:00.000 UTC (aka "UNIX timestamp"). + /// + /// This is guaranteed to round-trip with [`timestamp_nanos`](DateTime::timestamp_nanos). + /// + /// If you need to create a `DateTime` with a [`TimeZone`] different from [`Utc`], use + /// [`TimeZone::timestamp_nanos`] or [`DateTime::with_timezone`]. + /// + /// The UNIX epoch starts on midnight, January 1, 1970, UTC. + /// + /// An `i64` with nanosecond precision can span a range of ~584 years. Because all values can + /// be represented as a `DateTime` this method never fails. + /// + /// # Example + /// + /// ``` + /// use chrono::DateTime; + /// + /// let timestamp_nanos: i64 = 1662921288_000_000_000; // Sun, 11 Sep 2022 18:34:48 UTC + /// let dt = DateTime::from_timestamp_nanos(timestamp_nanos); + /// assert_eq!(timestamp_nanos, dt.timestamp_nanos_opt().unwrap()); + /// + /// // Negative timestamps (before the UNIX epoch) are supported as well. + /// let timestamp_nanos: i64 = -2208936075_000_000_000; // Mon, 1 Jan 1900 14:38:45 UTC + /// let dt = DateTime::from_timestamp_nanos(timestamp_nanos); + /// assert_eq!(timestamp_nanos, dt.timestamp_nanos_opt().unwrap()); + /// ``` + #[inline] + #[must_use] + pub const fn from_timestamp_nanos(nanos: i64) -> Self { + let secs = nanos.div_euclid(1_000_000_000); + let nsecs = nanos.rem_euclid(1_000_000_000) as u32; + expect!(Self::from_timestamp(secs, nsecs), "timestamp in nanos is always in range") + } + /// The Unix Epoch, 1970-01-01 00:00:00 UTC. pub const UNIX_EPOCH: Self = Self { datetime: NaiveDateTime::UNIX_EPOCH, offset: Utc }; } From e5572011650aae84d868ebc762ee6e9dda81d6ac Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Wed, 28 Feb 2024 21:57:40 +0100 Subject: [PATCH 2/9] Use `UNIX_EPOCH` in `NaiveDateTime::default()` --- src/naive/datetime/mod.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/naive/datetime/mod.rs b/src/naive/datetime/mod.rs index 3c323cb352..3af4900dd3 100644 --- a/src/naive/datetime/mod.rs +++ b/src/naive/datetime/mod.rs @@ -2353,12 +2353,11 @@ impl str::FromStr for NaiveDateTime { /// ```rust /// use chrono::NaiveDateTime; /// -/// let default_date = NaiveDateTime::default(); -/// assert_eq!(Some(default_date), NaiveDateTime::from_timestamp_opt(0, 0)); +/// assert_eq!(NaiveDateTime::default(), NaiveDateTime::UNIX_EPOCH); /// ``` impl Default for NaiveDateTime { fn default() -> Self { - NaiveDateTime::from_timestamp_opt(0, 0).unwrap() + Self::UNIX_EPOCH } } From 07574e5ecf06eb9c6e49c4df8cd01a1492e2b5c9 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Wed, 28 Feb 2024 18:38:41 +0100 Subject: [PATCH 3/9] Move implementation of `timestamp*` to `DateTime` --- src/datetime/mod.rs | 39 +++++++++++++++++++++++++--------- src/naive/datetime/mod.rs | 44 ++++++++------------------------------- 2 files changed, 38 insertions(+), 45 deletions(-) diff --git a/src/datetime/mod.rs b/src/datetime/mod.rs index 4243481f58..3d1a96c353 100644 --- a/src/datetime/mod.rs +++ b/src/datetime/mod.rs @@ -26,9 +26,9 @@ use crate::naive::{Days, IsoWeek, NaiveDate, NaiveDateTime, NaiveTime}; #[cfg(feature = "clock")] use crate::offset::Local; use crate::offset::{FixedOffset, Offset, TimeZone, Utc}; -use crate::try_opt; #[allow(deprecated)] use crate::Date; +use crate::{expect, try_opt}; use crate::{Datelike, Months, TimeDelta, Timelike, Weekday}; #[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))] @@ -201,7 +201,10 @@ impl DateTime { #[inline] #[must_use] pub const fn timestamp(&self) -> i64 { - self.datetime.timestamp() + const UNIX_EPOCH_DAY: i64 = 719_163; + let gregorian_day = self.datetime.date().num_days_from_ce() as i64; + let seconds_from_midnight = self.datetime.time().num_seconds_from_midnight() as i64; + (gregorian_day - UNIX_EPOCH_DAY) * 86_400 + seconds_from_midnight } /// Returns the number of non-leap-milliseconds since January 1, 1970 UTC. @@ -230,7 +233,8 @@ impl DateTime { #[inline] #[must_use] pub const fn timestamp_millis(&self) -> i64 { - self.datetime.timestamp_millis() + let as_ms = self.timestamp() * 1000; + as_ms + self.timestamp_subsec_millis() as i64 } /// Returns the number of non-leap-microseconds since January 1, 1970 UTC. @@ -259,7 +263,8 @@ impl DateTime { #[inline] #[must_use] pub const fn timestamp_micros(&self) -> i64 { - self.datetime.timestamp_micros() + let as_us = self.timestamp() * 1_000_000; + as_us + self.timestamp_subsec_micros() as i64 } /// Returns the number of non-leap-nanoseconds since January 1, 1970 UTC. @@ -274,9 +279,11 @@ impl DateTime { #[deprecated(since = "0.4.31", note = "use `timestamp_nanos_opt()` instead")] #[inline] #[must_use] - #[allow(deprecated)] pub const fn timestamp_nanos(&self) -> i64 { - self.datetime.timestamp_nanos() + expect!( + self.timestamp_nanos_opt(), + "value can not be represented in a timestamp with nanosecond precision." + ) } /// Returns the number of non-leap-nanoseconds since January 1, 1970 UTC. @@ -345,7 +352,19 @@ impl DateTime { #[inline] #[must_use] pub const fn timestamp_nanos_opt(&self) -> Option { - self.datetime.timestamp_nanos_opt() + let mut timestamp = self.timestamp(); + let mut subsec_nanos = self.timestamp_subsec_nanos() as i64; + // `(timestamp * 1_000_000_000) + subsec_nanos` may create a temporary that underflows while + // the final value can be represented as an `i64`. + // As workaround we converting the negative case to: + // `((timestamp + 1) * 1_000_000_000) + (ns - 1_000_000_000)`` + // + // Also see . + if timestamp < 0 { + subsec_nanos -= 1_000_000_000; + timestamp += 1; + } + try_opt!(timestamp.checked_mul(1_000_000_000)).checked_add(subsec_nanos) } /// Returns the number of milliseconds since the last second boundary. @@ -354,7 +373,7 @@ impl DateTime { #[inline] #[must_use] pub const fn timestamp_subsec_millis(&self) -> u32 { - self.datetime.timestamp_subsec_millis() + self.timestamp_subsec_nanos() / 1_000_000 } /// Returns the number of microseconds since the last second boundary. @@ -363,7 +382,7 @@ impl DateTime { #[inline] #[must_use] pub const fn timestamp_subsec_micros(&self) -> u32 { - self.datetime.timestamp_subsec_micros() + self.timestamp_subsec_nanos() / 1_000 } /// Returns the number of nanoseconds since the last second boundary @@ -372,7 +391,7 @@ impl DateTime { #[inline] #[must_use] pub const fn timestamp_subsec_nanos(&self) -> u32 { - self.datetime.timestamp_subsec_nanos() + self.datetime.time().nanosecond() } /// Retrieves an associated offset from UTC. diff --git a/src/naive/datetime/mod.rs b/src/naive/datetime/mod.rs index 3af4900dd3..96b8ab30c6 100644 --- a/src/naive/datetime/mod.rs +++ b/src/naive/datetime/mod.rs @@ -454,10 +454,7 @@ impl NaiveDateTime { #[inline] #[must_use] pub const fn timestamp(&self) -> i64 { - const UNIX_EPOCH_DAY: i64 = 719_163; - let gregorian_day = self.date.num_days_from_ce() as i64; - let seconds_from_midnight = self.time.num_seconds_from_midnight() as i64; - (gregorian_day - UNIX_EPOCH_DAY) * 86_400 + seconds_from_midnight + self.and_utc().timestamp() } /// Returns the number of non-leap *milliseconds* since midnight on January 1, 1970. @@ -484,8 +481,7 @@ impl NaiveDateTime { #[inline] #[must_use] pub const fn timestamp_millis(&self) -> i64 { - let as_ms = self.timestamp() * 1000; - as_ms + self.timestamp_subsec_millis() as i64 + self.and_utc().timestamp_millis() } /// Returns the number of non-leap *microseconds* since midnight on January 1, 1970. @@ -508,8 +504,7 @@ impl NaiveDateTime { #[inline] #[must_use] pub const fn timestamp_micros(&self) -> i64 { - let as_us = self.timestamp() * 1_000_000; - as_us + self.timestamp_subsec_micros() as i64 + self.and_utc().timestamp_micros() } /// Returns the number of non-leap *nanoseconds* since midnight on January 1, 1970. @@ -527,11 +522,9 @@ impl NaiveDateTime { #[deprecated(since = "0.4.31", note = "use `timestamp_nanos_opt()` instead")] #[inline] #[must_use] + #[allow(deprecated)] pub const fn timestamp_nanos(&self) -> i64 { - expect!( - self.timestamp_nanos_opt(), - "value can not be represented in a timestamp with nanosecond precision." - ) + self.and_utc().timestamp_nanos() } /// Returns the number of non-leap *nanoseconds* since midnight on January 1, 1970. @@ -568,26 +561,7 @@ impl NaiveDateTime { #[inline] #[must_use] pub const fn timestamp_nanos_opt(&self) -> Option { - let mut timestamp = self.timestamp(); - let mut timestamp_subsec_nanos = self.timestamp_subsec_nanos() as i64; - - // subsec nanos are always non-negative, however the timestamp itself (both in seconds and in nanos) can be - // negative. Now i64::MIN is NOT dividable by 1_000_000_000, so - // - // (timestamp * 1_000_000_000) + nanos - // - // may underflow (even when in theory we COULD represent the datetime as i64) because we add the non-negative - // nanos AFTER the multiplication. This is fixed by converting the negative case to - // - // ((timestamp + 1) * 1_000_000_000) + (ns - 1_000_000_000) - // - // Also see . - if timestamp < 0 && timestamp_subsec_nanos > 0 { - timestamp_subsec_nanos -= 1_000_000_000; - timestamp += 1; - } - - try_opt!(timestamp.checked_mul(1_000_000_000)).checked_add(timestamp_subsec_nanos) + self.and_utc().timestamp_nanos_opt() } /// Returns the number of milliseconds since the last whole non-leap second. @@ -615,7 +589,7 @@ impl NaiveDateTime { #[inline] #[must_use] pub const fn timestamp_subsec_millis(&self) -> u32 { - self.timestamp_subsec_nanos() / 1_000_000 + self.and_utc().timestamp_subsec_millis() } /// Returns the number of microseconds since the last whole non-leap second. @@ -643,7 +617,7 @@ impl NaiveDateTime { #[inline] #[must_use] pub const fn timestamp_subsec_micros(&self) -> u32 { - self.timestamp_subsec_nanos() / 1_000 + self.and_utc().timestamp_subsec_micros() } /// Returns the number of nanoseconds since the last whole non-leap second. @@ -671,7 +645,7 @@ impl NaiveDateTime { #[inline] #[must_use] pub const fn timestamp_subsec_nanos(&self) -> u32 { - self.time.nanosecond() + self.and_utc().timestamp_subsec_nanos() } /// Adds given `TimeDelta` to the current date and time. From e798549394e5fea38e3b34f411b1287aa7965f55 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Wed, 28 Feb 2024 21:59:59 +0100 Subject: [PATCH 4/9] Move implementation of `from_timestamp*` to `DateTime` --- src/datetime/mod.rs | 35 ++++++++++++++++++++--------------- src/naive/datetime/mod.rs | 25 ++++++------------------- 2 files changed, 26 insertions(+), 34 deletions(-) diff --git a/src/datetime/mod.rs b/src/datetime/mod.rs index 3d1a96c353..5813ae831d 100644 --- a/src/datetime/mod.rs +++ b/src/datetime/mod.rs @@ -201,7 +201,6 @@ impl DateTime { #[inline] #[must_use] pub const fn timestamp(&self) -> i64 { - const UNIX_EPOCH_DAY: i64 = 719_163; let gregorian_day = self.datetime.date().num_days_from_ce() as i64; let seconds_from_midnight = self.datetime.time().num_seconds_from_midnight() as i64; (gregorian_day - UNIX_EPOCH_DAY) * 86_400 + seconds_from_midnight @@ -673,7 +672,7 @@ impl DateTime { } impl DateTime { - /// Makes a new [`DateTime`] from the number of non-leap seconds + /// Makes a new `DateTime` from the number of non-leap seconds /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp") /// and the number of nanoseconds since the last whole non-leap second. /// @@ -695,10 +694,9 @@ impl DateTime { /// # Example /// /// ``` - /// use chrono::{DateTime, Utc}; + /// use chrono::DateTime; /// - /// let dt: DateTime = - /// DateTime::::from_timestamp(1431648000, 0).expect("invalid timestamp"); + /// let dt = DateTime::from_timestamp(1431648000, 0).expect("invalid timestamp"); /// /// assert_eq!(dt.to_string(), "2015-05-15 00:00:00 UTC"); /// assert_eq!(DateTime::from_timestamp(dt.timestamp(), dt.timestamp_subsec_nanos()).unwrap(), dt); @@ -706,16 +704,20 @@ impl DateTime { #[inline] #[must_use] pub const fn from_timestamp(secs: i64, nsecs: u32) -> Option { - Some(DateTime { - datetime: try_opt!(NaiveDateTime::from_timestamp_opt(secs, nsecs)), - offset: Utc, - }) + let days = secs.div_euclid(86_400) + UNIX_EPOCH_DAY; + let secs = secs.rem_euclid(86_400); + if days < i32::MIN as i64 || days > i32::MAX as i64 { + return None; + } + let date = try_opt!(NaiveDate::from_num_days_from_ce_opt(days as i32)); + let time = try_opt!(NaiveTime::from_num_seconds_from_midnight_opt(secs as u32, nsecs)); + Some(date.and_time(time).and_utc()) } - /// Makes a new [`DateTime`] from the number of non-leap milliseconds + /// Makes a new `DateTime` from the number of non-leap milliseconds /// since January 1, 1970 0:00:00.000 UTC (aka "UNIX timestamp"). /// - /// This is guaranteed to round-trip with regard to [`timestamp_millis`](DateTime::timestamp_millis). + /// This is guaranteed to round-trip with [`timestamp_millis`](DateTime::timestamp_millis). /// /// If you need to create a `DateTime` with a [`TimeZone`] different from [`Utc`], use /// [`TimeZone::timestamp_millis_opt`] or [`DateTime::with_timezone`]. @@ -727,10 +729,9 @@ impl DateTime { /// # Example /// /// ``` - /// use chrono::{DateTime, Utc}; + /// use chrono::DateTime; /// - /// let dt: DateTime = - /// DateTime::::from_timestamp_millis(947638923004).expect("invalid timestamp"); + /// let dt = DateTime::from_timestamp_millis(947638923004).expect("invalid timestamp"); /// /// assert_eq!(dt.to_string(), "2000-01-12 01:02:03.004 UTC"); /// assert_eq!(DateTime::from_timestamp_millis(dt.timestamp_millis()).unwrap(), dt); @@ -738,7 +739,9 @@ impl DateTime { #[inline] #[must_use] pub const fn from_timestamp_millis(millis: i64) -> Option { - Some(try_opt!(NaiveDateTime::from_timestamp_millis(millis)).and_utc()) + let secs = millis.div_euclid(1000); + let nsecs = millis.rem_euclid(1000) as u32 * 1_000_000; + Self::from_timestamp(secs, nsecs) } /// Creates a new `DateTime` from the number of non-leap microseconds @@ -1878,6 +1881,8 @@ where } } +const UNIX_EPOCH_DAY: i64 = 719_163; + #[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] fn test_encodable_json(to_string_utc: FUtc, to_string_fixed: FFixed) where diff --git a/src/naive/datetime/mod.rs b/src/naive/datetime/mod.rs index 96b8ab30c6..78cf91a4d4 100644 --- a/src/naive/datetime/mod.rs +++ b/src/naive/datetime/mod.rs @@ -130,8 +130,9 @@ impl NaiveDateTime { #[inline] #[must_use] pub const fn from_timestamp(secs: i64, nsecs: u32) -> NaiveDateTime { - let datetime = NaiveDateTime::from_timestamp_opt(secs, nsecs); - expect!(datetime, "invalid or out-of-range datetime") + let datetime = + expect!(DateTime::from_timestamp(secs, nsecs), "invalid or out-of-range datetime"); + datetime.naive_utc() } /// Creates a new [NaiveDateTime] from milliseconds since the UNIX epoch. @@ -161,9 +162,7 @@ impl NaiveDateTime { #[inline] #[must_use] pub const fn from_timestamp_millis(millis: i64) -> Option { - let secs = millis.div_euclid(1000); - let nsecs = millis.rem_euclid(1000) as u32 * 1_000_000; - NaiveDateTime::from_timestamp_opt(secs, nsecs) + Some(try_opt!(DateTime::from_timestamp_millis(millis)).naive_utc()) } /// Creates a new [NaiveDateTime] from microseconds since the UNIX epoch. @@ -227,8 +226,7 @@ impl NaiveDateTime { pub const fn from_timestamp_nanos(nanos: i64) -> Option { 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) + Some(try_opt!(DateTime::from_timestamp(secs, nsecs)).naive_utc()) } /// Makes a new `NaiveDateTime` corresponding to a UTC date and time, @@ -264,18 +262,7 @@ impl NaiveDateTime { #[inline] #[must_use] pub const fn from_timestamp_opt(secs: i64, nsecs: u32) -> Option { - let days = secs.div_euclid(86_400); - let secs = secs.rem_euclid(86_400); - if days < i32::MIN as i64 || days > i32::MAX as i64 { - return None; - } - let date = - NaiveDate::from_num_days_from_ce_opt(try_opt!((days as i32).checked_add(719_163))); - let time = NaiveTime::from_num_seconds_from_midnight_opt(secs as u32, nsecs); - match (date, time) { - (Some(date), Some(time)) => Some(NaiveDateTime { date, time }), - (_, _) => None, - } + Some(try_opt!(DateTime::from_timestamp(secs, nsecs)).naive_utc()) } /// Parses a string with the specified format string and returns a new `NaiveDateTime`. From 42740bcdd4bbe6b3aa728e1509754ab03e39eca0 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Wed, 28 Feb 2024 20:57:22 +0100 Subject: [PATCH 5/9] Translate timestamp tests to `DateTime` --- src/datetime/tests.rs | 200 +++++++++++++++++++++++++++++++++--- src/naive/datetime/tests.rs | 185 --------------------------------- 2 files changed, 188 insertions(+), 197 deletions(-) diff --git a/src/datetime/tests.rs b/src/datetime/tests.rs index be83955ee7..5b6a0530e4 100644 --- a/src/datetime/tests.rs +++ b/src/datetime/tests.rs @@ -123,6 +123,194 @@ impl TimeZone for DstTester { } } +#[test] +fn test_datetime_from_timestamp_millis() { + let valid_map = [ + (1662921288000, "2022-09-11 18:34:48.000000000"), + (1662921288123, "2022-09-11 18:34:48.123000000"), + (1662921287890, "2022-09-11 18:34:47.890000000"), + (-2208936075000, "1900-01-01 14:38:45.000000000"), + (0, "1970-01-01 00:00:00.000000000"), + (119731017000, "1973-10-17 18:36:57.000000000"), + (1234567890000, "2009-02-13 23:31:30.000000000"), + (2034061609000, "2034-06-16 09:06:49.000000000"), + ]; + + for (timestamp_millis, _formatted) in valid_map.iter().copied() { + let datetime = DateTime::from_timestamp_millis(timestamp_millis).unwrap(); + assert_eq!(timestamp_millis, datetime.timestamp_millis()); + #[cfg(feature = "alloc")] + assert_eq!(datetime.format("%F %T%.9f").to_string(), _formatted); + } + + let invalid = [i64::MAX, i64::MIN]; + + for timestamp_millis in invalid.iter().copied() { + let datetime = DateTime::from_timestamp_millis(timestamp_millis); + assert!(datetime.is_none()); + } + + // Test that the result of `from_timestamp_millis` 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().cloned() { + assert_eq!(DateTime::from_timestamp_millis(secs * 1000), DateTime::from_timestamp(secs, 0)); + } +} + +#[test] +fn test_datetime_from_timestamp_micros() { + let valid_map = [ + (1662921288000000, "2022-09-11 18:34:48.000000000"), + (1662921288123456, "2022-09-11 18:34:48.123456000"), + (1662921287890000, "2022-09-11 18:34:47.890000000"), + (-2208936075000000, "1900-01-01 14:38:45.000000000"), + (0, "1970-01-01 00:00:00.000000000"), + (119731017000000, "1973-10-17 18:36:57.000000000"), + (1234567890000000, "2009-02-13 23:31:30.000000000"), + (2034061609000000, "2034-06-16 09:06:49.000000000"), + ]; + + for (timestamp_micros, _formatted) in valid_map.iter().copied() { + let datetime = DateTime::from_timestamp_micros(timestamp_micros).unwrap(); + assert_eq!(timestamp_micros, datetime.timestamp_micros()); + #[cfg(feature = "alloc")] + assert_eq!(datetime.format("%F %T%.9f").to_string(), _formatted); + } + + let invalid = [i64::MAX, i64::MIN]; + + for timestamp_micros in invalid.iter().copied() { + let datetime = DateTime::from_timestamp_micros(timestamp_micros); + assert!(datetime.is_none()); + } + + // Test that the result of `TimeZone::timestamp_micros` compares equal to + // that of `TimeZone::timestamp_opt`. + let secs_test = [0, 1, 2, 1000, 1234, 12345678, -1, -2, -1000, -12345678]; + for secs in secs_test.iter().copied() { + assert_eq!( + DateTime::from_timestamp_micros(secs * 1_000_000), + DateTime::from_timestamp(secs, 0) + ); + } +} + +#[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 datetime = DateTime::from_timestamp_nanos(timestamp_nanos); + assert_eq!(timestamp_nanos, datetime.timestamp_nanos_opt().unwrap()); + #[cfg(feature = "alloc")] + assert_eq!(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.854775804UTC"; + let parsed: DateTime = maximum.parse().unwrap(); + let nanos = parsed.timestamp_nanos_opt().unwrap(); + assert_eq!( + Some(DateTime::from_timestamp_nanos(nanos)), + DateTime::from_timestamp(nanos / A_BILLION, (nanos % A_BILLION) as u32) + ); + // Minimum datetime in nanoseconds + let minimum = "1677-09-21T00:12:44.000000000UTC"; + let parsed: DateTime = minimum.parse().unwrap(); + let nanos = parsed.timestamp_nanos_opt().unwrap(); + assert_eq!( + Some(DateTime::from_timestamp_nanos(nanos)), + DateTime::from_timestamp(nanos / A_BILLION, (nanos % A_BILLION) as u32) + ); + + // Test that the result of `TimeZone::timestamp_nanos` compares equal to + // that of `TimeZone::timestamp_opt`. + let secs_test = [0, 1, 2, 1000, 1234, 12345678, -1, -2, -1000, -12345678]; + for secs in secs_test.iter().copied() { + assert_eq!( + Some(DateTime::from_timestamp_nanos(secs * 1_000_000_000)), + DateTime::from_timestamp(secs, 0) + ); + } +} + +#[test] +fn test_datetime_from_timestamp() { + let from_timestamp = |secs| DateTime::from_timestamp(secs, 0); + let ymdhms = |y, m, d, h, n, s| { + NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap().and_utc() + }; + assert_eq!(from_timestamp(-1), Some(ymdhms(1969, 12, 31, 23, 59, 59))); + assert_eq!(from_timestamp(0), Some(ymdhms(1970, 1, 1, 0, 0, 0))); + assert_eq!(from_timestamp(1), Some(ymdhms(1970, 1, 1, 0, 0, 1))); + assert_eq!(from_timestamp(1_000_000_000), Some(ymdhms(2001, 9, 9, 1, 46, 40))); + assert_eq!(from_timestamp(0x7fffffff), Some(ymdhms(2038, 1, 19, 3, 14, 7))); + assert_eq!(from_timestamp(i64::MIN), None); + assert_eq!(from_timestamp(i64::MAX), None); +} + +#[test] +fn test_datetime_timestamp() { + let to_timestamp = |y, m, d, h, n, s| { + NaiveDate::from_ymd_opt(y, m, d) + .unwrap() + .and_hms_opt(h, n, s) + .unwrap() + .and_utc() + .timestamp() + }; + assert_eq!(to_timestamp(1969, 12, 31, 23, 59, 59), -1); + assert_eq!(to_timestamp(1970, 1, 1, 0, 0, 0), 0); + assert_eq!(to_timestamp(1970, 1, 1, 0, 0, 1), 1); + assert_eq!(to_timestamp(2001, 9, 9, 1, 46, 40), 1_000_000_000); + assert_eq!(to_timestamp(2038, 1, 19, 3, 14, 7), 0x7fffffff); +} + +#[test] +fn test_nanosecond_range() { + const A_BILLION: i64 = 1_000_000_000; + let maximum = "2262-04-11T23:47:16.854775804UTC"; + let parsed: DateTime = maximum.parse().unwrap(); + let nanos = parsed.timestamp_nanos_opt().unwrap(); + assert_eq!( + parsed, + DateTime::::from_timestamp(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap() + ); + + let minimum = "1677-09-21T00:12:44.000000000UTC"; + let parsed: DateTime = minimum.parse().unwrap(); + let nanos = parsed.timestamp_nanos_opt().unwrap(); + assert_eq!( + parsed, + DateTime::::from_timestamp(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap() + ); + + // Just beyond range + let maximum = "2262-04-11T23:47:16.854775804UTC"; + let parsed: DateTime = maximum.parse().unwrap(); + let beyond_max = parsed + TimeDelta::milliseconds(300); + assert!(beyond_max.timestamp_nanos_opt().is_none()); + + // Far beyond range + let maximum = "2262-04-11T23:47:16.854775804UTC"; + let parsed: DateTime = maximum.parse().unwrap(); + let beyond_max = parsed + TimeDelta::days(365); + assert!(beyond_max.timestamp_nanos_opt().is_none()); +} + #[test] fn test_datetime_add_days() { let est = FixedOffset::west_opt(5 * 60 * 60).unwrap(); @@ -1252,18 +1440,6 @@ fn test_datetime_from_local() { assert_eq!(datetime_west, datetime_utc.with_timezone(&timezone_west)); } -#[test] -fn test_datetime_from_timestamp_millis() { - // 2000-01-12T01:02:03:004Z - let naive_dt = - NaiveDate::from_ymd_opt(2000, 1, 12).unwrap().and_hms_milli_opt(1, 2, 3, 4).unwrap(); - let datetime_utc = DateTime::::from_naive_utc_and_offset(naive_dt, Utc); - assert_eq!( - datetime_utc, - DateTime::::from_timestamp_millis(datetime_utc.timestamp_millis()).unwrap() - ); -} - #[test] #[cfg(feature = "clock")] fn test_datetime_before_windows_api_limits() { diff --git a/src/naive/datetime/tests.rs b/src/naive/datetime/tests.rs index 4d36208c39..751ff7e696 100644 --- a/src/naive/datetime/tests.rs +++ b/src/naive/datetime/tests.rs @@ -1,147 +1,6 @@ use super::NaiveDateTime; use crate::{Datelike, FixedOffset, LocalResult, NaiveDate, TimeDelta, Utc}; -#[test] -fn test_datetime_from_timestamp_millis() { - let valid_map = [ - (1662921288000, "2022-09-11 18:34:48.000000000"), - (1662921288123, "2022-09-11 18:34:48.123000000"), - (1662921287890, "2022-09-11 18:34:47.890000000"), - (-2208936075000, "1900-01-01 14:38:45.000000000"), - (0, "1970-01-01 00:00:00.000000000"), - (119731017000, "1973-10-17 18:36:57.000000000"), - (1234567890000, "2009-02-13 23:31:30.000000000"), - (2034061609000, "2034-06-16 09:06:49.000000000"), - ]; - - for (timestamp_millis, _formatted) in valid_map.iter().copied() { - let naive_datetime = NaiveDateTime::from_timestamp_millis(timestamp_millis); - assert_eq!(timestamp_millis, naive_datetime.unwrap().timestamp_millis()); - #[cfg(feature = "alloc")] - assert_eq!(naive_datetime.unwrap().format("%F %T%.9f").to_string(), _formatted); - } - - let invalid = [i64::MAX, i64::MIN]; - - for timestamp_millis in invalid.iter().copied() { - let naive_datetime = NaiveDateTime::from_timestamp_millis(timestamp_millis); - assert!(naive_datetime.is_none()); - } - - // Test that the result of `from_timestamp_millis` 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().cloned() { - assert_eq!( - NaiveDateTime::from_timestamp_millis(secs * 1000), - NaiveDateTime::from_timestamp_opt(secs, 0) - ); - } -} - -#[test] -fn test_datetime_from_timestamp_micros() { - let valid_map = [ - (1662921288000000, "2022-09-11 18:34:48.000000000"), - (1662921288123456, "2022-09-11 18:34:48.123456000"), - (1662921287890000, "2022-09-11 18:34:47.890000000"), - (-2208936075000000, "1900-01-01 14:38:45.000000000"), - (0, "1970-01-01 00:00:00.000000000"), - (119731017000000, "1973-10-17 18:36:57.000000000"), - (1234567890000000, "2009-02-13 23:31:30.000000000"), - (2034061609000000, "2034-06-16 09:06:49.000000000"), - ]; - - for (timestamp_micros, _formatted) in valid_map.iter().copied() { - let naive_datetime = NaiveDateTime::from_timestamp_micros(timestamp_micros); - assert_eq!(timestamp_micros, naive_datetime.unwrap().timestamp_micros()); - #[cfg(feature = "alloc")] - assert_eq!(naive_datetime.unwrap().format("%F %T%.9f").to_string(), _formatted); - } - - let invalid = [i64::MAX, i64::MIN]; - - for timestamp_micros in invalid.iter().copied() { - let naive_datetime = NaiveDateTime::from_timestamp_micros(timestamp_micros); - assert!(naive_datetime.is_none()); - } - - // Test that the result of `from_timestamp_micros` 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_micros(secs * 1_000_000), - NaiveDateTime::from_timestamp_opt(secs, 0) - ); - } -} - -#[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(); - 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(); - 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`. - 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); - let ymdhms = - |y, m, d, h, n, s| NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap(); - assert_eq!(from_timestamp(-1), Some(ymdhms(1969, 12, 31, 23, 59, 59))); - assert_eq!(from_timestamp(0), Some(ymdhms(1970, 1, 1, 0, 0, 0))); - assert_eq!(from_timestamp(1), Some(ymdhms(1970, 1, 1, 0, 0, 1))); - assert_eq!(from_timestamp(1_000_000_000), Some(ymdhms(2001, 9, 9, 1, 46, 40))); - assert_eq!(from_timestamp(0x7fffffff), Some(ymdhms(2038, 1, 19, 3, 14, 7))); - assert_eq!(from_timestamp(i64::MIN), None); - assert_eq!(from_timestamp(i64::MAX), None); -} - #[test] fn test_datetime_add() { fn check( @@ -252,18 +111,6 @@ fn test_core_duration_max() { utc_dt += Duration::MAX; } -#[test] -fn test_datetime_timestamp() { - let to_timestamp = |y, m, d, h, n, s| { - NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap().timestamp() - }; - assert_eq!(to_timestamp(1969, 12, 31, 23, 59, 59), -1); - assert_eq!(to_timestamp(1970, 1, 1, 0, 0, 0), 0); - assert_eq!(to_timestamp(1970, 1, 1, 0, 0, 1), 1); - assert_eq!(to_timestamp(2001, 9, 9, 1, 46, 40), 1_000_000_000); - assert_eq!(to_timestamp(2038, 1, 19, 3, 14, 7), 0x7fffffff); -} - #[test] fn test_datetime_from_str() { // valid cases @@ -423,38 +270,6 @@ fn test_datetime_add_sub_invariant() { assert_eq!(t, time.signed_duration_since(base).num_microseconds().unwrap()); } -#[test] -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_opt().unwrap(); - assert_eq!( - parsed, - NaiveDateTime::from_timestamp_opt(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap() - ); - - let minimum = "1677-09-21T00:12:44.000000000"; - let parsed: NaiveDateTime = minimum.parse().unwrap(); - let nanos = parsed.timestamp_nanos_opt().unwrap(); - assert_eq!( - parsed, - NaiveDateTime::from_timestamp_opt(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap() - ); - - // Just beyond range - let maximum = "2262-04-11T23:47:16.854775804"; - let parsed: NaiveDateTime = maximum.parse().unwrap(); - let beyond_max = parsed + TimeDelta::milliseconds(300); - assert!(beyond_max.timestamp_nanos_opt().is_none()); - - // Far beyond range - let maximum = "2262-04-11T23:47:16.854775804"; - let parsed: NaiveDateTime = maximum.parse().unwrap(); - let beyond_max = parsed + TimeDelta::days(365); - assert!(beyond_max.timestamp_nanos_opt().is_none()); -} - #[test] fn test_and_local_timezone() { let ndt = NaiveDate::from_ymd_opt(2022, 6, 15).unwrap().and_hms_opt(18, 59, 36).unwrap(); From 2e3170acaca90b0595d4a8c506e2d91f2c67d4b5 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 29 Feb 2024 07:22:38 +0100 Subject: [PATCH 6/9] Don't use timestamp methods from `NaiveDateTime` --- src/datetime/tests.rs | 2 +- src/format/formatting.rs | 8 ++++---- src/format/parsed.rs | 9 ++++----- src/offset/local/unix.rs | 4 ++-- src/offset/mod.rs | 19 +++++++------------ src/offset/utc.rs | 8 +++----- src/round.rs | 36 ++++++++++++++++++++++-------------- 7 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/datetime/tests.rs b/src/datetime/tests.rs index 5b6a0530e4..f237fbb434 100644 --- a/src/datetime/tests.rs +++ b/src/datetime/tests.rs @@ -1103,7 +1103,7 @@ fn test_parse_from_str() { .is_err()); assert_eq!( DateTime::parse_from_str("0", "%s").unwrap(), - NaiveDateTime::from_timestamp_opt(0, 0).unwrap().and_utc().fixed_offset() + DateTime::from_timestamp(0, 0).unwrap().fixed_offset() ); assert_eq!( diff --git a/src/format/formatting.rs b/src/format/formatting.rs index 9b055289ce..f3448f94fe 100644 --- a/src/format/formatting.rs +++ b/src/format/formatting.rs @@ -231,10 +231,10 @@ fn format_inner( Timestamp => ( 1, match (date, time, off) { - (Some(d), Some(t), None) => Some(d.and_time(*t).timestamp()), - (Some(d), Some(t), Some(&(_, off))) => { - Some(d.and_time(*t).timestamp() - i64::from(off.local_minus_utc())) - } + (Some(d), Some(t), None) => Some(d.and_time(*t).and_utc().timestamp()), + (Some(d), Some(t), Some(&(_, off))) => Some( + d.and_time(*t).and_utc().timestamp() - i64::from(off.local_minus_utc()), + ), (_, _, _) => None, }, ), diff --git a/src/format/parsed.rs b/src/format/parsed.rs index 0f5005129d..be8db51788 100644 --- a/src/format/parsed.rs +++ b/src/format/parsed.rs @@ -858,7 +858,7 @@ impl Parsed { // verify the timestamp field if any // the following is safe, `timestamp` is very limited in range - let timestamp = datetime.timestamp() - i64::from(offset); + let timestamp = datetime.and_utc().timestamp() - i64::from(offset); if let Some(given_timestamp) = self.timestamp { // if `datetime` represents a leap second, it might be off by one second. if given_timestamp != timestamp @@ -883,8 +883,7 @@ impl Parsed { // reconstruct date and time fields from timestamp let ts = timestamp.checked_add(i64::from(offset)).ok_or(OUT_OF_RANGE)?; - let datetime = NaiveDateTime::from_timestamp_opt(ts, 0); - let mut datetime = datetime.ok_or(OUT_OF_RANGE)?; + let mut datetime = DateTime::from_timestamp(ts, 0).ok_or(OUT_OF_RANGE)?.naive_utc(); // fill year, ordinal, hour, minute and second fields from timestamp. // if existing fields are consistent, this will allow the full date/time reconstruction. @@ -1000,8 +999,8 @@ impl Parsed { // make a naive `DateTime` from given timestamp and (if any) nanosecond. // an empty `nanosecond` is always equal to zero, so missing nanosecond is fine. let nanosecond = self.nanosecond.unwrap_or(0); - let dt = NaiveDateTime::from_timestamp_opt(timestamp, nanosecond); - let dt = dt.ok_or(OUT_OF_RANGE)?; + let dt = + DateTime::from_timestamp(timestamp, nanosecond).ok_or(OUT_OF_RANGE)?.naive_utc(); guessed_offset = tz.offset_from_utc_datetime(&dt).fix().local_minus_utc(); } diff --git a/src/offset/local/unix.rs b/src/offset/local/unix.rs index ce96a6e3bb..0b5bfefe70 100644 --- a/src/offset/local/unix.rs +++ b/src/offset/local/unix.rs @@ -151,7 +151,7 @@ impl Cache { if !local { let offset = self .zone - .find_local_time_type(d.timestamp()) + .find_local_time_type(d.and_utc().timestamp()) .expect("unable to select local time type") .offset(); @@ -164,7 +164,7 @@ impl Cache { // we pass through the year as the year of a local point in time must either be valid in that locale, or // the entire time was skipped in which case we will return LocalResult::None anyway. self.zone - .find_local_time_type_from_local(d.timestamp(), d.year()) + .find_local_time_type_from_local(d.and_utc().timestamp(), d.year()) .expect("unable to select local time type") .map(|o| FixedOffset::east_opt(o.offset()).unwrap()) } diff --git a/src/offset/mod.rs b/src/offset/mod.rs index 26566ad020..005c6bdd9a 100644 --- a/src/offset/mod.rs +++ b/src/offset/mod.rs @@ -380,8 +380,8 @@ pub trait TimeZone: Sized + Clone { /// assert_eq!(Utc.timestamp_opt(1431648000, 0).unwrap().to_string(), "2015-05-15 00:00:00 UTC"); /// ``` fn timestamp_opt(&self, secs: i64, nsecs: u32) -> LocalResult> { - match NaiveDateTime::from_timestamp_opt(secs, nsecs) { - Some(dt) => LocalResult::Single(self.from_utc_datetime(&dt)), + match DateTime::from_timestamp(secs, nsecs) { + Some(dt) => LocalResult::Single(self.from_utc_datetime(&dt.naive_utc())), None => LocalResult::None, } } @@ -414,8 +414,8 @@ pub trait TimeZone: Sized + Clone { /// }; /// ``` fn timestamp_millis_opt(&self, millis: i64) -> LocalResult> { - match NaiveDateTime::from_timestamp_millis(millis) { - Some(dt) => LocalResult::Single(self.from_utc_datetime(&dt)), + match DateTime::from_timestamp_millis(millis) { + Some(dt) => LocalResult::Single(self.from_utc_datetime(&dt.naive_utc())), None => LocalResult::None, } } @@ -433,12 +433,7 @@ pub trait TimeZone: Sized + Clone { /// assert_eq!(Utc.timestamp_nanos(1431648000000000).timestamp(), 1431648); /// ``` fn timestamp_nanos(&self, nanos: i64) -> DateTime { - let (mut secs, mut nanos) = (nanos / 1_000_000_000, nanos % 1_000_000_000); - if nanos < 0 { - secs -= 1; - nanos += 1_000_000_000; - } - self.timestamp_opt(secs, nanos as u32).unwrap() + self.from_utc_datetime(&DateTime::from_timestamp_nanos(nanos).naive_utc()) } /// Makes a new `DateTime` from the number of non-leap microseconds @@ -452,8 +447,8 @@ pub trait TimeZone: Sized + Clone { /// assert_eq!(Utc.timestamp_micros(1431648000000).unwrap().timestamp(), 1431648); /// ``` fn timestamp_micros(&self, micros: i64) -> LocalResult> { - match NaiveDateTime::from_timestamp_micros(micros) { - Some(dt) => LocalResult::Single(self.from_utc_datetime(&dt)), + match DateTime::from_timestamp_micros(micros) { + Some(dt) => LocalResult::Single(self.from_utc_datetime(&dt.naive_utc())), None => LocalResult::None, } } diff --git a/src/offset/utc.rs b/src/offset/utc.rs index be88e37eaf..5bfbe69e9c 100644 --- a/src/offset/utc.rs +++ b/src/offset/utc.rs @@ -33,9 +33,9 @@ use crate::{Date, DateTime}; /// # Example /// /// ``` -/// use chrono::{NaiveDateTime, TimeZone, Utc}; +/// use chrono::{DateTime, TimeZone, Utc}; /// -/// let dt = Utc.from_utc_datetime(&NaiveDateTime::from_timestamp_opt(61, 0).unwrap()); +/// let dt = DateTime::from_timestamp(61, 0).unwrap(); /// /// assert_eq!(Utc.timestamp_opt(61, 0).unwrap(), dt); /// assert_eq!(Utc.with_ymd_and_hms(1970, 1, 1, 0, 1, 1).unwrap(), dt); @@ -95,9 +95,7 @@ impl Utc { pub fn now() -> DateTime { let now = SystemTime::now().duration_since(UNIX_EPOCH).expect("system time before Unix epoch"); - let naive = - NaiveDateTime::from_timestamp_opt(now.as_secs() as i64, now.subsec_nanos()).unwrap(); - Utc.from_utc_datetime(&naive) + DateTime::from_timestamp(now.as_secs() as i64, now.subsec_nanos()).unwrap() } /// Returns a `DateTime` which corresponds to the current date and time. diff --git a/src/round.rs b/src/round.rs index c70eea6a00..ef3e39d2ba 100644 --- a/src/round.rs +++ b/src/round.rs @@ -196,7 +196,8 @@ where if span < 0 { return Err(RoundingError::DurationExceedsLimit); } - let stamp = naive.timestamp_nanos_opt().ok_or(RoundingError::TimestampExceedsLimit)?; + let stamp = + naive.and_utc().timestamp_nanos_opt().ok_or(RoundingError::TimestampExceedsLimit)?; if span == 0 { return Ok(original); } @@ -232,7 +233,8 @@ where if span < 0 { return Err(RoundingError::DurationExceedsLimit); } - let stamp = naive.timestamp_nanos_opt().ok_or(RoundingError::TimestampExceedsLimit)?; + let stamp = + naive.and_utc().timestamp_nanos_opt().ok_or(RoundingError::TimestampExceedsLimit)?; let delta_down = stamp % span; match delta_down.cmp(&0) { Ordering::Equal => Ok(original), @@ -312,7 +314,7 @@ mod tests { use super::{DurationRound, RoundingError, SubsecRound, TimeDelta}; use crate::offset::{FixedOffset, TimeZone, Utc}; use crate::Timelike; - use crate::{NaiveDate, NaiveDateTime}; + use crate::{DateTime, NaiveDate}; #[test] fn test_round_subsecs() { @@ -768,15 +770,15 @@ mod tests { #[test] fn issue1010() { - let dt = NaiveDateTime::from_timestamp_opt(-4_227_854_320, 678_774_288).unwrap(); + let dt = DateTime::from_timestamp(-4_227_854_320, 678_774_288).unwrap(); let span = TimeDelta::microseconds(-7_019_067_213_869_040); assert_eq!(dt.duration_trunc(span), Err(RoundingError::DurationExceedsLimit)); - let dt = NaiveDateTime::from_timestamp_opt(320_041_586, 920_103_021).unwrap(); + let dt = DateTime::from_timestamp(320_041_586, 920_103_021).unwrap(); let span = TimeDelta::nanoseconds(-8_923_838_508_697_114_584); assert_eq!(dt.duration_round(span), Err(RoundingError::DurationExceedsLimit)); - let dt = NaiveDateTime::from_timestamp_opt(-2_621_440, 0).unwrap(); + let dt = DateTime::from_timestamp(-2_621_440, 0).unwrap(); let span = TimeDelta::nanoseconds(-9_223_372_036_854_771_421); assert_eq!(dt.duration_round(span), Err(RoundingError::DurationExceedsLimit)); } @@ -807,16 +809,22 @@ mod tests { fn test_duration_round_close_to_min_max() { let span = TimeDelta::nanoseconds(i64::MAX); - let dt = NaiveDateTime::from_timestamp_nanos(i64::MIN / 2 - 1).unwrap(); - assert_eq!(dt.duration_round(span).unwrap().to_string(), "1677-09-21 00:12:43.145224193"); + let dt = DateTime::from_timestamp_nanos(i64::MIN / 2 - 1); + assert_eq!( + dt.duration_round(span).unwrap().to_string(), + "1677-09-21 00:12:43.145224193 UTC" + ); - let dt = NaiveDateTime::from_timestamp_nanos(i64::MIN / 2 + 1).unwrap(); - assert_eq!(dt.duration_round(span).unwrap().to_string(), "1970-01-01 00:00:00"); + let dt = DateTime::from_timestamp_nanos(i64::MIN / 2 + 1); + assert_eq!(dt.duration_round(span).unwrap().to_string(), "1970-01-01 00:00:00 UTC"); - let dt = NaiveDateTime::from_timestamp_nanos(i64::MAX / 2 + 1).unwrap(); - assert_eq!(dt.duration_round(span).unwrap().to_string(), "2262-04-11 23:47:16.854775807"); + let dt = DateTime::from_timestamp_nanos(i64::MAX / 2 + 1); + assert_eq!( + dt.duration_round(span).unwrap().to_string(), + "2262-04-11 23:47:16.854775807 UTC" + ); - let dt = NaiveDateTime::from_timestamp_nanos(i64::MAX / 2 - 1).unwrap(); - assert_eq!(dt.duration_round(span).unwrap().to_string(), "1970-01-01 00:00:00"); + let dt = DateTime::from_timestamp_nanos(i64::MAX / 2 - 1); + assert_eq!(dt.duration_round(span).unwrap().to_string(), "1970-01-01 00:00:00 UTC"); } } From 202fa253ab0ea75bfcc442a2316c55485cb83c7a Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 29 Feb 2024 20:52:16 +0100 Subject: [PATCH 7/9] Don't use timestamp methods from `NaiveDateTime` in `serde` module --- src/naive/datetime/serde.rs | 126 +++++++++++++++++++++--------------- 1 file changed, 73 insertions(+), 53 deletions(-) diff --git a/src/naive/datetime/serde.rs b/src/naive/datetime/serde.rs index da4f416017..de8abd7dc5 100644 --- a/src/naive/datetime/serde.rs +++ b/src/naive/datetime/serde.rs @@ -83,7 +83,7 @@ pub mod ts_nanoseconds { use serde::{de, ser}; use crate::serde::invalid_ts; - use crate::NaiveDateTime; + use crate::{DateTime, NaiveDateTime}; /// Serialize a datetime into an integer number of nanoseconds since the epoch /// @@ -123,7 +123,7 @@ pub mod ts_nanoseconds { where S: ser::Serializer, { - serializer.serialize_i64(dt.timestamp_nanos_opt().ok_or(ser::Error::custom( + serializer.serialize_i64(dt.and_utc().timestamp_nanos_opt().ok_or(ser::Error::custom( "value out of range for a timestamp with nanosecond precision", ))?) } @@ -135,7 +135,7 @@ pub mod ts_nanoseconds { /// # Example: /// /// ```rust - /// # use chrono::NaiveDateTime; + /// # use chrono::{DateTime, NaiveDateTime}; /// # use serde_derive::Deserialize; /// use chrono::naive::serde::ts_nanoseconds::deserialize as from_nano_ts; /// #[derive(Debug, PartialEq, Deserialize)] @@ -145,10 +145,12 @@ pub mod ts_nanoseconds { /// } /// /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?; - /// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(1526522699, 918355733).unwrap() }); + /// let expected = DateTime::from_timestamp(1526522699, 918355733).unwrap().naive_utc(); + /// assert_eq!(my_s, S { time: expected }); /// /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(-1, 999_999_999).unwrap() }); + /// let expected = DateTime::from_timestamp(-1, 999_999_999).unwrap().naive_utc(); + /// assert_eq!(my_s, S { time: expected }); /// # Ok::<(), serde_json::Error>(()) /// ``` pub fn deserialize<'de, D>(d: D) -> Result @@ -171,10 +173,11 @@ pub mod ts_nanoseconds { where E: de::Error, { - NaiveDateTime::from_timestamp_opt( + DateTime::from_timestamp( value.div_euclid(1_000_000_000), (value.rem_euclid(1_000_000_000)) as u32, ) + .map(|dt| dt.naive_utc()) .ok_or_else(|| invalid_ts(value)) } @@ -182,11 +185,9 @@ pub mod ts_nanoseconds { where E: de::Error, { - NaiveDateTime::from_timestamp_opt( - (value / 1_000_000_000) as i64, - (value % 1_000_000_000) as u32, - ) - .ok_or_else(|| invalid_ts(value)) + DateTime::from_timestamp((value / 1_000_000_000) as i64, (value % 1_000_000_000) as u32) + .map(|dt| dt.naive_utc()) + .ok_or_else(|| invalid_ts(value)) } } } @@ -269,7 +270,7 @@ pub mod ts_nanoseconds_option { S: ser::Serializer, { match *opt { - Some(ref dt) => serializer.serialize_some(&dt.timestamp_nanos_opt().ok_or( + Some(ref dt) => serializer.serialize_some(&dt.and_utc().timestamp_nanos_opt().ok_or( ser::Error::custom("value out of range for a timestamp with nanosecond precision"), )?), None => serializer.serialize_none(), @@ -283,7 +284,7 @@ pub mod ts_nanoseconds_option { /// # Example: /// /// ```rust - /// # use chrono::naive::NaiveDateTime; + /// # use chrono::{DateTime, NaiveDateTime}; /// # use serde_derive::Deserialize; /// use chrono::naive::serde::ts_nanoseconds_option::deserialize as from_nano_tsopt; /// #[derive(Debug, PartialEq, Deserialize)] @@ -293,10 +294,12 @@ pub mod ts_nanoseconds_option { /// } /// /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?; - /// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(1526522699, 918355733) }); + /// let expected = DateTime::from_timestamp(1526522699, 918355733).unwrap().naive_utc(); + /// assert_eq!(my_s, S { time: Some(expected) }); /// /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(-1, 999_999_999) }); + /// let expected = DateTime::from_timestamp(-1, 999_999_999).unwrap().naive_utc(); + /// assert_eq!(my_s, S { time: Some(expected) }); /// # Ok::<(), serde_json::Error>(()) /// ``` pub fn deserialize<'de, D>(d: D) -> Result, D::Error> @@ -372,7 +375,7 @@ pub mod ts_microseconds { use serde::{de, ser}; use crate::serde::invalid_ts; - use crate::NaiveDateTime; + use crate::{DateTime, NaiveDateTime}; /// Serialize a datetime into an integer number of microseconds since the epoch /// @@ -404,7 +407,7 @@ pub mod ts_microseconds { where S: ser::Serializer, { - serializer.serialize_i64(dt.timestamp_micros()) + serializer.serialize_i64(dt.and_utc().timestamp_micros()) } /// Deserialize a `NaiveDateTime` from a microseconds timestamp @@ -414,7 +417,7 @@ pub mod ts_microseconds { /// # Example: /// /// ```rust - /// # use chrono::NaiveDateTime; + /// # use chrono::{DateTime, NaiveDateTime}; /// # use serde_derive::Deserialize; /// use chrono::naive::serde::ts_microseconds::deserialize as from_micro_ts; /// #[derive(Debug, PartialEq, Deserialize)] @@ -424,10 +427,12 @@ pub mod ts_microseconds { /// } /// /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355 }"#)?; - /// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(1526522699, 918355000).unwrap() }); + /// let expected = DateTime::from_timestamp(1526522699, 918355000).unwrap().naive_utc(); + /// assert_eq!(my_s, S { time: expected }); /// /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(-1, 999_999_000).unwrap() }); + /// let expected = DateTime::from_timestamp(-1, 999_999_000).unwrap().naive_utc(); + /// assert_eq!(my_s, S { time: expected }); /// # Ok::<(), serde_json::Error>(()) /// ``` pub fn deserialize<'de, D>(d: D) -> Result @@ -450,17 +455,20 @@ pub mod ts_microseconds { where E: de::Error, { - NaiveDateTime::from_timestamp_micros(value).ok_or_else(|| invalid_ts(value)) + DateTime::from_timestamp_micros(value) + .map(|dt| dt.naive_utc()) + .ok_or_else(|| invalid_ts(value)) } fn visit_u64(self, value: u64) -> Result where E: de::Error, { - NaiveDateTime::from_timestamp_opt( + DateTime::from_timestamp( (value / 1_000_000) as i64, ((value % 1_000_000) * 1_000) as u32, ) + .map(|dt| dt.naive_utc()) .ok_or_else(|| invalid_ts(value)) } } @@ -536,7 +544,7 @@ pub mod ts_microseconds_option { S: ser::Serializer, { match *opt { - Some(ref dt) => serializer.serialize_some(&dt.timestamp_micros()), + Some(ref dt) => serializer.serialize_some(&dt.and_utc().timestamp_micros()), None => serializer.serialize_none(), } } @@ -548,7 +556,7 @@ pub mod ts_microseconds_option { /// # Example: /// /// ```rust - /// # use chrono::naive::NaiveDateTime; + /// # use chrono::{DateTime, NaiveDateTime}; /// # use serde_derive::Deserialize; /// use chrono::naive::serde::ts_microseconds_option::deserialize as from_micro_tsopt; /// #[derive(Debug, PartialEq, Deserialize)] @@ -558,10 +566,12 @@ pub mod ts_microseconds_option { /// } /// /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355 }"#)?; - /// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(1526522699, 918355000) }); + /// let expected = DateTime::from_timestamp(1526522699, 918355000).unwrap().naive_utc(); + /// assert_eq!(my_s, S { time: Some(expected) }); /// /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(-1, 999_999_000) }); + /// let expected = DateTime::from_timestamp(-1, 999_999_000).unwrap().naive_utc(); + /// assert_eq!(my_s, S { time: Some(expected) }); /// # Ok::<(), serde_json::Error>(()) /// ``` pub fn deserialize<'de, D>(d: D) -> Result, D::Error> @@ -635,7 +645,7 @@ pub mod ts_milliseconds { use serde::{de, ser}; use crate::serde::invalid_ts; - use crate::NaiveDateTime; + use crate::{DateTime, NaiveDateTime}; /// Serialize a datetime into an integer number of milliseconds since the epoch /// @@ -667,7 +677,7 @@ pub mod ts_milliseconds { where S: ser::Serializer, { - serializer.serialize_i64(dt.timestamp_millis()) + serializer.serialize_i64(dt.and_utc().timestamp_millis()) } /// Deserialize a `NaiveDateTime` from a milliseconds timestamp @@ -677,7 +687,7 @@ pub mod ts_milliseconds { /// # Example: /// /// ```rust - /// # use chrono::NaiveDateTime; + /// # use chrono::{DateTime, NaiveDateTime}; /// # use serde_derive::Deserialize; /// use chrono::naive::serde::ts_milliseconds::deserialize as from_milli_ts; /// #[derive(Debug, PartialEq, Deserialize)] @@ -687,10 +697,12 @@ pub mod ts_milliseconds { /// } /// /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?; - /// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(1526522699, 918000000).unwrap() }); + /// let expected = DateTime::from_timestamp(1526522699, 918000000).unwrap().naive_utc(); + /// assert_eq!(my_s, S { time: expected }); /// /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(-1, 999_000_000).unwrap() }); + /// let expected = DateTime::from_timestamp(-1, 999_000_000).unwrap().naive_utc(); + /// assert_eq!(my_s, S { time: expected }); /// # Ok::<(), serde_json::Error>(()) /// ``` pub fn deserialize<'de, D>(d: D) -> Result @@ -713,18 +725,18 @@ pub mod ts_milliseconds { where E: de::Error, { - NaiveDateTime::from_timestamp_millis(value).ok_or_else(|| invalid_ts(value)) + DateTime::from_timestamp_millis(value) + .map(|dt| dt.naive_utc()) + .ok_or_else(|| invalid_ts(value)) } fn visit_u64(self, value: u64) -> Result where E: de::Error, { - NaiveDateTime::from_timestamp_opt( - (value / 1000) as i64, - ((value % 1000) * 1_000_000) as u32, - ) - .ok_or_else(|| invalid_ts(value)) + DateTime::from_timestamp((value / 1000) as i64, ((value % 1000) * 1_000_000) as u32) + .map(|dt| dt.naive_utc()) + .ok_or_else(|| invalid_ts(value)) } } } @@ -796,7 +808,7 @@ pub mod ts_milliseconds_option { S: ser::Serializer, { match *opt { - Some(ref dt) => serializer.serialize_some(&dt.timestamp_millis()), + Some(ref dt) => serializer.serialize_some(&dt.and_utc().timestamp_millis()), None => serializer.serialize_none(), } } @@ -808,7 +820,7 @@ pub mod ts_milliseconds_option { /// # Example: /// /// ```rust - /// # use chrono::naive::NaiveDateTime; + /// # use chrono::{DateTime, NaiveDateTime}; /// # use serde_derive::Deserialize; /// use chrono::naive::serde::ts_milliseconds_option::deserialize as from_milli_tsopt; /// #[derive(Debug, PartialEq, Deserialize)] @@ -818,10 +830,12 @@ pub mod ts_milliseconds_option { /// } /// /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?; - /// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(1526522699, 918000000) }); + /// let expected = DateTime::from_timestamp(1526522699, 918000000).unwrap().naive_utc(); + /// assert_eq!(my_s, S { time: Some(expected) }); /// /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(-1, 999_000_000) }); + /// let expected = DateTime::from_timestamp(-1, 999_000_000).unwrap().naive_utc(); + /// assert_eq!(my_s, S { time: Some(expected) }); /// # Ok::<(), serde_json::Error>(()) /// ``` pub fn deserialize<'de, D>(d: D) -> Result, D::Error> @@ -894,7 +908,7 @@ pub mod ts_seconds { use serde::{de, ser}; use crate::serde::invalid_ts; - use crate::NaiveDateTime; + use crate::{DateTime, NaiveDateTime}; /// Serialize a datetime into an integer number of seconds since the epoch /// @@ -922,7 +936,7 @@ pub mod ts_seconds { where S: ser::Serializer, { - serializer.serialize_i64(dt.timestamp()) + serializer.serialize_i64(dt.and_utc().timestamp()) } /// Deserialize a `NaiveDateTime` from a seconds timestamp @@ -932,7 +946,7 @@ pub mod ts_seconds { /// # Example: /// /// ```rust - /// # use chrono::NaiveDateTime; + /// # use chrono::{DateTime, NaiveDateTime}; /// # use serde_derive::Deserialize; /// use chrono::naive::serde::ts_seconds::deserialize as from_ts; /// #[derive(Debug, PartialEq, Deserialize)] @@ -942,7 +956,8 @@ pub mod ts_seconds { /// } /// /// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?; - /// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(1431684000, 0).unwrap() }); + /// let expected = DateTime::from_timestamp(1431684000, 0).unwrap().naive_utc(); + /// assert_eq!(my_s, S { time: expected }); /// # Ok::<(), serde_json::Error>(()) /// ``` pub fn deserialize<'de, D>(d: D) -> Result @@ -965,7 +980,9 @@ pub mod ts_seconds { where E: de::Error, { - NaiveDateTime::from_timestamp_opt(value, 0).ok_or_else(|| invalid_ts(value)) + DateTime::from_timestamp(value, 0) + .map(|dt| dt.naive_utc()) + .ok_or_else(|| invalid_ts(value)) } fn visit_u64(self, value: u64) -> Result @@ -975,7 +992,9 @@ pub mod ts_seconds { 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)) + DateTime::from_timestamp(value as i64, 0) + .map(|dt| dt.naive_utc()) + .ok_or_else(|| invalid_ts(value)) } } } @@ -1029,9 +1048,9 @@ pub mod ts_seconds_option { /// time: Option, /// } /// - /// let my_s = S { - /// time: Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_opt(02, 04, 59).unwrap()), - /// }; + /// let expected = + /// NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_opt(02, 04, 59).unwrap(); + /// let my_s = S { time: Some(expected) }; /// let as_string = serde_json::to_string(&my_s)?; /// assert_eq!(as_string, r#"{"time":1526522699}"#); /// # Ok::<(), serde_json::Error>(()) @@ -1041,7 +1060,7 @@ pub mod ts_seconds_option { S: ser::Serializer, { match *opt { - Some(ref dt) => serializer.serialize_some(&dt.timestamp()), + Some(ref dt) => serializer.serialize_some(&dt.and_utc().timestamp()), None => serializer.serialize_none(), } } @@ -1053,7 +1072,7 @@ pub mod ts_seconds_option { /// # Example: /// /// ```rust - /// # use chrono::naive::NaiveDateTime; + /// # use chrono::{DateTime, NaiveDateTime}; /// # use serde_derive::Deserialize; /// use chrono::naive::serde::ts_seconds_option::deserialize as from_tsopt; /// #[derive(Debug, PartialEq, Deserialize)] @@ -1063,7 +1082,8 @@ pub mod ts_seconds_option { /// } /// /// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?; - /// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(1431684000, 0) }); + /// let expected = DateTime::from_timestamp(1431684000, 0).unwrap().naive_utc(); + /// assert_eq!(my_s, S { time: Some(expected) }); /// # Ok::<(), serde_json::Error>(()) /// ``` pub fn deserialize<'de, D>(d: D) -> Result, D::Error> From 9a1707244fe5149e3f673188f310b273bf654abe Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Wed, 28 Feb 2024 18:58:45 +0100 Subject: [PATCH 8/9] Deprecate timestamp methods on `NaiveDateTime` --- src/naive/datetime/mod.rs | 203 +++----------------------------------- 1 file changed, 13 insertions(+), 190 deletions(-) diff --git a/src/naive/datetime/mod.rs b/src/naive/datetime/mod.rs index 78cf91a4d4..cf31bfb4f4 100644 --- a/src/naive/datetime/mod.rs +++ b/src/naive/datetime/mod.rs @@ -126,7 +126,7 @@ impl NaiveDateTime { /// Panics if the number of seconds would be out of range for a `NaiveDateTime` (more than /// ca. 262,000 years away from common era), and panics on an invalid nanosecond (2 seconds or /// more). - #[deprecated(since = "0.4.23", note = "use `from_timestamp_opt()` instead")] + #[deprecated(since = "0.4.23", note = "use `DateTime::from_timestamp` instead")] #[inline] #[must_use] pub const fn from_timestamp(secs: i64, nsecs: u32) -> NaiveDateTime { @@ -143,22 +143,7 @@ impl NaiveDateTime { /// /// Returns `None` if the number of milliseconds would be out of range for a `NaiveDateTime` /// (more than ca. 262,000 years away from common era) - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDateTime; - /// let timestamp_millis: i64 = 1662921288000; //Sunday, September 11, 2022 6:34:48 PM - /// 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 = -2208936075000; //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()); - /// ``` + #[deprecated(since = "0.4.34", note = "use `DateTime::from_timestamp_millis` instead")] #[inline] #[must_use] pub const fn from_timestamp_millis(millis: i64) -> Option { @@ -173,28 +158,13 @@ impl NaiveDateTime { /// /// Returns `None` if the number of microseconds would be out of range for a `NaiveDateTime` /// (more than ca. 262,000 years away from common era) - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDateTime; - /// let timestamp_micros: i64 = 1662921288000000; //Sunday, September 11, 2022 6:34:48 PM - /// let naive_datetime = NaiveDateTime::from_timestamp_micros(timestamp_micros); - /// assert!(naive_datetime.is_some()); - /// assert_eq!(timestamp_micros, naive_datetime.unwrap().timestamp_micros()); - /// - /// // Negative timestamps (before the UNIX epoch) are supported as well. - /// let timestamp_micros: i64 = -2208936075000000; //Mon Jan 01 1900 14:38:45 GMT+0000 - /// let naive_datetime = NaiveDateTime::from_timestamp_micros(timestamp_micros); - /// assert!(naive_datetime.is_some()); - /// assert_eq!(timestamp_micros, naive_datetime.unwrap().timestamp_micros()); - /// ``` + #[deprecated(since = "0.4.34", note = "use `DateTime::from_timestamp_micros` instead")] #[inline] #[must_use] pub const fn from_timestamp_micros(micros: i64) -> Option { let secs = micros.div_euclid(1_000_000); let nsecs = micros.rem_euclid(1_000_000) as u32 * 1000; - NaiveDateTime::from_timestamp_opt(secs, nsecs) + Some(try_opt!(DateTime::::from_timestamp(secs, nsecs)).naive_utc()) } /// Creates a new [NaiveDateTime] from nanoseconds since the UNIX epoch. @@ -205,22 +175,7 @@ impl NaiveDateTime { /// /// 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()); - /// ``` + #[deprecated(since = "0.4.34", note = "use `DateTime::from_timestamp_nanos` instead")] #[inline] #[must_use] pub const fn from_timestamp_nanos(nanos: i64) -> Option { @@ -243,22 +198,7 @@ impl NaiveDateTime { /// Returns `None` if the number of seconds would be out of range for a `NaiveDateTime` (more /// than ca. 262,000 years away from common era), and panics on an invalid nanosecond /// (2 seconds or more). - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDateTime; - /// use std::i64; - /// - /// let from_timestamp_opt = NaiveDateTime::from_timestamp_opt; - /// - /// assert!(from_timestamp_opt(0, 0).is_some()); - /// assert!(from_timestamp_opt(0, 999_999_999).is_some()); - /// assert!(from_timestamp_opt(0, 1_500_000_000).is_none()); // invalid leap second - /// assert!(from_timestamp_opt(59, 1_500_000_000).is_some()); // leap second - /// assert!(from_timestamp_opt(59, 2_000_000_000).is_none()); - /// assert!(from_timestamp_opt(i64::MAX, 0).is_none()); - /// ``` + #[deprecated(since = "0.4.34", note = "use `DateTime::from_timestamp` instead")] #[inline] #[must_use] pub const fn from_timestamp_opt(secs: i64, nsecs: u32) -> Option { @@ -420,24 +360,7 @@ impl NaiveDateTime { /// /// Note that this does *not* account for the timezone! /// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDate; - /// - /// let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_milli_opt(0, 0, 1, 980).unwrap(); - /// assert_eq!(dt.timestamp(), 1); - /// - /// let dt = NaiveDate::from_ymd_opt(2001, 9, 9).unwrap().and_hms_opt(1, 46, 40).unwrap(); - /// assert_eq!(dt.timestamp(), 1_000_000_000); - /// - /// let dt = NaiveDate::from_ymd_opt(1969, 12, 31).unwrap().and_hms_opt(23, 59, 59).unwrap(); - /// assert_eq!(dt.timestamp(), -1); - /// - /// let dt = NaiveDate::from_ymd_opt(-1, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); - /// assert_eq!(dt.timestamp(), -62198755200); - /// ``` + #[deprecated(since = "0.4.34", note = "use `.and_utc().timestamp()` instead")] #[inline] #[must_use] pub const fn timestamp(&self) -> i64 { @@ -448,23 +371,7 @@ impl NaiveDateTime { /// /// Note that this does *not* account for the timezone! /// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDate; - /// - /// let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_milli_opt(0, 0, 1, 444).unwrap(); - /// assert_eq!(dt.timestamp_millis(), 1_444); - /// - /// let dt = - /// NaiveDate::from_ymd_opt(2001, 9, 9).unwrap().and_hms_milli_opt(1, 46, 40, 555).unwrap(); - /// assert_eq!(dt.timestamp_millis(), 1_000_000_000_555); - /// - /// let dt = - /// NaiveDate::from_ymd_opt(1969, 12, 31).unwrap().and_hms_milli_opt(23, 59, 59, 100).unwrap(); - /// assert_eq!(dt.timestamp_millis(), -900); - /// ``` + #[deprecated(since = "0.4.34", note = "use `.and_utc().timestamp_millis()` instead")] #[inline] #[must_use] pub const fn timestamp_millis(&self) -> i64 { @@ -475,19 +382,7 @@ impl NaiveDateTime { /// /// Note that this does *not* account for the timezone! /// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDate; - /// - /// let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_micro_opt(0, 0, 1, 444).unwrap(); - /// assert_eq!(dt.timestamp_micros(), 1_000_444); - /// - /// let dt = - /// NaiveDate::from_ymd_opt(2001, 9, 9).unwrap().and_hms_micro_opt(1, 46, 40, 555).unwrap(); - /// assert_eq!(dt.timestamp_micros(), 1_000_000_000_000_555); - /// ``` + #[deprecated(since = "0.4.34", note = "use `.and_utc().timestamp_micros()` instead")] #[inline] #[must_use] pub const fn timestamp_micros(&self) -> i64 { @@ -506,7 +401,7 @@ impl NaiveDateTime { /// /// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:43.145224192 /// and 2262-04-11T23:47:16.854775807. - #[deprecated(since = "0.4.31", note = "use `timestamp_nanos_opt()` instead")] + #[deprecated(since = "0.4.31", note = "use `.and_utc().timestamp_nanos_opt()` instead")] #[inline] #[must_use] #[allow(deprecated)] @@ -526,25 +421,7 @@ impl NaiveDateTime { /// /// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:43.145224192 /// and 2262-04-11T23:47:16.854775807. - /// - /// # Example - /// - /// ``` - /// 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_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_opt().unwrap(); - /// assert_eq!(nanos, 1_000_000_000_000_000_555); - /// assert_eq!( - /// Some(dt), - /// NaiveDateTime::from_timestamp_opt(nanos / A_BILLION, (nanos % A_BILLION) as u32) - /// ); - /// ``` + #[deprecated(since = "0.4.34", note = "use `.and_utc().timestamp_nanos_opt()` instead")] #[inline] #[must_use] pub const fn timestamp_nanos_opt(&self) -> Option { @@ -555,24 +432,7 @@ impl NaiveDateTime { /// /// The return value ranges from 0 to 999, /// or for [leap seconds](./struct.NaiveTime.html#leap-second-handling), to 1,999. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDate; - /// - /// let dt = NaiveDate::from_ymd_opt(2016, 7, 8) - /// .unwrap() - /// .and_hms_nano_opt(9, 10, 11, 123_456_789) - /// .unwrap(); - /// assert_eq!(dt.timestamp_subsec_millis(), 123); - /// - /// let dt = NaiveDate::from_ymd_opt(2015, 7, 1) - /// .unwrap() - /// .and_hms_nano_opt(8, 59, 59, 1_234_567_890) - /// .unwrap(); - /// assert_eq!(dt.timestamp_subsec_millis(), 1_234); - /// ``` + #[deprecated(since = "0.4.34", note = "use `.and_utc().timestamp_subsec_millis()` instead")] #[inline] #[must_use] pub const fn timestamp_subsec_millis(&self) -> u32 { @@ -583,24 +443,7 @@ impl NaiveDateTime { /// /// The return value ranges from 0 to 999,999, /// or for [leap seconds](./struct.NaiveTime.html#leap-second-handling), to 1,999,999. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDate; - /// - /// let dt = NaiveDate::from_ymd_opt(2016, 7, 8) - /// .unwrap() - /// .and_hms_nano_opt(9, 10, 11, 123_456_789) - /// .unwrap(); - /// assert_eq!(dt.timestamp_subsec_micros(), 123_456); - /// - /// let dt = NaiveDate::from_ymd_opt(2015, 7, 1) - /// .unwrap() - /// .and_hms_nano_opt(8, 59, 59, 1_234_567_890) - /// .unwrap(); - /// assert_eq!(dt.timestamp_subsec_micros(), 1_234_567); - /// ``` + #[deprecated(since = "0.4.34", note = "use `.and_utc().timestamp_subsec_micros()` instead")] #[inline] #[must_use] pub const fn timestamp_subsec_micros(&self) -> u32 { @@ -611,26 +454,6 @@ impl NaiveDateTime { /// /// The return value ranges from 0 to 999,999,999, /// or for [leap seconds](./struct.NaiveTime.html#leap-second-handling), to 1,999,999,999. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDate; - /// - /// let dt = NaiveDate::from_ymd_opt(2016, 7, 8) - /// .unwrap() - /// .and_hms_nano_opt(9, 10, 11, 123_456_789) - /// .unwrap(); - /// assert_eq!(dt.timestamp_subsec_nanos(), 123_456_789); - /// - /// let dt = NaiveDate::from_ymd_opt(2015, 7, 1) - /// .unwrap() - /// .and_hms_nano_opt(8, 59, 59, 1_234_567_890) - /// .unwrap(); - /// assert_eq!(dt.timestamp_subsec_nanos(), 1_234_567_890); - /// ``` - #[inline] - #[must_use] pub const fn timestamp_subsec_nanos(&self) -> u32 { self.and_utc().timestamp_subsec_nanos() } From 1159dd90cb0527beeae8be768a75e4005690fa83 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 29 Feb 2024 21:23:57 +0100 Subject: [PATCH 9/9] Add comment to calculate `UNIX_EPOCH_DAY` --- src/datetime/mod.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/datetime/mod.rs b/src/datetime/mod.rs index 5813ae831d..9640b5005b 100644 --- a/src/datetime/mod.rs +++ b/src/datetime/mod.rs @@ -1881,6 +1881,14 @@ where } } +/// Number of days between Januari 1, 1970 and December 31, 1 BCE which we define to be day 0. +/// 4 full leap year cycles until December 31, 1600 4 * 146097 = 584388 +/// 1 day until January 1, 1601 1 +/// 369 years until Januari 1, 1970 369 * 365 = 134685 +/// of which floor(369 / 4) are leap years floor(369 / 4) = 92 +/// except for 1700, 1800 and 1900 -3 + +/// -------- +/// 719163 const UNIX_EPOCH_DAY: i64 = 719_163; #[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))]