about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--library/core/src/time.rs128
-rw-r--r--library/std/src/error.rs3
-rw-r--r--library/std/src/lib.rs1
3 files changed, 132 insertions, 0 deletions
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 @@ impl 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<Duration, FromSecsError> {
+        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 @@ impl 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<Duration, FromSecsError> {
+        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 @@ impl fmt::Debug for Duration {
         }
     }
 }
+
+/// 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 @@ impl Error for char::ParseCharError {
 #[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)]