Skip to content

Commit

Permalink
Add from_timestamp_nanos (chronotope#1357)
Browse files Browse the repository at this point in the history
* Add from_timestamp_nanos

Add and implement from_timestamp_nanos and add unit test for it

* Fix lint check error

* Add pub(crate) for NANOS_PER_SEC

* Replace number with constant variable
  • Loading branch information
Ali-Mirghasemi authored Nov 24, 2023
1 parent 6f3ab9d commit e1a9494
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 2 deletions.
2 changes: 1 addition & 1 deletion src/duration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const NANOS_PER_MICRO: i32 = 1000;
/// The number of nanoseconds in a millisecond.
const NANOS_PER_MILLI: i32 = 1_000_000;
/// The number of nanoseconds in seconds.
const NANOS_PER_SEC: i32 = 1_000_000_000;
pub(crate) const NANOS_PER_SEC: i32 = 1_000_000_000;
/// The number of microseconds per second.
const MICROS_PER_SEC: i64 = 1_000_000;
/// The number of milliseconds per second.
Expand Down
35 changes: 34 additions & 1 deletion src/naive/datetime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use core::{fmt, str};
#[cfg(feature = "rkyv")]
use rkyv::{Archive, Deserialize, Serialize};

use crate::duration::Duration as OldDuration;
use crate::duration::{Duration as OldDuration, NANOS_PER_SEC};
#[cfg(feature = "alloc")]
use crate::format::DelayedFormat;
use crate::format::{parse, parse_and_remainder, ParseError, ParseResult, Parsed, StrftimeItems};
Expand Down Expand Up @@ -197,6 +197,39 @@ impl NaiveDateTime {
NaiveDateTime::from_timestamp_opt(secs, nsecs)
}

/// Creates a new [NaiveDateTime] from nanoseconds since the UNIX epoch.
///
/// The UNIX epoch starts on midnight, January 1, 1970, UTC.
///
/// # Errors
///
/// 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());
/// ```
#[inline]
#[must_use]
pub const fn from_timestamp_nanos(nanos: i64) -> Option<NaiveDateTime> {
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)
}

/// Makes a new `NaiveDateTime` corresponding to a UTC date and time,
/// from the number of non-leap seconds
/// since the midnight UTC on January 1, 1970 (aka "UNIX timestamp")
Expand Down
51 changes: 51 additions & 0 deletions src/naive/datetime/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,57 @@ fn test_datetime_from_timestamp_micros() {
}
}

#[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);
Expand Down

0 comments on commit e1a9494

Please sign in to comment.