From c56b52e7486620f78096db8bf4f9c0184950f524 Mon Sep 17 00:00:00 2001 From: Charles Samborski Date: Sun, 10 Sep 2023 01:47:08 +0200 Subject: [PATCH] Add `DateTime::::from_timestamp` This commit adds the new constructor `from_timestamp` to build a `DateTime` from a UNIX timestamp. Figuring out how to convert a timestamp into a `DateTime` was a common issue: - https://github.com/chronotope/chrono/issues/88 - https://github.com/chronotope/chrono/issues/200 - https://github.com/chronotope/chrono/issues/832 This commit should make `DateTime` creation more discoverable and intuitive. This commit respects the current convention of preferring fallible functions. It avoids however the `_opt` suffix as there is no panicking variant. See [this issue](https://github.com/chronotope/chrono/issues/815) for discussion about error handling and panics. Closes https://github.com/chronotope/chrono/issues/832 --- src/datetime/mod.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 6 +++--- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/datetime/mod.rs b/src/datetime/mod.rs index 2701cd1776..bd5d2067f9 100644 --- a/src/datetime/mod.rs +++ b/src/datetime/mod.rs @@ -209,6 +209,18 @@ impl DateTime { /// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC /// (aka "UNIX timestamp"). + /// + /// The reverse operation of creating a [`DateTime`] from a timestamp can be performed + /// using [`from_timestamp_opt`](#method.from_timestamp_opt) or [`TimeZone::timestamp_opt`]. + /// + /// ``` + /// use chrono::{DateTime, TimeZone, Utc}; + /// + /// let dt: DateTime = Utc.with_ymd_and_hms(2015, 5, 15, 0, 0, 0).unwrap(); + /// assert_eq!(dt.timestamp(), 1431648000); + /// + /// assert_eq!(DateTime::from_timestamp_opt(dt.timestamp(), dt.timestamp_subsec_nanos()).unwrap(), dt); + /// ``` #[inline] #[must_use] pub fn timestamp(&self) -> i64 { @@ -559,6 +571,38 @@ impl DateTime { pub const MAX_UTC: DateTime = DateTime { datetime: NaiveDateTime::MAX, offset: Utc }; } +impl DateTime { + /// 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. + /// + /// This is guaranteed to round-trip with regard to [`timestamp`](#method.timestamp) and + /// [`timestamp_subsec_nanos`](#method.timestamp). + /// + /// Returns `None` on out-of-range number of seconds and/or + /// invalid nanosecond, otherwise returns `Some(DateTime {...})`. + /// + /// If you need to create a `DateTime` with some other [`TimeZone`], use [`TimeZone::timestamp_opt`] + /// or [`DateTime::with_timezone`]. + /// + /// # Example + /// + /// ``` + /// use chrono::{DateTime, Utc}; + /// + /// let dt: DateTime = DateTime::::from_timestamp_opt(1431648000, 0).expect("invalid timestamp"); + /// + /// assert_eq!(dt.to_string(), "2015-05-15 00:00:00 UTC"); + /// assert_eq!(DateTime::from_timestamp_opt(dt.timestamp(), dt.timestamp_subsec_nanos()).unwrap(), dt); + /// ``` + #[inline] + #[must_use] + pub fn from_timestamp_opt(secs: i64, nsecs: u32) -> Option { + NaiveDateTime::from_timestamp_opt(secs, nsecs) + .map(|ndt| DateTime::from_naive_utc_and_offset(ndt, Utc)) + } +} + impl Default for DateTime { fn default() -> Self { Utc.from_utc_datetime(&NaiveDateTime::default()) diff --git a/src/lib.rs b/src/lib.rs index 677fdd189b..68b6188239 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -309,7 +309,7 @@ //! //! ### Conversion from and to EPOCH timestamps //! -//! Use [`Utc.timestamp(seconds, nanoseconds)`](./offset/trait.TimeZone.html#method.timestamp) +//! Use [`DateTime::from_timestamp_opt(seconds, nanoseconds)`](./struct.DateTime.html#method.from_timestamp_opt) //! to construct a [`DateTime`](./struct.DateTime.html) from a UNIX timestamp //! (seconds, nanoseconds that passed since January 1st 1970). //! @@ -321,10 +321,10 @@ #![cfg_attr(not(feature = "std"), doc = "```ignore")] #![cfg_attr(feature = "std", doc = "```rust")] //! // We need the trait in scope to use Utc::timestamp(). -//! use chrono::{DateTime, TimeZone, Utc}; +//! use chrono::{DateTime, FixedOffset, Utc}; //! //! // Construct a datetime from epoch: -//! let dt = Utc.timestamp_opt(1_500_000_000, 0).unwrap(); +//! let dt: DateTime = DateTime::from_timestamp_opt(1_500_000_000, 0).unwrap(); //! assert_eq!(dt.to_rfc2822(), "Fri, 14 Jul 2017 02:40:00 +0000"); //! //! // Get epoch value from a datetime: