From 26652611d5d3db618d01aeeeeacbce33b65b3102 Mon Sep 17 00:00:00 2001 From: Jacob Pratt Date: Tue, 19 Mar 2024 23:59:23 -0400 Subject: [PATCH] Add `ext::InstantExt`, deprecate `time::Instant` --- benchmarks/instant.rs | 2 + tests/derives.rs | 19 ++++++-- tests/instant.rs | 2 + tests/meta.rs | 13 ++++-- time/src/duration.rs | 2 + time/src/ext/instant.rs | 100 ++++++++++++++++++++++++++++++++++++++++ time/src/ext/mod.rs | 4 ++ time/src/instant.rs | 8 ++++ time/src/lib.rs | 1 + 9 files changed, 143 insertions(+), 8 deletions(-) create mode 100644 time/src/ext/instant.rs diff --git a/benchmarks/instant.rs b/benchmarks/instant.rs index 55f31f368b..f05af3487e 100644 --- a/benchmarks/instant.rs +++ b/benchmarks/instant.rs @@ -1,3 +1,5 @@ +#![allow(deprecated)] + use std::time::Instant as StdInstant; use criterion::Bencher; diff --git a/tests/derives.rs b/tests/derives.rs index d788f1cb1a..9392add4e3 100644 --- a/tests/derives.rs +++ b/tests/derives.rs @@ -7,7 +7,9 @@ use time::ext::NumericalDuration; use time::format_description::{self, modifier, well_known, Component, FormatItem, OwnedFormatItem}; use time::macros::{date, offset, time}; use time::parsing::Parsed; -use time::{Duration, Error, Instant, Month, Time, Weekday}; +use time::{Duration, Error, Month, Time, Weekday}; +#[allow(deprecated)] +use time::Instant; use time_macros::datetime; macro_rules! assert_cloned_eq { @@ -27,6 +29,7 @@ fn invalid_format_description() -> error::InvalidFormatDescription { #[allow(clippy::cognitive_complexity)] // all test the same thing #[test] fn clone() { + #[allow(deprecated)] let instant = Instant::now(); assert_cloned_eq!(date!(2021 - 001)); assert_cloned_eq!(time!(0:00)); @@ -96,6 +99,7 @@ fn hash() { datetime!(2021-001 0:00 UTC).hash(&mut hasher); Weekday::Monday.hash(&mut hasher); Month::January.hash(&mut hasher); + #[allow(deprecated)] Instant::now().hash(&mut hasher); Duration::ZERO.hash(&mut hasher); component_range_error().hash(&mut hasher); @@ -103,6 +107,7 @@ fn hash() { #[test] fn partial_ord() { + #[allow(deprecated)] let instant = Instant::now(); assert_eq!(offset!(UTC).partial_cmp(&offset!(+1)), Some(Ordering::Less)); assert_eq!( @@ -129,9 +134,16 @@ fn ord() { #[test] fn debug() { macro_rules! debug_all { - ($($x:expr;)*) => {$( + () => {}; + (#[$meta:meta] $x:expr; $($rest:tt)*) => { + #[$meta] let _unused = format!("{:?}", $x); - )*}; + debug_all!($($rest)*); + }; + ($x:expr; $($rest:tt)*) => { + let _unused = format!("{:?}", $x); + debug_all!($($rest)*); + }; } debug_all! { @@ -140,6 +152,7 @@ fn debug() { ConversionRange; TryFromParsed::InsufficientInformation; Parsed::new(); + #[allow(deprecated)] Instant::now(); error::ParseFromDescription::InvalidComponent("foo"); error::Format::InvalidComponent("foo"); diff --git a/tests/instant.rs b/tests/instant.rs index cc993e3e7f..ec1405191f 100644 --- a/tests/instant.rs +++ b/tests/instant.rs @@ -1,3 +1,5 @@ +#![allow(deprecated)] + use std::cmp::Ordering; use std::thread; use std::time::Instant as StdInstant; diff --git a/tests/meta.rs b/tests/meta.rs index 34045f37c1..9142c83cde 100644 --- a/tests/meta.rs +++ b/tests/meta.rs @@ -17,9 +17,11 @@ use time::format_description::well_known::iso8601; use time::format_description::{modifier, well_known, Component, FormatItem}; use time::formatting::Formattable; use time::parsing::{Parsable, Parsed}; +#[allow(deprecated)] +use time::Instant; use time::{ - error, ext, Date, Duration, Error, Instant, Month, OffsetDateTime, PrimitiveDateTime, Time, - UtcOffset, Weekday, + error, ext, Date, Duration, Error, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, + Weekday, }; #[allow(clippy::cognitive_complexity)] // all test the same thing @@ -183,7 +185,8 @@ assert_obj_safe!(ext::NumericalStdDuration); // `Formattable` is not object safe. macro_rules! assert_impl { - ($($(@$lifetimes:lifetime),+ ;)? $type:ty: $($trait:path),+ $(,)?) => { + ($(#[$meta:meta])* $($(@$lifetimes:lifetime),+ ;)? $type:ty: $($trait:path),+ $(,)?) => { + $(#[$meta])* const _: fn() = || { fn assert_impl_all<$($($lifetimes,)+)? T: ?Sized $(+ $trait)+>() {} assert_impl_all::<$type>(); @@ -287,7 +290,7 @@ assert_impl! { @'a; Duration: Unpin, UnwindSafe, } -assert_impl! { Instant: +assert_impl! { #[allow(deprecated)] Instant: Add, Add, AddAssign, @@ -1040,7 +1043,7 @@ assert_impl! { StdDuration: SubAssign, TryFrom, } -assert_impl! { StdInstant: +assert_impl! { #[allow(deprecated)] StdInstant: Add, AddAssign, Sub, diff --git a/time/src/duration.rs b/time/src/duration.rs index 788f737486..0da8385b3c 100644 --- a/time/src/duration.rs +++ b/time/src/duration.rs @@ -15,6 +15,7 @@ use crate::internal_macros::{ const_try_opt, expect_opt, impl_add_assign, impl_div_assign, impl_mul_assign, impl_sub_assign, }; #[cfg(feature = "std")] +#[allow(deprecated)] use crate::Instant; /// By explicitly inserting this enum where padding is expected, the compiler is able to better @@ -1171,6 +1172,7 @@ impl Duration { since = "0.3.32", note = "extremely limited use case, not intended for benchmarking" )] + #[allow(deprecated)] pub fn time_fn(f: impl FnOnce() -> T) -> (Self, T) { let start = Instant::now(); let return_value = f(); diff --git a/time/src/ext/instant.rs b/time/src/ext/instant.rs new file mode 100644 index 0000000000..dbc6a948f0 --- /dev/null +++ b/time/src/ext/instant.rs @@ -0,0 +1,100 @@ +use std::time::Instant as StdInstant; + +use crate::Duration; + +/// Sealed trait to prevent downstream implementations. +mod sealed { + /// A trait that cannot be implemented by downstream users. + pub trait Sealed: Sized {} + impl Sealed for std::time::Instant {} +} + +/// An extension trait for [`std::time::Instant`] that adds methods for +/// [`time::Duration`](Duration)s. +pub trait InstantExt: sealed::Sealed { + /// # Panics + /// + /// This function may panic if the resulting point in time cannot be represented by the + /// underlying data structure. See [`InstantExt::checked_add_signed`] for a non-panicking + /// version. + fn add_signed(self, duration: Duration) -> Self { + self.checked_add_signed(duration) + .expect("overflow when adding duration to instant") + } + + /// # Panics + /// + /// This function may panic if the resulting point in time cannot be represented by the + /// underlying data structure. See [`InstantExt::checked_sub_signed`] for a non-panicking + /// version. + fn sub_signed(self, duration: Duration) -> Self { + self.checked_sub_signed(duration) + .expect("overflow when subtracting duration from instant") + } + + /// Returns `Some(t)` where `t` is the time `self.checked_add_signed(duration)` if `t` can be + /// represented as `Instant` (which means it's inside the bounds of the underlying data + /// structure), `None` otherwise. + fn checked_add_signed(&self, duration: Duration) -> Option; + + /// Returns `Some(t)` where `t` is the time `self.checked_sub_signed(duration)` if `t` can be + /// represented as `Instant` (which means it's inside the bounds of the underlying data + /// structure), `None` otherwise. + fn checked_sub_signed(&self, duration: Duration) -> Option; + + /// Returns the amount of time elapsed from another instant to this one. This will be negative + /// if `earlier` is later than `self`. + /// + /// # Example + /// + /// ```rust + /// # use std::thread::sleep; + /// # use std::time::{Duration, Instant}; + /// # use time::ext::InstantExt; + /// let now = Instant::now(); + /// sleep(Duration::new(1, 0)); + /// let new_now = Instant::now(); + /// println!("{:?}", new_now.signed_duration_since(now)); // positive + /// println!("{:?}", now.signed_duration_since(new_now)); // negative + /// ``` + fn signed_duration_since(&self, earlier: Self) -> Duration; +} + +impl InstantExt for StdInstant { + fn checked_add_signed(&self, duration: Duration) -> Option { + if duration.is_positive() { + self.checked_add(duration.unsigned_abs()) + } else if duration.is_negative() { + #[allow(clippy::unchecked_duration_subtraction)] + self.checked_sub(duration.unsigned_abs()) + } else { + debug_assert!(duration.is_zero()); + Some(*self) + } + } + + fn checked_sub_signed(&self, duration: Duration) -> Option { + if duration.is_positive() { + #[allow(clippy::unchecked_duration_subtraction)] + self.checked_sub(duration.unsigned_abs()) + } else if duration.is_negative() { + self.checked_add(duration.unsigned_abs()) + } else { + debug_assert!(duration.is_zero()); + Some(*self) + } + } + + fn signed_duration_since(&self, earlier: Self) -> Duration { + if *self > earlier { + self.saturating_duration_since(earlier) + .try_into() + .unwrap_or(Duration::MAX) + } else { + earlier + .saturating_duration_since(*self) + .try_into() + .map_or(Duration::MIN, |d: Duration| -d) + } + } +} diff --git a/time/src/ext/mod.rs b/time/src/ext/mod.rs index 18f089822e..7cc2d0d920 100644 --- a/time/src/ext/mod.rs +++ b/time/src/ext/mod.rs @@ -1,9 +1,13 @@ //! Extension traits. mod digit_count; +#[cfg(feature = "std")] +mod instant; mod numerical_duration; mod numerical_std_duration; pub(crate) use self::digit_count::DigitCount; +#[cfg(feature = "std")] +pub use self::instant::InstantExt; pub use self::numerical_duration::NumericalDuration; pub use self::numerical_std_duration::NumericalStdDuration; diff --git a/time/src/instant.rs b/time/src/instant.rs index 0ebda4c3e4..4c2a16f6ef 100644 --- a/time/src/instant.rs +++ b/time/src/instant.rs @@ -1,5 +1,7 @@ //! The [`Instant`] struct and its associated `impl`s. +#![allow(deprecated)] + use core::borrow::Borrow; use core::cmp::{Ord, Ordering, PartialEq, PartialOrd}; use core::ops::{Add, Sub}; @@ -26,6 +28,7 @@ use crate::Duration; /// /// This implementation allows for operations with signed [`Duration`]s, but is otherwise identical /// to [`std::time::Instant`]. +#[deprecated(since = "0.3.35", note = "import `time::ext::InstantExt` instead")] #[repr(transparent)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Instant(pub StdInstant); @@ -35,6 +38,7 @@ impl Instant { /// Returns an `Instant` corresponding to "now". /// /// ```rust + /// # #![allow(deprecated)] /// # use time::Instant; /// println!("{:?}", Instant::now()); /// ``` @@ -46,6 +50,7 @@ impl Instant { /// be nonnegative if the instant is not synthetically created. /// /// ```rust + /// # #![allow(deprecated)] /// # use time::{Instant, ext::{NumericalStdDuration, NumericalDuration}}; /// # use std::thread; /// let instant = Instant::now(); @@ -63,6 +68,7 @@ impl Instant { /// otherwise. /// /// ```rust + /// # #![allow(deprecated)] /// # use time::{Instant, ext::NumericalDuration}; /// let now = Instant::now(); /// assert_eq!(now.checked_add(5.seconds()), Some(now + 5.seconds())); @@ -84,6 +90,7 @@ impl Instant { /// otherwise. /// /// ```rust + /// # #![allow(deprecated)] /// # use time::{Instant, ext::NumericalDuration}; /// let now = Instant::now(); /// assert_eq!(now.checked_sub(5.seconds()), Some(now - 5.seconds())); @@ -104,6 +111,7 @@ impl Instant { /// Obtain the inner [`std::time::Instant`]. /// /// ```rust + /// # #![allow(deprecated)] /// # use time::Instant; /// let now = Instant::now(); /// assert_eq!(now.into_inner(), now.0); diff --git a/time/src/lib.rs b/time/src/lib.rs index 5a9e08046c..e2ef68b928 100644 --- a/time/src/lib.rs +++ b/time/src/lib.rs @@ -116,6 +116,7 @@ pub use crate::date::Date; pub use crate::duration::Duration; pub use crate::error::Error; #[cfg(feature = "std")] +#[allow(deprecated)] pub use crate::instant::Instant; pub use crate::month::Month; pub use crate::offset_date_time::OffsetDateTime;