Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add timestamp_nanos_opt, deprecate timestamp_nanos #1275

Merged
merged 6 commits into from Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 21 additions & 4 deletions src/datetime/mod.rs
Expand Up @@ -253,6 +253,23 @@ impl<Tz: TimeZone> DateTime<Tz> {
self.datetime.timestamp_micros()
}

/// Returns the number of non-leap-nanoseconds since January 1, 1970 UTC.
///
/// # Panics
///
/// An `i64` with nanosecond precision can span a range of ~584 years. This function panics on
/// an out of range `DateTime`.
///
/// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:44.0 and
/// 2262-04-11T23:47:16.854775804.
#[deprecated(since = "0.4.31", note = "use `timestamp_nanos_opt()` instead")]
#[inline]
#[must_use]
pub fn timestamp_nanos(&self) -> i64 {
self.timestamp_nanos_opt()
.expect("value can not be represented in a timestamp with nanosecond precision.")
}

/// Returns the number of non-leap-nanoseconds since January 1, 1970 UTC.
///
/// # Panics
Expand All @@ -269,15 +286,15 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// use chrono::{Utc, NaiveDate};
///
/// let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_nano_opt(0, 0, 1, 444).unwrap().and_local_timezone(Utc).unwrap();
/// assert_eq!(dt.timestamp_nanos(), 1_000_000_444);
/// assert_eq!(dt.timestamp_nanos_opt(), Some(1_000_000_444));
///
/// let dt = NaiveDate::from_ymd_opt(2001, 9, 9).unwrap().and_hms_nano_opt(1, 46, 40, 555).unwrap().and_local_timezone(Utc).unwrap();
/// assert_eq!(dt.timestamp_nanos(), 1_000_000_000_000_000_555);
/// assert_eq!(dt.timestamp_nanos_opt(), Some(1_000_000_000_000_000_555));
/// ```
#[inline]
#[must_use]
pub fn timestamp_nanos(&self) -> i64 {
self.datetime.timestamp_nanos()
pub fn timestamp_nanos_opt(&self) -> Option<i64> {
self.datetime.timestamp_nanos_opt()
}

/// Returns the number of milliseconds since the last second boundary.
Expand Down
24 changes: 22 additions & 2 deletions src/datetime/serde.rs
Expand Up @@ -157,6 +157,14 @@ pub mod ts_nanoseconds {
///
/// Intended for use with `serde`s `serialize_with` attribute.
///
/// # Errors
///
/// An `i64` with nanosecond precision can span a range of ~584 years. This function returns an
/// error on an out of range `DateTime`.
///
/// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:44.0 and
/// 2262-04-11T23:47:16.854775804.
///
/// # Example:
///
/// ```rust
Expand All @@ -180,7 +188,9 @@ pub mod ts_nanoseconds {
where
S: ser::Serializer,
{
serializer.serialize_i64(dt.timestamp_nanos())
serializer.serialize_i64(dt.timestamp_nanos_opt().ok_or(ser::Error::custom(
"value out of range for a timestamp with nanosecond precision",
))?)
}

/// Deserialize a [`DateTime`] from a nanosecond timestamp
Expand Down Expand Up @@ -286,6 +296,14 @@ pub mod ts_nanoseconds_option {
///
/// Intended for use with `serde`s `serialize_with` attribute.
///
/// # Errors
///
/// An `i64` with nanosecond precision can span a range of ~584 years. This function returns an
/// error on an out of range `DateTime`.
///
/// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:44.0 and
/// 2262-04-11T23:47:16.854775804.
///
/// # Example:
///
/// ```rust
Expand All @@ -310,7 +328,9 @@ pub mod ts_nanoseconds_option {
S: ser::Serializer,
{
match *opt {
Some(ref dt) => serializer.serialize_some(&dt.timestamp_nanos()),
Some(ref dt) => serializer.serialize_some(&dt.timestamp_nanos_opt().ok_or(
ser::Error::custom("value out of range for a timestamp with nanosecond precision"),
)?),
None => serializer.serialize_none(),
}
}
Expand Down
31 changes: 24 additions & 7 deletions src/naive/datetime/mod.rs
Expand Up @@ -460,19 +460,39 @@
///
/// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:44.0 and
/// 2262-04-11T23:47:16.854775804.
#[deprecated(since = "0.4.31", note = "use `timestamp_nanos_opt()` instead")]
#[inline]
#[must_use]
pub fn timestamp_nanos(&self) -> i64 {
self.timestamp_nanos_opt()
.expect("value can not be represented in a timestamp with nanosecond precision.")
}

Check warning on line 469 in src/naive/datetime/mod.rs

View check run for this annotation

Codecov / codecov/patch

src/naive/datetime/mod.rs#L466-L469

Added lines #L466 - L469 were not covered by tests

/// Returns the number of non-leap *nanoseconds* since midnight on January 1, 1970.
///
/// Note that this does *not* account for the timezone!
/// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch.
///
/// # Errors
///
/// An `i64` with nanosecond precision can span a range of ~584 years. This function returns
/// `None` on an out of range `NaiveDateTime`.
///
/// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:44.0 and
/// 2262-04-11T23:47:16.854775804.
///
/// # Example
///
/// ```
/// use chrono::{NaiveDate, NaiveDateTime};
///
/// let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_nano_opt(0, 0, 1, 444).unwrap();
/// assert_eq!(dt.timestamp_nanos(), 1_000_000_444);
/// assert_eq!(dt.timestamp_nanos_opt(), Some(1_000_000_444));
///
/// let dt = NaiveDate::from_ymd_opt(2001, 9, 9).unwrap().and_hms_nano_opt(1, 46, 40, 555).unwrap();
///
/// const A_BILLION: i64 = 1_000_000_000;
/// let nanos = dt.timestamp_nanos();
/// let nanos = dt.timestamp_nanos_opt().unwrap();
/// assert_eq!(nanos, 1_000_000_000_000_000_555);
/// assert_eq!(
/// Some(dt),
Expand All @@ -481,11 +501,8 @@
/// ```
#[inline]
#[must_use]
pub fn timestamp_nanos(&self) -> i64 {
self.timestamp()
.checked_mul(1_000_000_000)
.and_then(|ns| ns.checked_add(i64::from(self.timestamp_subsec_nanos())))
.expect("value can not be represented in a timestamp with nanosecond precision.")
pub fn timestamp_nanos_opt(&self) -> Option<i64> {
self.timestamp().checked_mul(1_000_000_000)?.checked_add(self.time.nanosecond() as i64)
}

/// Returns the number of milliseconds since the last whole non-leap second.
Expand Down
24 changes: 22 additions & 2 deletions src/naive/datetime/serde.rs
Expand Up @@ -91,6 +91,14 @@ pub mod ts_nanoseconds {
///
/// Intended for use with `serde`s `serialize_with` attribute.
///
/// # Errors
///
/// An `i64` with nanosecond precision can span a range of ~584 years. This function returns an
/// error on an out of range `DateTime`.
///
/// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:44.0 and
/// 2262-04-11T23:47:16.854775804.
///
/// # Example:
///
/// ```rust
Expand All @@ -114,7 +122,9 @@ pub mod ts_nanoseconds {
where
S: ser::Serializer,
{
serializer.serialize_i64(dt.timestamp_nanos())
serializer.serialize_i64(dt.timestamp_nanos_opt().ok_or(ser::Error::custom(
"value out of range for a timestamp with nanosecond precision",
))?)
}

/// Deserialize a `NaiveDateTime` from a nanoseconds timestamp
Expand Down Expand Up @@ -218,6 +228,14 @@ pub mod ts_nanoseconds_option {
///
/// Intended for use with `serde`s `serialize_with` attribute.
///
/// # Errors
///
/// An `i64` with nanosecond precision can span a range of ~584 years. This function returns an
/// error on an out of range `DateTime`.
///
/// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:44.0 and
/// 2262-04-11T23:47:16.854775804.
///
/// # Example:
///
/// ```rust
Expand All @@ -242,7 +260,9 @@ pub mod ts_nanoseconds_option {
S: ser::Serializer,
{
match *opt {
Some(ref dt) => serializer.serialize_some(&dt.timestamp_nanos()),
Some(ref dt) => serializer.serialize_some(&dt.timestamp_nanos_opt().ok_or(
ser::Error::custom("value out of range for a timestamp with nanosecond precision"),
)?),
None => serializer.serialize_none(),
}
}
Expand Down
18 changes: 6 additions & 12 deletions src/naive/datetime/tests.rs
Expand Up @@ -386,37 +386,31 @@ fn test_nanosecond_range() {
const A_BILLION: i64 = 1_000_000_000;
let maximum = "2262-04-11T23:47:16.854775804";
let parsed: NaiveDateTime = maximum.parse().unwrap();
let nanos = parsed.timestamp_nanos();
let nanos = parsed.timestamp_nanos_opt().unwrap();
assert_eq!(
parsed,
NaiveDateTime::from_timestamp_opt(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap()
);

let minimum = "1677-09-21T00:12:44.000000000";
let parsed: NaiveDateTime = minimum.parse().unwrap();
let nanos = parsed.timestamp_nanos();
let nanos = parsed.timestamp_nanos_opt().unwrap();
assert_eq!(
parsed,
NaiveDateTime::from_timestamp_opt(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap()
);
}

#[test]
#[should_panic]
fn test_nanosecond_just_beyond_range() {
// Just beyond range
let maximum = "2262-04-11T23:47:16.854775804";
let parsed: NaiveDateTime = maximum.parse().unwrap();
let beyond_max = parsed + OldDuration::milliseconds(300);
let _ = beyond_max.timestamp_nanos();
}
assert!(beyond_max.timestamp_nanos_opt().is_none());

#[test]
#[should_panic]
fn test_nanosecond_far_beyond_range() {
// Far beyond range
let maximum = "2262-04-11T23:47:16.854775804";
let parsed: NaiveDateTime = maximum.parse().unwrap();
let beyond_max = parsed + OldDuration::days(365);
let _ = beyond_max.timestamp_nanos();
assert!(beyond_max.timestamp_nanos_opt().is_none());
}

#[test]
Expand Down
13 changes: 2 additions & 11 deletions src/round.rs
Expand Up @@ -142,9 +142,6 @@ pub trait DurationRound: Sized {
fn duration_trunc(self, duration: Duration) -> Result<Self, Self::Err>;
}

/// The maximum number of seconds a DateTime can be to be represented as nanoseconds
const MAX_SECONDS_TIMESTAMP_FOR_NANOS: i64 = 9_223_372_036;

impl<Tz: TimeZone> DurationRound for DateTime<Tz> {
type Err = RoundingError;

Expand Down Expand Up @@ -181,10 +178,7 @@ where
if span < 0 {
return Err(RoundingError::DurationExceedsLimit);
}
if naive.timestamp().abs() > MAX_SECONDS_TIMESTAMP_FOR_NANOS {
return Err(RoundingError::TimestampExceedsLimit);
}
let stamp = naive.timestamp_nanos();
let stamp = naive.timestamp_nanos_opt().ok_or(RoundingError::TimestampExceedsLimit)?;
if span > stamp.abs() {
return Err(RoundingError::DurationExceedsTimestamp);
}
Expand Down Expand Up @@ -223,10 +217,7 @@ where
if span < 0 {
return Err(RoundingError::DurationExceedsLimit);
}
if naive.timestamp().abs() > MAX_SECONDS_TIMESTAMP_FOR_NANOS {
return Err(RoundingError::TimestampExceedsLimit);
}
let stamp = naive.timestamp_nanos();
let stamp = naive.timestamp_nanos_opt().ok_or(RoundingError::TimestampExceedsLimit)?;
if span > stamp.abs() {
return Err(RoundingError::DurationExceedsTimestamp);
}
Expand Down