use core::cmp::Ordering;
use core::marker::PhantomData;
use core::ops::{Add, AddAssign, Sub, SubAssign};
use irox_units::units::duration::Duration;
use crate::gregorian::Date;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Epoch(pub Date);
impl Epoch {
pub fn get_gregorian_date(&self) -> Date {
self.0
}
}
#[derive(Debug, Copy, Clone)]
pub struct Timestamp<T> {
epoch: Epoch,
offset: Duration,
_phantom: PhantomData<T>,
}
impl<T> Timestamp<T> {
pub(crate) fn new(epoch: Epoch, duration: Duration) -> Self {
Self {
epoch,
offset: duration,
_phantom: PhantomData,
}
}
#[must_use]
pub fn get_epoch(&self) -> Epoch {
self.epoch
}
#[must_use]
pub fn get_offset(&self) -> Duration {
self.offset
}
}
impl<T> PartialEq for Timestamp<T> {
fn eq(&self, other: &Self) -> bool {
if self.epoch != other.epoch {
return false;
}
self.offset.eq(&other.offset)
}
}
impl<T> Eq for Timestamp<T> {}
impl<T> PartialOrd for Timestamp<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.offset.cmp(&other.offset))
}
}
impl<T> Ord for Timestamp<T> {
fn cmp(&self, other: &Self) -> Ordering {
self.offset.cmp(&other.offset)
}
}
pub const UNIX_EPOCH: Epoch = Epoch(Date {
year: 1970,
day_of_year: 0,
});
pub type UnixTimestamp = Timestamp<UnixEpoch>;
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
pub struct UnixEpoch;
pub trait FromTimestamp<T> {
fn from_timestamp(other: &Timestamp<T>) -> Self;
}
macro_rules! derive_timestamp_impl {
($epoch:ident,$name:ident) => {
impl $name {
pub const fn from_offset(offset: Duration) -> $name {
$name {
epoch: $epoch,
offset,
_phantom: PhantomData {},
}
}
pub const fn from_seconds(seconds: u32) -> $name {
$name::from_seconds_f64(seconds as f64)
}
pub const fn from_seconds_f64(seconds: f64) -> $name {
$name::from_offset(Duration::new_seconds(seconds))
}
}
impl Default for $name {
fn default() -> Self {
$name {
epoch: $epoch,
offset: Duration::default(),
_phantom: Default::default(),
}
}
}
impl From<Duration> for $name {
fn from(value: Duration) -> Self {
$name::from_offset(value)
}
}
impl<T> FromTimestamp<T> for $name {
fn from_timestamp(other: &Timestamp<T>) -> Self {
let epoch_offset = $epoch.0 - other.epoch.0;
let new_duration = other.offset - epoch_offset;
$name::from_offset(new_duration)
}
}
};
}
impl<T> AddAssign<Duration> for Timestamp<T> {
fn add_assign(&mut self, rhs: Duration) {
self.offset += rhs;
}
}
impl<T> SubAssign<Duration> for Timestamp<T> {
fn sub_assign(&mut self, rhs: Duration) {
self.offset -= rhs;
}
}
impl<T> AddAssign<&Duration> for Timestamp<T> {
fn add_assign(&mut self, rhs: &Duration) {
self.offset += *rhs;
}
}
impl<T> SubAssign<&Duration> for Timestamp<T> {
fn sub_assign(&mut self, rhs: &Duration) {
self.offset -= *rhs;
}
}
impl<T> SubAssign<&mut Duration> for Timestamp<T> {
fn sub_assign(&mut self, rhs: &mut Duration) {
self.offset -= *rhs;
}
}
macro_rules! impl_sub_timestamp {
($($sub:ty)+, $($slf:ty)+) => {
impl<T> Sub<$($sub)+> for $($slf)+ {
type Output = Duration;
fn sub(self, rhs: $($sub)+) -> Self::Output {
self.offset - rhs.offset
}
}
};
}
macro_rules! impl_sub_duration {
($($sub:ty)+, $($slf:ty)+) => {
impl<T> Sub<$($sub)+> for $($slf)+ {
type Output = Timestamp<T>;
fn sub(self, rhs: $($sub)+) -> Self::Output {
let offset = self.offset - rhs;
Timestamp::new(self.epoch, offset)
}
}
};
}
macro_rules! impl_add_timestamp {
($($sub:ty)+, $($slf:ty)+) => {
impl<T> Add<$($sub)+> for $($slf)+ {
type Output = Timestamp<T>;
fn add(self, rhs: $($sub)+) -> Self::Output {
let offset = self.offset + rhs;
Timestamp::new(self.epoch, offset)
}
}
};
}
macro_rules! impl_op {
($op:ident, $($operand:ty)+) => {
$op!($($operand)+, Timestamp<T>);
$op!($($operand)+, &Timestamp<T>);
$op!($($operand)+, &mut Timestamp<T>);
$op!(&$($operand)+, Timestamp<T>);
$op!(&$($operand)+, &Timestamp<T>);
$op!(&$($operand)+, &mut Timestamp<T>);
$op!(&mut $($operand)+, Timestamp<T>);
$op!(&mut $($operand)+, &Timestamp<T>);
$op!(&mut $($operand)+, &mut Timestamp<T>);
};
}
impl_op!(impl_sub_timestamp, Timestamp<T>);
impl_op!(impl_add_timestamp, Duration);
impl_op!(impl_sub_duration, Duration);
impl UnixTimestamp {
#[must_use]
#[cfg(feature = "std")]
pub fn now() -> UnixTimestamp {
match std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH) {
Ok(t) => UnixTimestamp::from_offset(t.into()),
Err(t) => {
UnixTimestamp::from_offset(Duration::new_seconds(-1.0 * t.duration().as_secs_f64()))
}
}
}
#[must_use]
#[cfg(feature = "std")]
pub fn elapsed(&self) -> Duration {
Self::now().offset - self.offset
}
#[must_use]
pub fn as_date(&self) -> Date {
self.into()
}
}
derive_timestamp_impl!(UNIX_EPOCH, UnixTimestamp);
pub const GPS_EPOCH: Epoch = Epoch(Date {
year: 1980,
day_of_year: 5,
});
pub type GPSTimestamp = Timestamp<GPSEpoch>;
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
pub struct GPSEpoch;
derive_timestamp_impl!(GPS_EPOCH, GPSTimestamp);
pub const GREGORIAN_EPOCH: Epoch = Epoch(Date {
year: 1582,
day_of_year: 287,
});
pub type GregorianTimestamp = Timestamp<GregorianEpoch>;
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
pub struct GregorianEpoch;
derive_timestamp_impl!(GREGORIAN_EPOCH, GregorianTimestamp);
pub const WINDOWS_NT_EPOCH: Epoch = Epoch(Date {
year: 1601,
day_of_year: 0,
});
pub type WindowsNTTimestamp = Timestamp<WindowsEpoch>;
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
pub struct WindowsEpoch;
derive_timestamp_impl!(WINDOWS_NT_EPOCH, WindowsNTTimestamp);
pub const COMMON_ERA_EPOCH: Epoch = Epoch(Date {
year: 1,
day_of_year: 0,
});
pub const PRIME_EPOCH: Epoch = Epoch(Date {
year: 1900,
day_of_year: 0,
});
pub type PrimeTimestamp = Timestamp<PrimeEpoch>;
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
pub struct PrimeEpoch;
derive_timestamp_impl!(PRIME_EPOCH, PrimeTimestamp);
pub const NTP_EPOCH: Epoch = PRIME_EPOCH;