#![no_std]
#![deny(missing_debug_implementations)]
#![deny(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg(feature = "std")]
extern crate std;
use core::time::Duration;
#[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
pub struct Instant {
nanos: i64,
}
impl Instant {
pub const ZERO: Instant = Instant { nanos: 0 };
pub fn from_nanos(nanos: i64) -> Self {
Self { nanos }
}
pub const fn as_nanos(&self) -> i64 {
self.nanos
}
pub const fn secs(&self) -> i64 {
self.nanos / 1_000_000_000
}
pub const fn subsec_nanos(&self) -> i64 {
self.nanos % 1_000_000_000
}
pub fn duration_since(&self, earlier: Instant) -> Duration {
self.saturating_duration_since(earlier)
}
pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
self.nanos.checked_sub(earlier.nanos).and_then(|nanos| {
if nanos < 0 {
None
} else {
Some(Duration::from_nanos(nanos as u64))
}
})
}
pub fn saturating_duration_since(&self, earlier: Instant) -> Duration {
self.checked_duration_since(earlier)
.unwrap_or(Duration::ZERO)
}
pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
let dur_nanos: i64 = duration.as_nanos().try_into().ok()?;
let nanos = self.nanos.checked_add(dur_nanos)?;
Some(Self { nanos })
}
pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
let dur_nanos: i64 = duration.as_nanos().try_into().ok()?;
let nanos = self.nanos.checked_sub(dur_nanos)?;
Some(Self { nanos })
}
#[cfg(feature = "std")]
pub fn from_std(instant: ::std::time::Instant) -> Self {
let elapsed = instant.elapsed();
Self {
nanos: elapsed
.as_nanos()
.try_into()
.expect("Elapsed time too large to fit into Instant"),
}
}
#[cfg(feature = "std")]
pub fn from_system_elapsed(
sys_time: ::std::time::SystemTime,
) -> Result<Self, std::time::SystemTimeError> {
let dur = sys_time.elapsed()?;
Ok(Self {
nanos: dur
.as_nanos()
.try_into()
.expect("Elapsed time too large to fit into Instant"),
})
}
#[cfg(feature = "std")]
pub fn from_system_unix_epoch(sys_time: ::std::time::SystemTime) -> Self {
let dur = sys_time
.duration_since(::std::time::UNIX_EPOCH)
.expect("start time must not be before the unix epoch");
Self {
nanos: dur
.as_nanos()
.try_into()
.expect("Elapsed time too large to fit into Instant"),
}
}
#[cfg(feature = "std")]
pub fn to_std(&self, base_instant: ::std::time::Instant) -> ::std::time::Instant {
let duration_since = ::core::time::Duration::from_nanos(
self.nanos
.try_into()
.expect("Elapsed time too large to fit into Duration"),
);
base_instant + duration_since
}
#[cfg(feature = "std")]
pub fn to_system(&self, base_system_time: ::std::time::SystemTime) -> ::std::time::SystemTime {
let duration_since = ::core::time::Duration::from_nanos(
self.nanos
.try_into()
.expect("Elapsed time too large to fit into Duration"),
);
base_system_time + duration_since
}
}
impl core::fmt::Display for Instant {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let secs = self.secs();
let nanos = self.subsec_nanos();
if secs != 0 {
write!(f, "{}", secs)?;
if nanos != 0 {
write!(f, ".{:0>9}", nanos.abs())?;
}
write!(f, "s")
} else if nanos != 0 {
if nanos < 0 {
write!(f, "-")?;
}
let millis = nanos.abs() / 1_000_000;
let millis_rem = nanos.abs() % 1_000_000;
write!(f, "{}.{:0>6}ms", millis, millis_rem)
} else {
write!(f, "0s")
}
}
}
impl core::ops::Add<Duration> for Instant {
type Output = Instant;
fn add(self, rhs: Duration) -> Self::Output {
self.checked_add(rhs)
.expect("Duration too large to fit into Instant")
}
}
impl core::ops::AddAssign<Duration> for Instant {
fn add_assign(&mut self, rhs: Duration) {
*self = self
.checked_add(rhs)
.expect("Duration too large to fit into Instant");
}
}
impl core::ops::Sub<Duration> for Instant {
type Output = Instant;
fn sub(self, rhs: Duration) -> Self::Output {
self.checked_sub(rhs)
.expect("Duration too large to fit into Instant")
}
}
impl core::ops::SubAssign<Duration> for Instant {
fn sub_assign(&mut self, rhs: Duration) {
*self = self
.checked_sub(rhs)
.expect("Duration too large to fit into Instant");
}
}
impl core::ops::Sub<Instant> for Instant {
type Output = Duration;
fn sub(self, rhs: Instant) -> Self::Output {
self.saturating_duration_since(rhs)
}
}
#[cfg(test)]
mod tests {
use super::*;
extern crate alloc;
#[test]
fn add() {
let base = Instant::from_nanos(1_222_333_444);
assert_eq!(
base + Duration::from_secs(1),
Instant::from_nanos(2_222_333_444)
);
let mut new = base;
new += Duration::from_secs(1);
assert_eq!(new, Instant::from_nanos(2_222_333_444));
}
#[test]
fn sub_duration() {
let base = Instant::from_nanos(1_222_333_444);
assert_eq!(
base - Duration::from_secs(1),
Instant::from_nanos(222_333_444)
);
let mut new = base;
new -= Duration::from_secs(1);
assert_eq!(new, Instant::from_nanos(222_333_444));
}
#[test]
fn sub_instant() {
let earlier = Instant::from_nanos(1_222_333_444);
let later = Instant::from_nanos(2_333_444_555);
assert_eq!(later - earlier, Duration::from_nanos(1_111_111_111));
assert_eq!(earlier - later, Duration::ZERO);
assert_eq!(earlier - earlier, Duration::ZERO);
}
#[test]
fn display() {
assert_eq!(&alloc::format!("{}", Instant::ZERO), "0s");
assert_eq!(&alloc::format!("{}", Instant::from_nanos(1)), "0.000001ms");
assert_eq!(
&alloc::format!("{}", Instant::from_nanos(-1)),
"-0.000001ms"
);
assert_eq!(
&alloc::format!("{}", Instant::from_nanos(1_000_000_000)),
"1s"
);
assert_eq!(
&alloc::format!("{}", Instant::from_nanos(1_000_000_001)),
"1.000000001s"
);
assert_eq!(
&alloc::format!("{}", Instant::from_nanos(1_100_000_000)),
"1.100000000s"
);
assert_eq!(
&alloc::format!("{}", Instant::from_nanos(-1_000_000_000)),
"-1s"
);
assert_eq!(
&alloc::format!("{}", Instant::from_nanos(-1_000_000_001)),
"-1.000000001s"
);
assert_eq!(
&alloc::format!("{}", Instant::from_nanos(-1_100_000_000)),
"-1.100000000s"
);
}
}