#[cfg(feature = "python")]
use pyo3::prelude::*;
#[cfg(kani)]
mod kani;
#[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
mod fmt;
pub(crate) mod tcl;
use crate::{Duration, Epoch, Unit, SECONDS_PER_DAY};
pub const J1900_REF_EPOCH: Epoch = Epoch {
duration: Duration {
centuries: 0,
nanoseconds: 43200000000000,
},
time_scale: TimeScale::TAI,
};
pub const J2000_REF_EPOCH: Epoch = Epoch {
duration: Duration {
centuries: 1,
nanoseconds: 43200000000000,
},
time_scale: TimeScale::TAI,
};
pub const GPST_REF_EPOCH: Epoch = Epoch::from_tai_duration(Duration {
centuries: 0,
nanoseconds: 2_524_953_619_000_000_000, });
pub const SECONDS_GPS_TAI_OFFSET: f64 = 2_524_953_619.0;
pub const SECONDS_GPS_TAI_OFFSET_I64: i64 = 2_524_953_619;
pub const DAYS_GPS_TAI_OFFSET: f64 = SECONDS_GPS_TAI_OFFSET / SECONDS_PER_DAY;
pub const QZSST_REF_EPOCH: Epoch = GPST_REF_EPOCH;
pub const GST_REF_EPOCH: Epoch = Epoch::from_tai_duration(Duration {
centuries: 0,
nanoseconds: 3_144_268_819_000_000_000, });
pub const SECONDS_GST_TAI_OFFSET: f64 = 3_144_268_819.0;
pub const SECONDS_GST_TAI_OFFSET_I64: i64 = 3_144_268_819;
pub const BDT_REF_EPOCH: Epoch = Epoch::from_tai_duration(Duration {
centuries: 1,
nanoseconds: 189_302_433_000_000_000, });
pub const SECONDS_BDT_TAI_OFFSET: f64 = 3_345_062_433.0;
pub const SECONDS_BDT_TAI_OFFSET_I64: i64 = 3_345_062_433;
pub const UNIX_REF_EPOCH: Epoch = Epoch::from_tai_duration(Duration {
centuries: 0,
nanoseconds: 2_208_988_800_000_000_000,
});
pub(crate) const HIFITIME_REF_YEAR: i32 = 1900;
#[non_exhaustive]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "python", pyclass(eq, eq_int))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TimeScale {
TAI,
TT,
ET,
TDB,
UTC,
GPST,
GST,
BDT,
QZSST,
TCG,
TCB,
TL,
TCL,
}
impl Default for TimeScale {
fn default() -> Self {
Self::TAI
}
}
impl TimeScale {
pub(crate) const fn formatted_len(&self) -> usize {
match &self {
Self::QZSST => 5,
Self::GPST => 4,
Self::TAI
| Self::TDB
| Self::UTC
| Self::GST
| Self::BDT
| Self::TCG
| Self::TCB
| Self::TCL => 3,
Self::ET | Self::TT | Self::TL => 2,
}
}
pub const fn is_gnss(&self) -> bool {
matches!(self, Self::GPST | Self::GST | Self::BDT | Self::QZSST)
}
pub const fn reference_epoch(self) -> Epoch {
Epoch {
duration: Duration::ZERO,
time_scale: self,
}
}
pub(crate) const fn prime_epoch_offset(self) -> Duration {
match self {
TimeScale::ET | TimeScale::TDB => {
Duration {
centuries: 0,
nanoseconds: 3155716800000000000,
}
}
TimeScale::GPST | TimeScale::QZSST => Duration {
centuries: 0,
nanoseconds: 2_524_953_619_000_000_000,
},
TimeScale::GST => Duration {
centuries: 0,
nanoseconds: 3_144_268_819_000_000_000,
},
TimeScale::BDT => Duration {
centuries: 1,
nanoseconds: 189_302_433_000_000_000,
},
TimeScale::TCG | TimeScale::TL | TimeScale::TCL => {
Duration {
centuries: 0,
nanoseconds: 2429913632184000000,
}
}
TimeScale::TCB => {
Duration {
centuries: 0,
nanoseconds: 2429913632184065500,
}
}
_ => Duration::ZERO,
}
}
pub(crate) fn gregorian_epoch_offset(self) -> Duration {
let prime_offset = self.prime_epoch_offset();
prime_offset - prime_offset.subdivision(Unit::Second).unwrap()
}
}
#[cfg_attr(feature = "python", pymethods)]
impl TimeScale {
pub const fn uses_leap_seconds(&self) -> bool {
matches!(self, Self::UTC)
}
}
impl From<TimeScale> for u8 {
fn from(ts: TimeScale) -> Self {
match ts {
TimeScale::TAI => 0,
TimeScale::TT => 1,
TimeScale::ET => 2,
TimeScale::TDB => 3,
TimeScale::UTC => 4,
TimeScale::GPST => 5,
TimeScale::GST => 6,
TimeScale::BDT => 7,
TimeScale::QZSST => 8,
TimeScale::TCG => 9,
TimeScale::TCB => 10,
TimeScale::TL => 11,
TimeScale::TCL => 12,
}
}
}
impl From<u8> for TimeScale {
fn from(val: u8) -> Self {
match val {
1 => Self::TT,
2 => Self::ET,
3 => Self::TDB,
4 => Self::UTC,
5 => Self::GPST,
6 => Self::GST,
7 => Self::BDT,
8 => Self::QZSST,
9 => Self::TCG,
10 => Self::TCB,
11 => Self::TL,
12 => Self::TCL,
_ => Self::TAI,
}
}
}
#[cfg(test)]
mod ut_timescale {
use super::TimeScale;
#[test]
#[cfg(feature = "serde")]
fn test_serdes() {
let ts = TimeScale::UTC;
let content = "\"UTC\"";
assert_eq!(content, serde_json::to_string(&ts).unwrap());
let parsed: TimeScale = serde_json::from_str(content).unwrap();
assert_eq!(ts, parsed);
}
#[test]
fn test_ts() {
for ts_u8 in 0..u8::MAX {
let ts = TimeScale::from(ts_u8);
let ts_u8_back: u8 = ts.into();
if ts_u8 < 13 {
assert_eq!(ts_u8_back, ts_u8, "got {ts_u8_back} want {ts_u8}");
} else {
assert_eq!(ts, TimeScale::TAI);
}
}
}
#[test]
#[cfg(feature = "std")]
fn test_ref_epoch() {
use crate::{Duration, Epoch, Unit};
let prime_e = Epoch::from_duration(Duration::ZERO, TimeScale::TAI);
assert_eq!(prime_e.duration, Duration::ZERO);
assert_eq!(format!("{prime_e}"), "1900-01-01T00:00:00 TAI");
assert_eq!(
format!("{}", prime_e + Unit::Century * 1),
"2000-01-02T00:00:00 TAI"
);
assert_eq!(
format!("{}", TimeScale::ET.reference_epoch()),
"2000-01-01T12:00:00 ET"
);
}
}