use core::{
cmp::Ordering,
ops::{Add, Sub},
time::Duration,
};
use crate::time::{TimeError, TimePoint};
#[cfg(feature = "std")]
use std::time::{SystemTime, UNIX_EPOCH};
pub mod error;
#[allow(deprecated)]
pub use error::TimestampError;
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
pub struct Timestamp {
pub t: u128,
}
impl Timestamp {
#[cfg(feature = "std")]
#[must_use = "this returns the result of the operation"]
pub fn now() -> Self {
let duration_since_epoch = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards");
Timestamp {
t: duration_since_epoch.as_nanos(),
}
}
#[must_use = "this returns the result of the operation"]
pub fn zero() -> Self {
Timestamp { t: 0 }
}
pub fn as_seconds(&self) -> Result<f64, TimeError> {
const NANOSECONDS_PER_SECOND: f64 = 1_000_000_000.0;
#[allow(clippy::cast_precision_loss)]
let seconds = self.t as f64 / NANOSECONDS_PER_SECOND;
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
if (seconds * NANOSECONDS_PER_SECOND) as u128 == self.t {
Ok(seconds)
} else {
Err(TimeError::AccuracyLoss)
}
}
#[must_use = "this returns the result of the operation"]
#[allow(clippy::cast_precision_loss)]
pub fn as_seconds_unchecked(&self) -> f64 {
const NANOSECONDS_PER_SECOND: f64 = 1_000_000_000.0;
self.t as f64 / NANOSECONDS_PER_SECOND
}
}
impl Sub<Timestamp> for Timestamp {
type Output = Result<Duration, TimeError>;
fn sub(
self,
other: Timestamp,
) -> Self::Output {
match self.t.cmp(&other.t) {
Ordering::Less => Err(TimeError::DurationUnderflow),
Ordering::Equal => Ok(Duration::from_secs(0)),
Ordering::Greater => {
let diff = self.t - other.t;
let seconds = diff / 1_000_000_000;
let nanos = (diff % 1_000_000_000) as u32;
if seconds > u128::from(u64::MAX) {
return Err(TimeError::DurationOverflow);
}
#[allow(clippy::cast_possible_truncation)]
Ok(Duration::new(seconds as u64, nanos))
}
}
}
}
impl Add<Duration> for Timestamp {
type Output = Result<Timestamp, TimeError>;
fn add(
self,
rhs: Duration,
) -> Self::Output {
(u128::from(rhs.as_secs()))
.checked_mul(1_000_000_000)
.and_then(|seconds| seconds.checked_add(u128::from(rhs.subsec_nanos())))
.and_then(|total_duration_nanos| self.t.checked_add(total_duration_nanos))
.map(|final_nanos| Timestamp { t: final_nanos })
.ok_or(TimeError::DurationOverflow)
}
}
impl Sub<Duration> for Timestamp {
type Output = Result<Timestamp, TimeError>;
fn sub(
self,
rhs: Duration,
) -> Self::Output {
u128::from(rhs.as_secs())
.checked_mul(1_000_000_000)
.and_then(|seconds| seconds.checked_add(u128::from(rhs.subsec_nanos())))
.and_then(|total_duration_nanos| self.t.checked_sub(total_duration_nanos))
.map(|final_nanos| Timestamp { t: final_nanos })
.ok_or(TimeError::DurationUnderflow)
}
}
impl TimePoint for Timestamp {
fn static_timestamp() -> Self {
Timestamp::zero()
}
fn duration_since(
self,
earlier: Self,
) -> Result<Duration, TimeError> {
self - earlier
}
fn checked_add(
self,
rhs: Duration,
) -> Result<Self, TimeError> {
self + rhs
}
fn checked_sub(
self,
rhs: Duration,
) -> Result<Self, TimeError> {
self - rhs
}
fn as_seconds(self) -> Result<f64, TimeError> {
Timestamp::as_seconds(&self)
}
}
#[cfg(test)]
mod tests;