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

116 lines
3.9 KiB
Rust

//! Tests for the calendar conversions.
//!
//! 日历换算的测试。
use apsis_time::{Calendar, days_in_month, is_leap_year, mjd_from_ymd, ymd_from_mjd};
#[test]
fn modified_julian_date_zero_is_the_documented_day() {
assert_eq!(mjd_from_ymd(1858, 11, 17), 0.0);
assert_eq!(ymd_from_mjd(0.0), (1858, 11, 17));
}
#[test]
fn known_dates_map_to_known_modified_julian_dates() {
assert_eq!(mjd_from_ymd(1970, 1, 1), 40_587.0);
assert_eq!(mjd_from_ymd(2000, 1, 1), 51_544.0);
assert_eq!(mjd_from_ymd(2017, 1, 1), 57_754.0);
assert_eq!(mjd_from_ymd(2024, 1, 1), 60_310.0);
}
#[test]
fn calendar_conversion_round_trips_over_a_long_span() {
// Walk day by day over a span that crosses every leap year rule.
// 逐日走过一个跨越每条闰年规则的跨度。
let mut mjd = mjd_from_ymd(1899, 12, 20);
let end = mjd_from_ymd(2101, 1, 10);
while mjd < end {
let (year, month, day) = ymd_from_mjd(mjd);
assert_eq!(
mjd_from_ymd(year, month, day),
mjd,
"round trip failed at MJD {mjd}"
);
mjd += 1.0;
}
}
#[test]
fn fractional_modified_julian_dates_map_to_the_containing_day() {
let day = mjd_from_ymd(2024, 6, 15);
assert_eq!(ymd_from_mjd(day + 0.25), (2024, 6, 15));
assert_eq!(ymd_from_mjd(day + 0.75), (2024, 6, 15));
// One day later, but still the same instant of the following day.
// 晚一天,但仍是次日的同一瞬时。
assert_eq!(ymd_from_mjd(day + 1.25), (2024, 6, 16));
}
#[test]
fn leap_years_follow_the_gregorian_rule() {
assert!(is_leap_year(2000));
assert!(is_leap_year(2024));
assert!(!is_leap_year(1900));
assert!(!is_leap_year(2100));
assert!(!is_leap_year(2023));
// Year zero is a leap year in the proleptic calendar.
// 在外推日历中零年是闰年。
assert!(is_leap_year(0));
}
#[test]
fn february_length_follows_the_leap_year_rule() {
assert_eq!(days_in_month(2024, 2), 29);
assert_eq!(days_in_month(2023, 2), 28);
assert_eq!(days_in_month(1900, 2), 28);
assert_eq!(days_in_month(2000, 2), 29);
}
#[test]
fn days_in_month_rejects_invalid_months() {
assert_eq!(days_in_month(2024, 0), 0);
assert_eq!(days_in_month(2024, 13), 0);
}
#[test]
fn every_month_has_the_expected_length() {
let expected = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
for (index, days) in expected.iter().enumerate() {
let month = index as u32 + 1;
assert_eq!(days_in_month(2023, month), *days, "month {month}");
}
}
#[test]
fn validation_accepts_real_dates() {
assert!(Calendar::from_ymd(2024, 2, 29).is_valid());
assert!(Calendar::new(2016, 12, 31, 23, 59, 59.5).is_valid());
}
#[test]
fn validation_rejects_impossible_dates_and_the_leap_second_label() {
assert!(!Calendar::from_ymd(2023, 2, 29).is_valid());
assert!(!Calendar::from_ymd(2024, 13, 1).is_valid());
assert!(!Calendar::from_ymd(2024, 1, 0).is_valid());
assert!(!Calendar::from_ymd(2024, 1, 32).is_valid());
assert!(!Calendar::new(2024, 1, 1, 24, 0, 0.0).is_valid());
assert!(!Calendar::new(2024, 1, 1, 0, 60, 0.0).is_valid());
// Second 60 is the leap second label, which is not a representable
// instant on the continuous Modified Julian Date scale.
// 第 60 秒是闰秒标签,它在连续的约化儒略日尺度上不是可表示的瞬时。
assert!(!Calendar::new(2016, 12, 31, 23, 59, 60.0).is_valid());
assert!(!Calendar::new(2024, 1, 1, 0, 0, 61.0).is_valid());
}
#[test]
fn seconds_of_day_accumulates_the_time_fields() {
let calendar = Calendar::new(2024, 1, 1, 1, 2, 3.5);
assert!((calendar.seconds_of_day() - 3_723.5).abs() < 1e-12);
assert_eq!(Calendar::from_ymd(2024, 1, 1).seconds_of_day(), 0.0);
}
#[test]
fn display_uses_an_iso_style_layout() {
let calendar = Calendar::new(2024, 6, 15, 12, 30, 45.25);
assert_eq!(calendar.to_string(), "2024-06-15T12:30:45.250");
}