/*! Time structures. The `time` module contains structures used to represent both absolute and relative time. - [Instant] is used to represent absolute time. - [Duration] is used to represent relative time. [Instant]: struct.Instant.html [Duration]: struct.Duration.html */ use core::{cmp, fmt, ops}; pub use core::time::Duration; /// A representation of an absolute time value. /// /// The `Instant` type is a wrapper around a `i64` value that represents a number of milliseconds, /// monotonically increasing since an arbitrary moment in time, such as system startup. Note that /// not all `Instant` need to reference the same epoch. /// /// * A value of `0` is inherently arbitrary. /// * A value less than `0` indicates a time before the starting point. /// /// Instants from different sources should not be mixed as their reference points may be different /// and thus yield surprising durations. They may not even advance at the same rate or be steady at /// all. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Instant { /// Milliseconds since the epoch. pub millis: i64, } /// An expiration time, inversion of `Option`. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Expiration { /// Some finite point in time. When(Instant), /// An expiration that will never happen, or is infinitely far away. /// /// This variant always compares greater or equal to all other possible values of `Expiration`. Never, } use Expiration::{When, Never}; impl Instant { /// Create a new `Instant` from a number of milliseconds. pub fn from_millis>(millis: T) -> Instant { Instant { millis: millis.into() } } /// Create a new `Instant` from a number of seconds. pub fn from_secs>(secs: T) -> Instant { Instant { millis: secs.into() * 1000 } } /// Create a new `Instant` from the current [std::time::SystemTime]. /// /// See [std::time::SystemTime::now] /// /// [std::time::SystemTime]: https://doc.rust-lang.org/std/time/struct.SystemTime.html /// [std::time::SystemTime::now]: https://doc.rust-lang.org/std/time/struct.SystemTime.html#method.now #[cfg(feature = "std")] pub fn now() -> Instant { Self::from(::std::time::SystemTime::now()) } /// The fractional number of milliseconds that have passed the epoch. pub fn millis(&self) -> i64 { self.millis % 1000 } /// The number of whole seconds that have passed since the epoch. pub fn secs(&self) -> i64 { self.millis / 1000 } /// The total number of milliseconds that have passed since the epoch. pub fn total_millis(&self) -> i64 { self.millis } } #[cfg(feature = "std")] /// Convert from a standard time. /// /// Note that this conversion is not completely trivial. Although there is no defined epoch, all /// timestamps converted in this manner will be based on the same reference time stamp. The standard /// library further guarantees that the timestamps are in fact monotonically increasing. This comes /// at a performance price of one `Once` (in addition to the possible `Mutex` in the standard /// library in the `Instant::now()` call). impl From<::std::time::Instant> for Instant { fn from(other: ::std::time::Instant) -> Instant { use ::std::sync::Once; use ::core::mem::MaybeUninit; static mut FIRST: MaybeUninit<::std::time::Instant> = MaybeUninit::uninit(); static ONCE: Once = Once::new(); ONCE.call_once(|| { unsafe { // SAFETY: by the Once, we are the first thread accessing this variable. That is, // no other thread is already trying to read it and all other threads are blocked // and will never attempt to write to it. FIRST.as_mut_ptr().write(::std::time::Instant::now()); } }); // SAFETY: this was initialized before by one `call_once`. let relative = unsafe { FIRST.assume_init() }; let elapsed = other - relative; Instant::from_millis((elapsed.as_secs() * 1_000) as i64 + (elapsed.subsec_nanos() / 1_000_000) as i64) } } #[cfg(feature = "std")] impl From<::std::time::SystemTime> for Instant { fn from(other: ::std::time::SystemTime) -> Instant { let n = other.duration_since(::std::time::UNIX_EPOCH) .expect("start time must not be before the unix epoch"); Self::from_millis(n.as_secs() as i64 * 1000 + (n.subsec_nanos() / 1000000) as i64) } } #[cfg(feature = "std")] impl Into<::std::time::SystemTime> for Instant { fn into(self) -> ::std::time::SystemTime { ::std::time::UNIX_EPOCH + ::std::time::Duration::from_millis(self.millis as u64) } } impl fmt::Display for Instant { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}.{}s", self.secs(), self.millis()) } } impl ops::Add for Instant { type Output = Instant; fn add(self, rhs: Duration) -> Instant { Instant::from_millis(self.millis + rhs.as_millis() as i64) } } impl ops::AddAssign for Instant { fn add_assign(&mut self, rhs: Duration) { self.millis += rhs.as_millis() as i64; } } impl ops::Sub for Instant { type Output = Instant; fn sub(self, rhs: Duration) -> Instant { Instant::from_millis(self.millis - rhs.as_millis() as i64) } } impl ops::SubAssign for Instant { fn sub_assign(&mut self, rhs: Duration) { self.millis -= rhs.as_millis() as i64; } } impl ops::Sub for Instant { type Output = Duration; fn sub(self, rhs: Instant) -> Duration { Duration::from_millis((self.millis - rhs.millis).abs() as u64) } } impl Default for Expiration { fn default() -> Self { Expiration::Never } } impl From> for Expiration { fn from(opt: Option) -> Self { match opt { Some(instant) => When(instant), None => Never, } } } impl From for Option { fn from(opt: Expiration) -> Self { match opt { When(instant) => Some(instant), Never => None, } } } impl cmp::PartialOrd for Expiration { fn partial_cmp(&self, other: &Self) -> Option { match (*self, *other) { (Never, Never) => Some(cmp::Ordering::Equal), (Never, When(_)) => Some(cmp::Ordering::Greater), (When(_), Never) => Some(cmp::Ordering::Less), (When(ref a), When(ref b)) => a.partial_cmp(b), } } } impl cmp::Ord for Expiration { fn cmp(&self, other: &Self) -> cmp::Ordering { match (*self, *other) { (Never, Never) => cmp::Ordering::Equal, (Never, When(_)) => cmp::Ordering::Greater, (When(_), Never) => cmp::Ordering::Less, (When(ref a), When(ref b)) => a.cmp(b), } } } #[cfg(test)] mod test { use super::*; #[test] fn test_instant_ops() { // std::ops::Add assert_eq!(Instant::from_millis(4) + Duration::from_millis(6), Instant::from_millis(10)); // std::ops::Sub assert_eq!(Instant::from_millis(7) - Duration::from_millis(5), Instant::from_millis(2)); } #[test] fn test_instant_getters() { let instant = Instant::from_millis(5674); assert_eq!(instant.secs(), 5); assert_eq!(instant.millis(), 674); assert_eq!(instant.total_millis(), 5674); } #[test] fn test_instant_display() { assert_eq!(format!("{}", Instant::from_millis(5674)), "5.674s"); assert_eq!(format!("{}", Instant::from_millis(5000)), "5.0s"); } #[test] #[cfg(feature = "std")] fn test_instant_conversions() { let mut epoc: ::std::time::SystemTime = Instant::from_millis(0).into(); assert_eq!(Instant::from(::std::time::UNIX_EPOCH), Instant::from_millis(0)); assert_eq!(epoc, ::std::time::UNIX_EPOCH); epoc = Instant::from_millis(2085955200i64 * 1000).into(); assert_eq!(epoc, ::std::time::UNIX_EPOCH + ::std::time::Duration::from_secs(2085955200)); } #[test] fn test_duration_ops() { // std::ops::Add assert_eq!(Duration::from_millis(40) + Duration::from_millis(2), Duration::from_millis(42)); // std::ops::Sub assert_eq!(Duration::from_millis(555) - Duration::from_millis(42), Duration::from_millis(513)); // std::ops::Mul assert_eq!(Duration::from_millis(13) * 22, Duration::from_millis(286)); // std::ops::Div assert_eq!((Duration::from_millis(53) / 4).as_millis(), 13); } #[test] fn test_duration_assign_ops() { let mut duration = Duration::from_millis(4735); duration += Duration::from_millis(1733); assert_eq!(duration, Duration::from_millis(6468)); duration -= Duration::from_millis(1234); assert_eq!(duration, Duration::from_millis(5234)); duration *= 4; assert_eq!(duration, Duration::from_millis(20936)); duration /= 5; assert_eq!(duration.as_millis(), 4187); } #[test] #[should_panic(expected = "overflow when subtracting durations")] fn test_sub_from_zero_overflow() { let _ = Duration::from_millis(0) - Duration::from_millis(1); } #[test] #[should_panic(expected = "divide by zero")] fn test_div_by_zero() { let _ = Duration::from_millis(4) / 0; } #[test] fn test_duration_getters() { let instant = Duration::from_millis(4934); assert_eq!(instant.as_secs(), 4); assert_eq!(instant.subsec_millis(), 934); assert_eq!(instant.as_millis(), 4934); } #[test] fn test_duration_conversions() { let mut std_duration = ::core::time::Duration::from_millis(4934); let duration: Duration = std_duration.into(); assert_eq!(duration, Duration::from_millis(4934)); assert_eq!(Duration::from(std_duration), Duration::from_millis(4934)); std_duration = duration.into(); assert_eq!(std_duration, ::core::time::Duration::from_millis(4934)); } }