Skip to content

Commit

Permalink
Add ext::InstantExt, deprecate time::Instant
Browse files Browse the repository at this point in the history
  • Loading branch information
jhpratt committed Mar 20, 2024
1 parent e96bc85 commit 2665261
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 8 deletions.
2 changes: 2 additions & 0 deletions benchmarks/instant.rs
@@ -1,3 +1,5 @@
#![allow(deprecated)]

use std::time::Instant as StdInstant;

use criterion::Bencher;
Expand Down
19 changes: 16 additions & 3 deletions tests/derives.rs
Expand Up @@ -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 {
Expand All @@ -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));
Expand Down Expand Up @@ -96,13 +99,15 @@ 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);
}

#[test]
fn partial_ord() {
#[allow(deprecated)]
let instant = Instant::now();
assert_eq!(offset!(UTC).partial_cmp(&offset!(+1)), Some(Ordering::Less));
assert_eq!(
Expand All @@ -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! {
Expand All @@ -140,6 +152,7 @@ fn debug() {
ConversionRange;
TryFromParsed::InsufficientInformation;
Parsed::new();
#[allow(deprecated)]
Instant::now();
error::ParseFromDescription::InvalidComponent("foo");
error::Format::InvalidComponent("foo");
Expand Down
2 changes: 2 additions & 0 deletions tests/instant.rs
@@ -1,3 +1,5 @@
#![allow(deprecated)]

use std::cmp::Ordering;
use std::thread;
use std::time::Instant as StdInstant;
Expand Down
13 changes: 8 additions & 5 deletions tests/meta.rs
Expand Up @@ -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
Expand Down Expand Up @@ -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>();
Expand Down Expand Up @@ -287,7 +290,7 @@ assert_impl! { @'a; Duration:
Unpin,
UnwindSafe,
}
assert_impl! { Instant:
assert_impl! { #[allow(deprecated)] Instant:
Add<Duration, Output = Instant>,
Add<StdDuration, Output = Instant>,
AddAssign<Duration>,
Expand Down Expand Up @@ -1040,7 +1043,7 @@ assert_impl! { StdDuration:
SubAssign<Duration>,
TryFrom<Duration>,
}
assert_impl! { StdInstant:
assert_impl! { #[allow(deprecated)] StdInstant:
Add<Duration, Output = StdInstant>,
AddAssign<Duration>,
Sub<Duration, Output = StdInstant>,
Expand Down
2 changes: 2 additions & 0 deletions time/src/duration.rs
Expand Up @@ -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
Expand Down Expand Up @@ -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<T>(f: impl FnOnce() -> T) -> (Self, T) {
let start = Instant::now();
let return_value = f();
Expand Down
100 changes: 100 additions & 0 deletions 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<Self>;

/// 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<Self>;

/// 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<Self> {
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<Self> {
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)
}
}
}
4 changes: 4 additions & 0 deletions 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;
8 changes: 8 additions & 0 deletions 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};
Expand All @@ -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);
Expand All @@ -35,6 +38,7 @@ impl Instant {
/// Returns an `Instant` corresponding to "now".
///
/// ```rust
/// # #![allow(deprecated)]
/// # use time::Instant;
/// println!("{:?}", Instant::now());
/// ```
Expand All @@ -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();
Expand All @@ -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()));
Expand All @@ -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()));
Expand All @@ -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);
Expand Down
1 change: 1 addition & 0 deletions time/src/lib.rs
Expand Up @@ -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;
Expand Down

0 comments on commit 2665261

Please sign in to comment.