use crate::deltas::TimeDelta;
use crate::deltas::ToDelta;
use crate::offsets::DefaultOffsetProvider;
use crate::offsets::Offset;
use crate::time::Time;
use crate::time_of_day::CivilTime;
use crate::time_of_day::TimeOfDay;
use crate::time_scales::ContinuousTimeScale;
use crate::time_scales::{Tai, TimeScale};
use lox_core::i64::consts::{SECONDS_PER_DAY, SECONDS_PER_HALF_DAY};
use lox_core::time::calendar_dates::Date;
use super::LeapSecondsProvider;
use super::Utc;
use super::leap_seconds::DefaultLeapSecondsProvider;
mod before1972;
impl Utc {
pub fn offset_tai(&self, provider: &impl LeapSecondsProvider) -> TimeDelta {
if self < &UTC_1972_01_01 {
before1972::delta_utc_tai(self)
} else {
provider.delta_utc_tai(*self)
}
}
pub fn to_time_with_provider(&self, provider: &impl LeapSecondsProvider) -> Time<Tai> {
let offset = self.offset_tai(provider);
Time::from_delta(Tai, self.to_delta() - offset)
}
pub fn to_time(&self) -> Time<Tai> {
self.to_time_with_provider(&DefaultLeapSecondsProvider)
}
pub fn to_dynamic_time_with_provider(&self, provider: &impl LeapSecondsProvider) -> Time {
let offset = self.offset_tai(provider);
Time::from_delta(TimeScale::Tai, self.to_delta() - offset)
}
pub fn to_dynamic_time(&self) -> Time {
self.to_dynamic_time_with_provider(&DefaultLeapSecondsProvider)
}
}
pub trait ToUtc {
fn to_utc_with_provider(&self, provider: &impl LeapSecondsProvider) -> Utc;
fn to_utc(&self) -> Utc {
self.to_utc_with_provider(&DefaultLeapSecondsProvider)
}
}
impl ToUtc for Utc {
fn to_utc_with_provider(&self, _provider: &impl LeapSecondsProvider) -> Utc {
*self
}
}
impl<T> ToUtc for Time<T>
where
T: ContinuousTimeScale + Copy,
DefaultOffsetProvider: Offset<T, Tai>,
{
fn to_utc_with_provider(&self, provider: &impl LeapSecondsProvider) -> Utc {
let tai = self.to_scale(Tai);
let delta = if tai < TAI_AT_UTC_1972_01_01 {
before1972::delta_tai_utc(&tai)
} else {
provider.delta_tai_utc(tai)
};
let mut utc = Utc::from_delta(tai.to_delta() - delta);
if provider.is_leap_second(tai) {
utc.time = TimeOfDay::new(utc.hour(), utc.minute(), 60)
.unwrap()
.with_subsecond(utc.time.subsecond());
}
utc
}
}
const LEAP_SECONDS_1972: i64 = 10;
const UTC_1972_01_01: Utc =
Utc::new_unchecked(Date::new_unchecked(1972, 1, 1), TimeOfDay::MIDNIGHT);
const TAI_AT_UTC_1972_01_01: Time<Tai> = {
let seconds =
Date::new_unchecked(1972, 1, 1).j2000_day_number() * SECONDS_PER_DAY - SECONDS_PER_HALF_DAY;
let base = TimeDelta::from_seconds(seconds);
let leap = TimeDelta::from_seconds(LEAP_SECONDS_1972);
Time::from_delta(Tai, base.add_const(leap))
};
#[cfg(test)]
mod test {
use crate::subsecond::Subsecond;
use crate::time;
use crate::time_of_day::TimeOfDay;
use crate::time_scales::{Tcb, Tcg, Tdb, Tt};
use crate::utc;
use rstest::rstest;
use super::*;
#[test]
fn test_utc_to_utc() {
let utc0 = utc!(2000, 1, 1).unwrap();
let utc1 = utc0.to_utc();
assert_eq!(utc0, utc1);
}
#[rstest]
#[case::before_1972(UTC_1971_01_01, TAI_AT_UTC_1971_01_01)]
#[case::before_leap_second(UTC_1S_BEFORE_2016_LEAP_SECOND, TAI_1S_BEFORE_2016_LEAP_SECOND)]
#[case::during_leap_second(UTC_DURING_2016_LEAP_SECOND, TAI_DURING_2016_LEAP_SECOND)]
#[case::after_leap_second(UTC_1S_AFTER_2016_LEAP_SECOND, TAI_1S_AFTER_2016_LEAP_SECOND)]
fn test_utc_to_tai(#[case] utc: Utc, #[case] expected: Time<Tai>) {
let actual = utc.to_time();
assert_eq!(expected, actual);
}
#[rstest]
#[case::before_utc_1972(TAI_AT_UTC_1971_01_01, UTC_1971_01_01)]
#[case::utc_1972(TAI_AT_UTC_1972_01_01, UTC_1972_01_01)]
#[case::before_leap_second(TAI_1S_BEFORE_2016_LEAP_SECOND, UTC_1S_BEFORE_2016_LEAP_SECOND)]
#[case::during_leap_second(TAI_DURING_2016_LEAP_SECOND, UTC_DURING_2016_LEAP_SECOND)]
#[case::after_leap_second(TAI_1S_AFTER_2016_LEAP_SECOND, UTC_1S_AFTER_2016_LEAP_SECOND)]
#[case::before_1960(TAI_BEFORE_1960, UTC_BEFORE_1960)]
fn test_tai_to_utc(#[case] tai: Time<Tai>, #[case] expected: Utc) {
let actual = tai.to_utc();
assert_eq!(expected, actual);
}
#[test]
fn test_all_scales_to_utc() {
use lox_approx::assert_approx_eq;
let tai = time!(Tai, 2024, 5, 17, 12, 13, 14.0).unwrap();
let exp = tai.to_utc();
let tt = tai.to_scale(Tt);
let act = tt.to_utc();
assert_eq!(act, exp);
let tcg = tai.to_scale(Tcg);
let act = tcg.to_utc();
assert_eq!(act, exp);
let tcb = tai.to_scale(Tcb);
let act = tcb.to_utc();
assert_approx_eq!(act, exp);
let tdb = tai.to_scale(Tdb);
let act = tdb.to_utc();
assert_eq!(act, exp);
}
const UTC_1971_01_01: Utc =
Utc::new_unchecked(Date::new_unchecked(1971, 1, 1), TimeOfDay::MIDNIGHT);
const TAI_AT_UTC_1971_01_01: Time<Tai> = {
const DELTA: TimeDelta = TimeDelta::from_seconds_and_subsecond_f64(8.0, 0.9461620000000011);
let seconds = Date::new_unchecked(1971, 1, 1).j2000_day_number() * SECONDS_PER_DAY
- SECONDS_PER_HALF_DAY;
let base = TimeDelta::from_seconds(seconds);
Time::from_delta(Tai, base.add_const(DELTA))
};
const UTC_1S_BEFORE_2016_LEAP_SECOND: Utc = Utc::new_unchecked(
Date::new_unchecked(2016, 12, 31),
TimeOfDay::new_unchecked(23, 59, 59),
);
const TAI_1S_BEFORE_2016_LEAP_SECOND: Time<Tai> = Time::new(Tai, 536500835, Subsecond::ZERO);
const UTC_DURING_2016_LEAP_SECOND: Utc = Utc::new_unchecked(
Date::new_unchecked(2016, 12, 31),
TimeOfDay::new_unchecked(23, 59, 60),
);
const TAI_DURING_2016_LEAP_SECOND: Time<Tai> = Time::new(Tai, 536500836, Subsecond::ZERO);
const UTC_1S_AFTER_2016_LEAP_SECOND: Utc =
Utc::new_unchecked(Date::new_unchecked(2017, 1, 1), TimeOfDay::MIDNIGHT);
const TAI_1S_AFTER_2016_LEAP_SECOND: Time<Tai> = Time::new(Tai, 536500837, Subsecond::ZERO);
const UTC_BEFORE_1960: Utc =
Utc::new_unchecked(Date::new_unchecked(1959, 12, 31), TimeOfDay::MIDNIGHT);
const TAI_BEFORE_1960: Time<Tai> = {
let seconds = Date::new_unchecked(1959, 12, 31).j2000_day_number() * SECONDS_PER_DAY
- SECONDS_PER_HALF_DAY;
Time::from_delta(Tai, TimeDelta::from_seconds(seconds))
};
}