From 449d9071d87c4978b5f1645c0ac4c19c843dad17 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 | 43 +++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 14 +++++++------- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/datetime/mod.rs b/src/datetime/mod.rs index 2701cd1776..78c3974ccb 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`](DateTime::from_timestamp) 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(dt.timestamp(), dt.timestamp_subsec_nanos()).unwrap(), dt); + /// ``` #[inline] #[must_use] pub fn timestamp(&self) -> i64 { @@ -559,6 +571,37 @@ 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`](DateTime::timestamp) and + /// [`timestamp_subsec_nanos`](DateTime::timestamp_subsec_nanos). + /// + /// 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 a [`TimeZone`] different from [`Utc`], use + /// [`TimeZone::timestamp_opt`] or [`DateTime::with_timezone`]. + /// + /// # Example + /// + /// ``` + /// use chrono::{DateTime, Utc}; + /// + /// let dt: DateTime = 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); + /// ``` + #[inline] + #[must_use] + pub fn from_timestamp(secs: i64, nsecs: u32) -> Option { + NaiveDateTime::from_timestamp_opt(secs, nsecs).as_ref().map(NaiveDateTime::and_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..e4d4479efd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -309,22 +309,22 @@ //! //! ### Conversion from and to EPOCH timestamps //! -//! Use [`Utc.timestamp(seconds, nanoseconds)`](./offset/trait.TimeZone.html#method.timestamp) -//! to construct a [`DateTime`](./struct.DateTime.html) from a UNIX timestamp +//! Use [`DateTime::from_timestamp(seconds, nanoseconds)`](DateTime::from_timestamp) +//! to construct a [`DateTime`] from a UNIX timestamp //! (seconds, nanoseconds that passed since January 1st 1970). //! -//! Use [`DateTime.timestamp`](./struct.DateTime.html#method.timestamp) to get the timestamp (in seconds) -//! from a [`DateTime`](./struct.DateTime.html). Additionally, you can use -//! [`DateTime.timestamp_subsec_nanos`](./struct.DateTime.html#method.timestamp_subsec_nanos) +//! Use [`DateTime.timestamp`](DateTime::timestamp) to get the timestamp (in seconds) +//! from a [`DateTime`]. Additionally, you can use +//! [`DateTime.timestamp_subsec_nanos`](DateTime::timestamp_subsec_nanos) //! to get the number of additional number of nanoseconds. //! #![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, Utc}; //! //! // Construct a datetime from epoch: -//! let dt = Utc.timestamp_opt(1_500_000_000, 0).unwrap(); +//! let dt: DateTime = DateTime::from_timestamp(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: