#![allow(clippy::all, clippy::pedantic, clippy::restriction, warnings)]
use deep_time::{Dt, Scale};
#[test]
fn test_ymd_to_jd() {
assert_eq!(Dt::ymd_to_jd(2025, 4, 16), 2460782);
assert_eq!(Dt::ymd_to_jd(2000, 1, 1), 2451545); assert_eq!(Dt::ymd_to_jd(1970, 1, 1), 2440588); assert_eq!(Dt::ymd_to_jd(1582, 10, 15), 2299161); assert_eq!(Dt::ymd_to_jd(1, 1, 1), 1721426);
assert_eq!(Dt::ymd_to_jd(0, 1, 1), 1721060);
assert_eq!(Dt::ymd_to_jd(0, 12, 31), 1721425);
assert_eq!(Dt::ymd_to_jd(-1, 1, 1), 1720695);
assert_eq!(Dt::ymd_to_jd(-1, 12, 31), 1721059);
assert_eq!(Dt::ymd_to_jd(-4, 1, 1), 1719599); assert_eq!(Dt::ymd_to_jd(-100, 1, 1), 1684536);
assert_eq!(Dt::ymd_to_jd(-400, 1, 1), 1574963);
assert_eq!(Dt::ymd_to_jd(-100000, 12, 31), -34802825);
assert_eq!(Dt::ymd_to_jd(2000, 2, 29), 2451604); assert_eq!(Dt::ymd_to_jd(1900, 2, 28), 2415079); assert_eq!(Dt::ymd_to_jd(4, 2, 29), 1722580); assert_eq!(Dt::ymd_to_jd(-4, 2, 29), 1719658);
let test_dates = [
(2025, 4, 16),
(2000, 1, 1),
(1970, 1, 1),
(1582, 10, 15),
(1, 1, 1),
(0, 1, 1),
(0, 12, 31),
(-1, 1, 1),
(-1, 12, 31),
(-4, 1, 1),
(-100, 1, 1),
(-400, 1, 1),
(-100000, 12, 31),
(123456, 7, 4),
(-123456, 12, 31),
];
for (y, m, d) in test_dates {
let jd = Dt::ymd_to_jd(y, m, d);
let (y2, m2, d2) = Dt::jd_to_ymd(jd);
assert_eq!(
(y2, m2, d2),
(y, m, d),
"round-trip failed for {}-{:02}-{:02}",
y,
m,
d
);
}
assert_eq!(Dt::jd_to_ymd(2460782), (2025, 4, 16));
assert_eq!(Dt::jd_to_ymd(2451545), (2000, 1, 1));
assert_eq!(Dt::jd_to_ymd(1721060), (0, 1, 1));
assert_eq!(Dt::jd_to_ymd(1720695), (-1, 1, 1));
assert_eq!(Dt::jd_to_ymd(-34802825), (-100000, 12, 31));
}
#[test]
fn tdb_tt_difference_matches_spice_approximation() {
let test_points = [
Dt::from_sec(0, Scale::TAI), Dt::from_sec(1_000_000_000, Scale::TAI), Dt::from_sec(-500_000_000, Scale::TAI), Dt::from_sec(86_400 * 365 * 50, Scale::TAI), Dt::from_sec(-86_400 * 365 * 100, Scale::TAI), Dt::from_sec(-2_208_945_600, Scale::TAI), ];
for &tai in &test_points {
let tt_num = tai.to(Scale::TT);
let tdb_num = tai.to(Scale::TDB);
let diff = tdb_num.to_diff_raw(tt_num).to_sec_f().abs();
assert!(
diff < 0.002,
"TDB-TT difference of {:.9} s exceeds expected max amplitude (~1.658 ms) at TAI = {:?}",
diff,
tai
);
}
}
#[test]
fn et_tai_roundtrip_is_lossless() {
let original = Dt::from_sec(987_654_321_098, Scale::TAI);
let et = original.to(Scale::ET);
let xt = et.to(Scale::TAI);
assert_eq!(original, xt, "ET round-trip must be lossless");
}
#[test]
fn tdb_tai_roundtrip_is_accurate() {
let test_points = [
Dt::from_sec(0, Scale::TAI), Dt::from_sec(86_400 * 365, Scale::TAI), Dt::from_sec(-86_400 * 365 * 10, Scale::TAI), Dt::from_sec(1_000_000_000, Scale::TAI), Dt::from_sec(-2_208_945_600, Scale::TAI), ];
for &p in &test_points {
let tdb = p.to(Scale::TDB);
let back = tdb.to(Scale::TAI);
let diff = back.to_diff_raw(p).to_sec_f().abs();
assert!(
diff == 0.0,
"TDB round-trip error too large: {} s at {:?}",
diff,
p
);
}
}
#[test]
fn et_tai_roundtrip_is_accurate() {
let test_points = [
Dt::from_sec(0, Scale::TAI), Dt::from_sec(86_400 * 365, Scale::TAI), Dt::from_sec(-86_400 * 365 * 10, Scale::TAI), Dt::from_sec(1_000_000_000, Scale::TAI), Dt::from_sec(-2_208_945_600, Scale::TAI), ];
for &p in &test_points {
let et = p.to(Scale::ET);
let back = et.to(Scale::TAI);
let diff = back.to_diff_raw(p).to_sec_f().abs();
assert!(
diff == 0.0,
"ET round-trip error too large: {} s at {:?}",
diff,
p
);
}
}
#[test]
fn tdb_correction_stays_within_bounds() {
let points = [
Dt::from_sec(0, Scale::TAI),
Dt::from_sec(86_400 * 365 * 100, Scale::TAI),
Dt::from_sec(-86_400 * 365 * 50, Scale::TAI),
];
for &p in &points {
let tt = p.to(Scale::TT);
let tdb = p.to(Scale::TDB);
let corr_s = tdb.to_diff_raw(tt).to_sec_f();
assert!(
corr_s.abs() < 0.002,
"TDB-TT correction should be < 2 ms (got {} s)",
corr_s
);
}
}
#[cfg(feature = "physics")]
#[test]
fn proper_to_tt_with_drift_roundtrip() {
use deep_time::Drift;
let epoch = Dt::from_sec(0, Scale::TAI);
let drift = Drift::new(
Dt::from_ms(100, Scale::TAI), Dt::from_ns(1, Scale::TAI), Dt::ZERO,
);
let onboard_proper = epoch.add(Dt::from_sec(1_000_000, Scale::TAI));
let tt = onboard_proper.convert_using_drift(epoch, drift);
let back = tt.convert_back_using_drift(epoch, drift);
assert_eq!(back, onboard_proper);
}
#[test]
fn tt_tai_offset_exact() {
let tai = Dt::ZERO;
let tt = tai.to(Scale::TT);
let diff_s = tt.to_diff_raw(tai).to_sec_f();
assert!(
(diff_s - 32.184).abs() < 1e-12,
"TT-TAI at J2000 was {} s (expected exactly 32.184)",
diff_s
);
}
#[test]
fn gnss_offsets_are_correct() {
let tai = Dt::ZERO;
let gpst = tai.to(Scale::GPS);
assert!((gpst.to_diff_raw(tai).to_sec_f() + 19.0).abs() < 1e-12);
let qzsst = tai.to(Scale::QZSS);
assert!((qzsst.to_diff_raw(tai).to_sec_f() + 19.0).abs() < 1e-12);
let gst = tai.to(Scale::GST);
assert!((gst.to_diff_raw(tai).to_sec_f() + 19.0).abs() < 1e-12);
let bdt = tai.to(Scale::BDT);
assert!((bdt.to_diff_raw(tai).to_sec_f() + 33.0).abs() < 1e-12);
}
#[test]
fn tcg_tai_roundtrip_is_accurate() {
let test_points = [
Dt::from_sec(0, Scale::TAI),
Dt::from_sec(86_400 * 365, Scale::TAI),
Dt::from_sec(-86_400 * 365 * 10, Scale::TAI),
Dt::from_sec(1_000_000_000, Scale::TAI),
Dt::from_sec(-2_208_945_600, Scale::TAI),
];
for &p in &test_points {
let tcg = p.to(Scale::TCG);
let back = tcg.to(Scale::TAI);
let diff = back.to_diff_raw(p).to_sec_f().abs();
assert!(
diff < 1e-9,
"TCG round-trip error too large: {} s at {:?}",
diff,
p
);
}
}
#[test]
fn tcb_tai_roundtrip_is_accurate() {
let test_points = [
Dt::from_sec(0, Scale::TAI),
Dt::from_sec(86_400 * 365, Scale::TAI),
Dt::from_sec(-86_400 * 365 * 10, Scale::TAI),
Dt::from_sec(1_000_000_000, Scale::TAI),
Dt::from_sec(-2_208_945_600, Scale::TAI),
];
for &p in &test_points {
let tcb = p.to(Scale::TCB);
let back = tcb.to(Scale::TAI);
let diff = back.to_diff_raw(p).to_sec_f().abs();
assert!(
diff < 1e-9,
"TCB round-trip error too large: {} s at {:?}",
diff,
p
);
}
}
#[test]
fn utc_tai_roundtrip_is_accurate() {
let test_points = [
Dt::from_sec(0, Scale::TAI),
Dt::from_sec(86_400 * 365, Scale::TAI),
Dt::from_sec(-86_400 * 365 * 10, Scale::TAI),
Dt::from_sec(1_000_000_000, Scale::TAI),
Dt::from_sec(-2_208_945_600, Scale::TAI),
Dt::from_sec(1_485_779_200, Scale::TAI), ];
for &p in &test_points {
let utc = p.to(Scale::UTC);
let back = utc.to(Scale::TAI);
assert_eq!(back, p, "UTC round-trip failed at {:?}", p);
}
}
#[test]
fn ymd_to_jd_j2000() {
assert_eq!(Dt::ymd_to_jd(2000, 1, 1), 2451545);
}
#[test]
fn ymd_to_jd_leap_year_handling() {
assert_eq!(Dt::ymd_to_jd(2000, 2, 29), 2451604); assert_eq!(Dt::ymd_to_jd(1900, 2, 28), 2415079); }
#[test]
fn is_leap_year_and_valid_date() {
assert!(Dt::is_leap_yr(2000));
assert!(!Dt::is_leap_yr(1900));
assert!(Dt::is_valid_ymd(2024, 2, 29));
assert!(!Dt::is_valid_ymd(2023, 2, 29));
}
#[test]
fn ntp_timestamp() {
let dt = Dt::from_ymd(1985, 7, 1, Scale::TAI, 0, 0, 0, 0);
let ntp = dt.to_ntp();
assert_eq!(
ntp.to_sec(),
2698012800_i128,
"ntp sec for 1985 is wrong, got: {}, expected: {}",
ntp.to_sec(),
2698012800_i128
);
let dt2 = Dt::from_ntp(ntp);
assert_eq!(
dt.to_sec(),
dt2.to_sec(),
"round trip to Dt got wrong sec, old: {}, new: {}",
dt.to_sec(),
dt2.to_sec()
);
let ymd = dt2.to_ymd();
assert_eq!(ymd.yr(), 1985_i64);
assert_eq!(ymd.mo(), 7);
assert_eq!(ymd.day(), 1);
assert_eq!(ymd.hr(), 0);
assert_eq!(ymd.min(), 0);
assert_eq!(ymd.sec(), 0);
assert_eq!(ymd.attos(), 0);
}