#[cfg(feature = "chrono")]
use core::convert::TryFrom;
use core::{convert::TryInto, fmt, ops};
use crate::{
time::Duration,
utils::{Init, ZeroInit},
};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct Time {
micros: u64,
}
impl Init for Time {
const INIT: Self = Self::ZERO;
}
unsafe impl ZeroInit for Time {}
impl Default for Time {
fn default() -> Self {
Self::INIT
}
}
impl Time {
pub const ZERO: Self = Time { micros: 0 };
pub const MAX: Self = Time { micros: u64::MAX };
#[inline]
pub const fn from_micros(micros: u64) -> Self {
Self { micros }
}
#[inline]
pub const fn from_millis(millis: u64) -> Self {
Self::from_micros(if let Some(x) = millis.checked_mul(1_000) {
x
} else {
panic!("duration overflow");
})
}
#[inline]
pub const fn from_secs(secs: u64) -> Self {
Self::from_micros(if let Some(x) = secs.checked_mul(1_000_000) {
x
} else {
panic!("duration overflow");
})
}
#[inline]
pub const fn as_micros(self) -> u64 {
self.micros
}
#[inline]
pub const fn as_millis(self) -> u64 {
self.micros / 1_000
}
#[inline]
pub const fn as_secs(self) -> u64 {
self.micros / 1_000_000
}
#[inline]
pub const fn as_secs_f64(self) -> f64 {
self.micros as f64 / 1_000_000.0
}
#[inline]
pub const fn as_secs_f32(self) -> f32 {
(self.micros / 1_000_000) as f32 + (self.micros % 1_000_000) as f32 / 1_000_000.0
}
#[inline]
pub const fn core_duration_since_origin(self) -> core::time::Duration {
core::time::Duration::from_micros(self.micros)
}
#[inline]
pub const fn core_duration_since(self, reference: Self) -> Option<core::time::Duration> {
if self.micros >= reference.micros {
Some(core::time::Duration::from_micros(self.micros))
} else {
None
}
}
#[inline]
pub fn duration_since(self, reference: Self) -> Option<Duration> {
Some(Duration::from_micros(
(self.micros as i128 - reference.micros as i128)
.try_into()
.ok()?,
))
}
#[inline]
pub const fn wrapping_add(&self, duration: Duration) -> Self {
Self::from_micros(self.micros.wrapping_add(duration.as_micros() as i64 as u64))
}
#[inline]
pub const fn wrapping_sub(&self, duration: Duration) -> Self {
Self::from_micros(self.micros.wrapping_sub(duration.as_micros() as i64 as u64))
}
}
impl fmt::Debug for Time {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.core_duration_since_origin().fmt(f)
}
}
impl ops::Add<Duration> for Time {
type Output = Self;
#[inline]
fn add(self, rhs: Duration) -> Self::Output {
self.wrapping_add(rhs)
}
}
impl ops::AddAssign<Duration> for Time {
#[inline]
fn add_assign(&mut self, rhs: Duration) {
*self = *self + rhs;
}
}
impl ops::Sub<Duration> for Time {
type Output = Self;
#[inline]
fn sub(self, rhs: Duration) -> Self::Output {
self.wrapping_sub(rhs)
}
}
impl ops::SubAssign<Duration> for Time {
#[inline]
fn sub_assign(&mut self, rhs: Duration) {
*self = *self - rhs;
}
}
#[cfg(feature = "chrono")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TryFromDateTimeError(());
#[cfg(feature = "chrono")]
impl TryFrom<chrono::DateTime<chrono::Utc>> for Time {
type Error = TryFromDateTimeError;
fn try_from(value: chrono::DateTime<chrono::Utc>) -> Result<Self, Self::Error> {
let secs: u64 = value
.timestamp()
.try_into()
.map_err(|_| TryFromDateTimeError(()))?;
let micros: u64 = value.timestamp_subsec_micros().into();
Ok(Self::from_micros(
secs.checked_mul(1_000_000)
.and_then(|x| x.checked_add(micros))
.ok_or(TryFromDateTimeError(()))?,
))
}
}
#[cfg(feature = "chrono")]
impl TryFrom<Time> for chrono::DateTime<chrono::Utc> {
type Error = TryFromDateTimeError;
fn try_from(value: Time) -> Result<Self, Self::Error> {
use chrono::TimeZone;
chrono::Utc
.timestamp_opt(
(value.micros / 1_000_000) as i64,
(value.micros % 1_000_000) as u32 * 1_000,
)
.single()
.ok_or(TryFromDateTimeError(()))
}
}