Skip to content

Commit

Permalink
Don't allow leap seconds that are not on a minute boundary
Browse files Browse the repository at this point in the history
in `from_num_seconds_from_midnight_opt`, and `from_timestamp` methods
  • Loading branch information
pitdicker committed Sep 12, 2023
1 parent 0da66de commit 27d90ad
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 15 deletions.
12 changes: 9 additions & 3 deletions src/datetime/mod.rs
Expand Up @@ -579,12 +579,18 @@ impl DateTime<Utc> {
/// 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`].
///
/// The nanosecond part can exceed 1,000,000,000 in order to represent a
/// [leap second](NaiveTime#leap-second-handling), but only when `secs % 60 == 59`.
/// (The true "UNIX timestamp" cannot represent a leap second unambiguously.)
///
/// # Errors
///
/// Returns `None` on out-of-range number of seconds and/or
/// invalid nanosecond, otherwise returns `Some(DateTime {...})`.
///
/// # Example
///
/// ```
Expand Down
15 changes: 8 additions & 7 deletions src/naive/datetime/mod.rs
Expand Up @@ -110,9 +110,9 @@ impl NaiveDateTime {
/// For a non-naive version of this function see
/// [`TimeZone::timestamp`](../offset/trait.TimeZone.html#method.timestamp).
///
/// The nanosecond part can exceed 1,000,000,000 in order to represent the
/// [leap second](./struct.NaiveTime.html#leap-second-handling). (The true "UNIX
/// timestamp" cannot represent a leap second unambiguously.)
/// The nanosecond part can exceed 1,000,000,000 in order to represent a
/// [leap second](NaiveTime#leap-second-handling), but only when `secs % 60 == 59`.
/// (The true "UNIX timestamp" cannot represent a leap second unambiguously.)
///
/// # Panics
///
Expand Down Expand Up @@ -196,8 +196,8 @@ impl NaiveDateTime {
/// since the midnight UTC on January 1, 1970 (aka "UNIX timestamp")
/// and the number of nanoseconds since the last whole non-leap second.
///
/// The nanosecond part can exceed 1,000,000,000
/// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling).
/// The nanosecond part can exceed 1,000,000,000 in order to represent a
/// [leap second](NaiveTime#leap-second-handling), but only when `secs % 60 == 59`.
/// (The true "UNIX timestamp" cannot represent a leap second unambiguously.)
///
/// # Errors
Expand All @@ -216,8 +216,9 @@ impl NaiveDateTime {
///
/// assert!(from_timestamp_opt(0, 0).is_some());
/// assert!(from_timestamp_opt(0, 999_999_999).is_some());
/// assert!(from_timestamp_opt(0, 1_500_000_000).is_some()); // leap second
/// assert!(from_timestamp_opt(0, 2_000_000_000).is_none());
/// assert!(from_timestamp_opt(0, 1_500_000_000).is_none()); // invalid leap second
/// assert!(from_timestamp_opt(59, 1_500_000_000).is_some()); // leap second
/// assert!(from_timestamp_opt(59, 2_000_000_000).is_none());
/// assert!(from_timestamp_opt(i64::MAX, 0).is_none());
/// ```
#[inline]
Expand Down
10 changes: 5 additions & 5 deletions src/naive/time/mod.rs
Expand Up @@ -420,8 +420,8 @@ impl NaiveTime {

/// Makes a new `NaiveTime` from the number of seconds since midnight and nanosecond.
///
/// The nanosecond part can exceed 1,000,000,000
/// in order to represent the [leap second](#leap-second-handling).
/// The nanosecond part is allowed exceed 1,000,000,000 in order to represent a
/// [leap second](#leap-second-handling), but only when `secs % 60 == 59`.
///
/// # Panics
///
Expand All @@ -435,8 +435,8 @@ impl NaiveTime {

/// Makes a new `NaiveTime` from the number of seconds since midnight and nanosecond.
///
/// The nanosecond part can exceed 1,000,000,000
/// in order to represent the [leap second](#leap-second-handling).
/// The nanosecond part is allowed exceed 1,000,000,000 in order to represent a
/// [leap second](#leap-second-handling), but only when `secs % 60 == 59`.
///
/// # Errors
///
Expand All @@ -458,7 +458,7 @@ impl NaiveTime {
#[inline]
#[must_use]
pub const fn from_num_seconds_from_midnight_opt(secs: u32, nano: u32) -> Option<NaiveTime> {
if secs >= 86_400 || nano >= 2_000_000_000 {
if secs >= 86_400 || (nano >= 1_000_000_000 && secs % 60 != 59) || nano >= 2_000_000_000 {
return None;
}
Some(NaiveTime { secs, frac: nano })
Expand Down
12 changes: 12 additions & 0 deletions src/offset/mod.rs
Expand Up @@ -347,6 +347,12 @@ pub trait TimeZone: Sized + Clone {
/// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp")
/// and the number of nanoseconds since the last whole non-leap second.
///
/// The nanosecond part can exceed 1,000,000,000 in order to represent a
/// [leap second](NaiveTime#leap-second-handling), but only when `secs % 60 == 59`.
/// (The true "UNIX timestamp" cannot represent a leap second unambiguously.)
///
/// # Panics
///
/// Panics on the out-of-range number of seconds and/or invalid nanosecond,
/// for a non-panicking version see [`timestamp_opt`](#method.timestamp_opt).
#[deprecated(since = "0.4.23", note = "use `timestamp_opt()` instead")]
Expand All @@ -358,6 +364,12 @@ pub trait TimeZone: Sized + Clone {
/// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp")
/// and the number of nanoseconds since the last whole non-leap second.
///
/// The nanosecond part can exceed 1,000,000,000 in order to represent a
/// [leap second](NaiveTime#leap-second-handling), but only when `secs % 60 == 59`.
/// (The true "UNIX timestamp" cannot represent a leap second unambiguously.)
///
/// # Errors
///
/// Returns `LocalResult::None` on out-of-range number of seconds and/or
/// invalid nanosecond, otherwise always returns `LocalResult::Single`.
///
Expand Down

0 comments on commit 27d90ad

Please sign in to comment.