apsis-rs/crates/physics/apsis-leap/tests/time.rs

126 lines
4.6 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Tests for UTC and TAI conversions on the Modified Julian Date scale.
//!
//! 约化儒略日尺度上 UTC 与 TAI 换算的测试。
use apsis_leap::{
MJD_AT_UNIX_EPOCH, calendar_mjd, tai_minus_utc_at_mjd, tai_mjd_from_utc_mjd,
utc_mjd_from_tai_mjd,
};
const SECONDS_PER_DAY: f64 = 86_400.0;
#[test]
fn unix_epoch_constant_matches_the_calendar_conversion() {
assert_eq!(MJD_AT_UNIX_EPOCH, calendar_mjd(1970, 1, 1));
}
#[test]
fn offset_is_rejected_before_the_table_begins() {
let before = calendar_mjd(1971, 12, 31);
assert_eq!(tai_minus_utc_at_mjd(before), None);
assert_eq!(tai_mjd_from_utc_mjd(before), None);
assert_eq!(tai_minus_utc_at_mjd(f64::NAN), None);
assert_eq!(tai_minus_utc_at_mjd(f64::INFINITY), None);
}
#[test]
fn first_entry_supplies_the_initial_offset() {
let start = calendar_mjd(1972, 1, 1);
assert_eq!(tai_minus_utc_at_mjd(start), Some(10.0));
// Just before midnight one day earlier the table does not apply.
// 再早一天、午夜之前,该表不适用。
assert_eq!(tai_minus_utc_at_mjd(start - 1.0), None);
}
#[test]
fn offset_changes_exactly_on_the_effective_date() {
// The 2017-01-01 leap second raised the offset from 36 to 37.
// 2017-01-01 的闰秒把偏移从 36 升到 37。
let effective = calendar_mjd(2017, 1, 1);
assert_eq!(tai_minus_utc_at_mjd(effective - 1.0), Some(36.0));
assert_eq!(tai_minus_utc_at_mjd(effective), Some(37.0));
assert_eq!(tai_minus_utc_at_mjd(effective + 0.5), Some(37.0));
}
#[test]
fn every_entry_is_reachable_at_its_effective_instant() {
for entry in apsis_leap::LEAP_SECONDS {
let at_entry = tai_minus_utc_at_mjd(entry.effective_mjd());
assert_eq!(at_entry, Some(entry.tai_minus_utc), "entry {entry:?}");
}
}
#[test]
fn offset_is_constant_between_entries() {
// Mid 2016 sits between the 2015-07-01 and 2017-01-01 entries.
// 2016 年年中位于 2015-07-01 与 2017-01-01 两项之间。
let instant = calendar_mjd(2016, 6, 1);
assert_eq!(tai_minus_utc_at_mjd(instant), Some(36.0));
assert_eq!(tai_minus_utc_at_mjd(instant + 0.25), Some(36.0));
}
#[test]
fn future_instants_keep_the_last_offset() {
let far_future = calendar_mjd(2100, 1, 1);
assert_eq!(tai_minus_utc_at_mjd(far_future), Some(37.0));
}
#[test]
fn utc_to_tai_adds_the_offset() {
let utc = calendar_mjd(2017, 6, 1) + 0.5;
let tai = tai_mjd_from_utc_mjd(utc).expect("instant is inside the table");
// An MJD near 58000 has a resolution of about 7e-12 days, so the sum
// can only be compared to well below a microsecond.
// 接近 58000 的 MJD 分辨率约为 7e-12 天,故该和只能比较到远低于一微秒。
assert!((tai - utc - 37.0 / SECONDS_PER_DAY).abs() < 1e-11);
}
#[test]
fn utc_to_tai_round_trips() {
for (year, month, day) in [
(1972, 1, 1),
(1980, 1, 6),
(1999, 1, 1),
(2015, 7, 1),
(2017, 1, 1),
(2024, 6, 15),
] {
let utc = calendar_mjd(year, month, day) + 0.375;
let tai = tai_mjd_from_utc_mjd(utc).expect("instant is inside the table");
let back = utc_mjd_from_tai_mjd(tai).expect("instant is inside the table");
assert!(
(back - utc).abs() < 1e-10,
"round trip failed at {year}-{month}-{day}: {utc} vs {back}"
);
}
}
#[test]
fn tai_to_utc_recovers_the_offset_on_each_side_of_a_step() {
let effective = calendar_mjd(2017, 1, 1);
// One hour before the step the offset is 36 s, one hour after it is 37 s.
// 阶跃前一小时偏移为 36 s,其后一小时为 37 s。
let before_utc = effective - 1.0 / 24.0;
let after_utc = effective + 1.0 / 24.0;
let before_tai = tai_mjd_from_utc_mjd(before_utc).expect("inside the table");
let after_tai = tai_mjd_from_utc_mjd(after_utc).expect("inside the table");
assert!((utc_mjd_from_tai_mjd(before_tai).expect("inside") - before_utc).abs() < 1e-10);
assert!((utc_mjd_from_tai_mjd(after_tai).expect("inside") - after_utc).abs() < 1e-10);
}
#[test]
fn tai_to_utc_rejects_non_finite_input() {
assert_eq!(utc_mjd_from_tai_mjd(f64::NAN), None);
assert_eq!(utc_mjd_from_tai_mjd(f64::NEG_INFINITY), None);
}
#[test]
fn tai_to_utc_at_the_boundary_stays_inside_the_table() {
// The first TAI instant that the table covers is 1972-01-01 00:00:10
// TAI, because the offset there is ten seconds.
// 该表覆盖的第一个 TAI 瞬时是 1972-01-01 00:00:10 TAI,因为那里的偏移是十秒。
let boundary = calendar_mjd(1972, 1, 1) + 10.0 / SECONDS_PER_DAY;
let utc = utc_mjd_from_tai_mjd(boundary).expect("boundary is covered");
assert!((utc - calendar_mjd(1972, 1, 1)).abs() < 1e-10);
}