From c2c1ca071f242c3e84775bba2a05a6c924f44b1e Mon Sep 17 00:00:00 2001 From: mbartlett21 <29034492+mbartlett21@users.noreply.github.com> Date: Mon, 14 Jun 2021 12:16:13 +0000 Subject: [PATCH] Add functions `Duration::try_from_secs_{f32, f64}` This also adds the error type used, `FromSecsError` and its `impl`s. --- library/core/src/time.rs | 128 +++++++++++++++++++++++++++++++++++++++ library/std/src/error.rs | 3 + library/std/src/lib.rs | 1 + 3 files changed, 132 insertions(+) diff --git a/library/core/src/time.rs b/library/core/src/time.rs index 88b4e2a2436..22e8e143986 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -697,6 +697,44 @@ pub const fn from_secs_f64(secs: f64) -> Duration { } } + /// The checked version of [`from_secs_f64`]. + /// + /// [`from_secs_f64`]: Duration::from_secs_f64 + /// + /// This constructor will return an `Err` if `secs` is not finite, negative or overflows `Duration`. + /// + /// # Examples + /// ``` + /// #![feature(duration_checked_float)] + /// + /// use std::time::Duration; + /// + /// let dur = Duration::try_from_secs_f64(2.7); + /// assert_eq!(dur, Ok(Duration::new(2, 700_000_000))); + /// + /// let negative = Duration::try_from_secs_f64(-5.0); + /// assert!(negative.is_err()); + /// ``` + #[unstable(feature = "duration_checked_float", issue = "83400")] + #[inline] + pub const fn try_from_secs_f64(secs: f64) -> Result { + const MAX_NANOS_F64: f64 = ((u64::MAX as u128 + 1) * (NANOS_PER_SEC as u128)) as f64; + let nanos = secs * (NANOS_PER_SEC as f64); + if !nanos.is_finite() { + Err(FromSecsError { kind: FromSecsErrorKind::NonFinite }) + } else if nanos >= MAX_NANOS_F64 { + Err(FromSecsError { kind: FromSecsErrorKind::Overflow }) + } else if nanos < 0.0 { + Err(FromSecsError { kind: FromSecsErrorKind::Underflow }) + } else { + let nanos = nanos as u128; + Ok(Duration { + secs: (nanos / (NANOS_PER_SEC as u128)) as u64, + nanos: (nanos % (NANOS_PER_SEC as u128)) as u32, + }) + } + } + /// Creates a new `Duration` from the specified number of seconds represented /// as `f32`. /// @@ -732,6 +770,44 @@ pub const fn from_secs_f32(secs: f32) -> Duration { } } + /// The checked version of [`from_secs_f32`]. + /// + /// [`from_secs_f32`]: Duration::from_secs_f32 + /// + /// This constructor will return an `Err` if `secs` is not finite, negative or overflows `Duration`. + /// + /// # Examples + /// ``` + /// #![feature(duration_checked_float)] + /// + /// use std::time::Duration; + /// + /// let dur = Duration::try_from_secs_f32(2.7); + /// assert_eq!(dur, Ok(Duration::new(2, 700_000_000))); + /// + /// let negative = Duration::try_from_secs_f32(-5.0); + /// assert!(negative.is_err()); + /// ``` + #[unstable(feature = "duration_checked_float", issue = "83400")] + #[inline] + pub const fn try_from_secs_f32(secs: f32) -> Result { + const MAX_NANOS_F32: f32 = ((u64::MAX as u128 + 1) * (NANOS_PER_SEC as u128)) as f32; + let nanos = secs * (NANOS_PER_SEC as f32); + if !nanos.is_finite() { + Err(FromSecsError { kind: FromSecsErrorKind::NonFinite }) + } else if nanos >= MAX_NANOS_F32 { + Err(FromSecsError { kind: FromSecsErrorKind::Overflow }) + } else if nanos < 0.0 { + Err(FromSecsError { kind: FromSecsErrorKind::Underflow }) + } else { + let nanos = nanos as u128; + Ok(Duration { + secs: (nanos / (NANOS_PER_SEC as u128)) as u64, + nanos: (nanos % (NANOS_PER_SEC as u128)) as u32, + }) + } + } + /// Multiplies `Duration` by `f64`. /// /// # Panics @@ -1081,3 +1157,55 @@ fn fmt_decimal( } } } + +/// An error which can be returned when converting a floating-point value of seconds +/// into a [`Duration`]. +/// +/// This error is used as the error type for [`Duration::try_from_secs_f32`] and +/// [`Duration::try_from_secs_f64`]. +/// +/// # Example +/// +/// ``` +/// #![feature(duration_checked_float)] +/// +/// use std::time::Duration; +/// +/// if let Err(e) = Duration::try_from_secs_f32(-1.0) { +/// println!("Failed conversion to Duration: {}", e); +/// } +/// ``` +#[derive(Debug, Clone, PartialEq, Eq)] +#[unstable(feature = "duration_checked_float", issue = "83400")] +pub struct FromSecsError { + kind: FromSecsErrorKind, +} + +impl FromSecsError { + const fn description(&self) -> &'static str { + match self.kind { + FromSecsErrorKind::NonFinite => { + "got non-finite value when converting float to duration" + } + FromSecsErrorKind::Overflow => "overflow when converting float to duration", + FromSecsErrorKind::Underflow => "underflow when converting float to duration", + } + } +} + +#[unstable(feature = "duration_checked_float", issue = "83400")] +impl fmt::Display for FromSecsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.description(), f) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +enum FromSecsErrorKind { + // Value is not a finite value (either infinity or NaN). + NonFinite, + // Value is too large to store in a `Duration`. + Overflow, + // Value is less than `0.0`. + Underflow, +} diff --git a/library/std/src/error.rs b/library/std/src/error.rs index ca83c409822..c16660ef92c 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -529,6 +529,9 @@ fn description(&self) -> &str { #[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] impl Error for alloc::collections::TryReserveError {} +#[unstable(feature = "duration_checked_float", issue = "83400")] +impl Error for core::time::FromSecsError {} + // Copied from `any.rs`. impl dyn Error + 'static { /// Returns `true` if the boxed type is the same as `T` diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 5ba13c2f913..a42d88ef714 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -256,6 +256,7 @@ #![feature(doc_masked)] #![feature(doc_spotlight)] #![feature(dropck_eyepatch)] +#![feature(duration_checked_float)] #![feature(duration_constants)] #![feature(duration_zero)] #![feature(exact_size_is_empty)] -- GitLab