diff --git a/src/datetime/mod.rs b/src/datetime/mod.rs index ac9ef511d0..f99f957476 100644 --- a/src/datetime/mod.rs +++ b/src/datetime/mod.rs @@ -624,6 +624,62 @@ impl DateTime { NaiveDateTime::from_timestamp_opt(secs, nsecs).as_ref().map(NaiveDateTime::and_utc) } + /// Makes a new [`DateTime`] from the number of non-leap microseconds + /// since January 1, 1970 0:00:00.000000 UTC (aka "UNIX timestamp"). + /// + /// This is guaranteed to round-trip with regard to [`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` on out-of-range number of microseconds, otherwise returns `Some(DateTime {...})`. + /// + /// # Example + /// + /// ``` + /// use chrono::{DateTime, Utc}; + /// + /// let dt: DateTime = DateTime::::from_timestamp_micros(947638923000004).expect("invalid timestamp"); + /// + /// assert_eq!(dt.to_string(), "2000-01-12 01:02:03.000004 UTC"); + /// assert_eq!(DateTime::from_timestamp_micros(dt.timestamp_micros()).unwrap(), dt); + /// ``` + #[inline] + #[must_use] + pub fn from_timestamp_micros(micros: i64) -> Option { + NaiveDateTime::from_timestamp_micros(micros).as_ref().map(NaiveDateTime::and_utc) + } + + /// 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). + /// + /// If you need to create a `DateTime` with a [`TimeZone`] different from [`Utc`], use + /// [`TimeZone::timestamp_millis_opt`] or [`DateTime::with_timezone`]. + /// + /// # Errors + /// + /// Returns `None` on out-of-range number of milliseconds, otherwise returns `Some(DateTime {...})`. + /// + /// # Example + /// + /// ``` + /// use chrono::{DateTime, Utc}; + /// + /// let dt: DateTime = 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); + /// ``` + #[inline] + #[must_use] + pub fn from_timestamp_millis(millis: i64) -> Option { + NaiveDateTime::from_timestamp_millis(millis).as_ref().map(NaiveDateTime::and_utc) + } + // FIXME: remove when our MSRV is 1.61+ // This method is used by `NaiveDateTime::and_utc` because `DateTime::from_naive_utc_and_offset` // can't be made const yet. diff --git a/src/datetime/tests.rs b/src/datetime/tests.rs index 3df11e78d4..4b1a8a9035 100644 --- a/src/datetime/tests.rs +++ b/src/datetime/tests.rs @@ -1253,6 +1253,30 @@ fn test_datetime_from_local() { assert_eq!(datetime_west, datetime_utc.with_timezone(&timezone_west)); } +#[test] +fn test_datetime_from_timestamp_micros() { + // 2000-01-12 01:02:03.000004Z + let naive_dt = + NaiveDate::from_ymd_opt(2000, 1, 12).unwrap().and_hms_micro_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_micros(datetime_utc.timestamp_micros()).unwrap() + ); +} + +#[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_years_elapsed() {