From c9e4a2056601d642f827e7086b16fdf1bf68b150 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_opt` This commit adds the new constructor `from_timestamp_opt` 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 using the `_opt` suffix for fallible functions. As panicking variants on invalid input are deprecated, no panicking variant is provided. 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 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/datetime/mod.rs b/src/datetime/mod.rs index cfa045dcf5..ff3cc6436e 100644 --- a/src/datetime/mod.rs +++ b/src/datetime/mod.rs @@ -202,6 +202,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`](#method.from_timestamp) or [`TimeZone::timestamp`]. + /// + /// ``` + /// 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.subsec_nanos()), dt); + /// ``` #[inline] #[must_use] pub fn timestamp(&self) -> i64 { @@ -552,6 +564,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(dt.timestamp(), dt.timestamp_subsec_nanos()), 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())