use core::marker::PhantomData;
use core::ops::{Add, AddAssign, Sub, SubAssign};
use irox_units::units::duration::{Duration, DurationUnit};
use crate::epoch::{PrimeEpoch, PRIME_EPOCH};
use crate::{
epoch::{Epoch, UnixTimestamp, COMMON_ERA_EPOCH, GREGORIAN_EPOCH},
gregorian::Date,
SECONDS_IN_DAY,
};
pub const JULIAN_EPOCH: Epoch = Epoch(Date {
year: -4712,
day_of_year: 0,
});
pub const REDUCED_JULIAN_EPOCH: Epoch = Epoch(Date {
year: 1858,
day_of_year: 320,
});
pub const TRUNCATED_JULIAN_EPOCH: Epoch = Epoch(Date {
year: 1968,
day_of_year: 145,
});
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub struct JulianDayNumber<T> {
epoch: Epoch,
day_number: f64,
_phantom: PhantomData<T>,
}
impl<T> JulianDayNumber<T> {
pub(crate) fn new(epoch: Epoch, day_number: f64) -> Self {
JulianDayNumber {
epoch,
day_number,
_phantom: Default::default(),
}
}
pub fn get_day_number(&self) -> f64 {
self.day_number
}
pub fn get_epoch(&self) -> Epoch {
self.epoch
}
}
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
pub struct JulianEpoch;
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
pub struct ReducedJulianEpoch;
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
pub struct ModifiedJulianEpoch;
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
pub struct TruncatedJulianEpoch;
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
pub struct LilianEpoch;
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
pub struct RataDieEpoch;
pub type JulianDate = JulianDayNumber<JulianEpoch>;
pub const JULIAN_JD_OFFSET: f64 = 0.0_f64;
pub type ReducedJulianDate = JulianDayNumber<ReducedJulianEpoch>;
pub const REDUCED_JD_OFFSET: f64 = 2400000_f64;
pub type ModifiedJulianDate = JulianDayNumber<ModifiedJulianEpoch>;
pub const MODIFIED_JD_OFFSET: f64 = 2400000.5_f64;
pub type TruncatedJulianDate = JulianDayNumber<TruncatedJulianEpoch>;
pub const TRUNCATED_JD_OFFSET: f64 = 2440000.5_f64;
pub type LilianDate = JulianDayNumber<LilianEpoch>;
pub const LILIAN_JD_OFFSET: f64 = 2299159.5_f64;
pub type RataDieDate = JulianDayNumber<RataDieEpoch>;
pub const RATA_DIE_JD_OFFSET: f64 = 1721424.5_f64;
pub const UNIX_TS_JD_OFFSET: f64 = 2240587.5_f64;
pub type PrimeDate = JulianDayNumber<PrimeEpoch>;
pub const PRIME_JD_OFFSET: f64 = 2415020.5_f64;
macro_rules! impl_julian {
($date:ident,$epoch:ident,$offset:ident) => {
impl From<JulianDate> for $date {
fn from(value: JulianDate) -> Self {
$date::new($epoch, value.day_number - $offset)
}
}
impl From<$date> for JulianDate {
fn from(value: $date) -> Self {
JulianDate::new(JULIAN_EPOCH, value.day_number + $offset)
}
}
impl From<&JulianDate> for $date {
fn from(value: &JulianDate) -> Self {
$date::new($epoch, value.day_number - $offset)
}
}
impl From<&$date> for JulianDate {
fn from(value: &$date) -> Self {
JulianDate::new(JULIAN_EPOCH, value.day_number + $offset)
}
}
};
}
impl<T> Add<Duration> for JulianDayNumber<T> {
type Output = JulianDayNumber<T>;
fn add(self, rhs: Duration) -> Self::Output {
let day_number = self.day_number + rhs.as_seconds_f64() / SECONDS_IN_DAY as f64;
Self::new(self.epoch, day_number)
}
}
impl<T> Add<&Duration> for JulianDayNumber<T> {
type Output = JulianDayNumber<T>;
fn add(self, rhs: &Duration) -> Self::Output {
let day_number = self.day_number + rhs.as_seconds_f64() / SECONDS_IN_DAY as f64;
Self::new(self.epoch, day_number)
}
}
impl<T> Sub<Duration> for JulianDayNumber<T> {
type Output = JulianDayNumber<T>;
fn sub(self, rhs: Duration) -> Self::Output {
let day_number = self.day_number - rhs.as_seconds_f64() / SECONDS_IN_DAY as f64;
Self::new(self.epoch, day_number)
}
}
impl<T> Sub<&Duration> for JulianDayNumber<T> {
type Output = JulianDayNumber<T>;
fn sub(self, rhs: &Duration) -> Self::Output {
let day_number = self.day_number - rhs.as_seconds_f64() / SECONDS_IN_DAY as f64;
Self::new(self.epoch, day_number)
}
}
impl<T> AddAssign<Duration> for JulianDayNumber<T> {
fn add_assign(&mut self, rhs: Duration) {
self.day_number += rhs.as_seconds_f64() / SECONDS_IN_DAY as f64;
}
}
impl<T> AddAssign<&Duration> for JulianDayNumber<T> {
fn add_assign(&mut self, rhs: &Duration) {
self.day_number += rhs.as_seconds_f64() / SECONDS_IN_DAY as f64;
}
}
impl<T> SubAssign<Duration> for JulianDayNumber<T> {
fn sub_assign(&mut self, rhs: Duration) {
self.day_number -= rhs.as_seconds_f64() / SECONDS_IN_DAY as f64;
}
}
impl<T> SubAssign<&Duration> for JulianDayNumber<T> {
fn sub_assign(&mut self, rhs: &Duration) {
self.day_number -= rhs.as_seconds_f64() / SECONDS_IN_DAY as f64;
}
}
impl<T> Sub<JulianDayNumber<T>> for JulianDayNumber<T> {
type Output = Duration;
fn sub(self, rhs: JulianDayNumber<T>) -> Self::Output {
let dx = self.day_number - rhs.day_number;
Duration::new(dx, DurationUnit::Day)
}
}
impl<T> Sub<&JulianDayNumber<T>> for JulianDayNumber<T> {
type Output = Duration;
fn sub(self, rhs: &JulianDayNumber<T>) -> Self::Output {
let dx = self.day_number - rhs.day_number;
Duration::new(dx, DurationUnit::Day)
}
}
impl From<UnixTimestamp> for JulianDate {
fn from(value: UnixTimestamp) -> Self {
let jd = value.get_offset().as_seconds_f64() / SECONDS_IN_DAY as f64 + UNIX_TS_JD_OFFSET;
JulianDate::new(JULIAN_EPOCH, jd)
}
}
impl From<JulianDate> for UnixTimestamp {
fn from(value: JulianDate) -> Self {
let ts = (value.day_number - UNIX_TS_JD_OFFSET) * SECONDS_IN_DAY as f64;
UnixTimestamp::from_seconds_f64(ts)
}
}
impl_julian!(ReducedJulianDate, REDUCED_JULIAN_EPOCH, REDUCED_JD_OFFSET);
impl_julian!(ModifiedJulianDate, REDUCED_JULIAN_EPOCH, MODIFIED_JD_OFFSET);
impl_julian!(
TruncatedJulianDate,
TRUNCATED_JULIAN_EPOCH,
TRUNCATED_JD_OFFSET
);
impl_julian!(LilianDate, GREGORIAN_EPOCH, LILIAN_JD_OFFSET);
impl_julian!(RataDieDate, COMMON_ERA_EPOCH, RATA_DIE_JD_OFFSET);
impl_julian!(PrimeDate, PRIME_EPOCH, PRIME_JD_OFFSET);