use std::marker::PhantomData;
use std::ops::{Add, AddAssign, Sub, SubAssign};
use irox_units::units::duration::Duration;
use crate::gregorian::Date;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Epoch(pub Date);
impl Epoch {
pub fn get_gregorian_date(&self) -> Date {
self.0
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
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
}
}
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> Add<Duration> for Timestamp<T> {
type Output = Timestamp<T>;
fn add(self, rhs: Duration) -> Self::Output {
let offset = self.offset + rhs;
Self::new(self.epoch, offset)
}
}
impl<T> AddAssign<Duration> for Timestamp<T> {
fn add_assign(&mut self, rhs: Duration) {
self.offset += rhs;
}
}
impl<T> Sub<Duration> for Timestamp<T> {
type Output = Timestamp<T>;
fn sub(self, rhs: Duration) -> Self::Output {
let offset = self.offset - rhs;
Self::new(self.epoch, offset)
}
}
impl<T> SubAssign<Duration> for Timestamp<T> {
fn sub_assign(&mut self, rhs: Duration) {
self.offset -= rhs;
}
}
impl<T> Add<&Duration> for Timestamp<T> {
type Output = Timestamp<T>;
fn add(self, rhs: &Duration) -> Self::Output {
let offset = self.offset + *rhs;
Self::new(self.epoch, offset)
}
}
impl<T> AddAssign<&Duration> for Timestamp<T> {
fn add_assign(&mut self, rhs: &Duration) {
self.offset += *rhs;
}
}
impl<T> Sub<&Duration> for Timestamp<T> {
type Output = Timestamp<T>;
fn sub(self, rhs: &Duration) -> Self::Output {
let offset = self.offset - *rhs;
Self::new(self.epoch, offset)
}
}
impl<T> SubAssign<&Duration> for Timestamp<T> {
fn sub_assign(&mut self, rhs: &Duration) {
self.offset -= *rhs;
}
}
impl<T> Sub<Timestamp<T>> for Timestamp<T> {
type Output = Duration;
fn sub(self, rhs: Timestamp<T>) -> Self::Output {
self.offset - rhs.offset
}
}
impl<T> Sub<&Timestamp<T>> for Timestamp<T> {
type Output = Duration;
fn sub(self, rhs: &Timestamp<T>) -> Self::Output {
self.offset - rhs.offset
}
}
impl UnixTimestamp {
#[must_use]
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]
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 const NTP_EPOCH: Epoch = PRIME_EPOCH;