200 lines
8.1 KiB
Rust
200 lines
8.1 KiB
Rust
//! The instant type and its scale conversions.
|
||
//!
|
||
//! An [`Epoch`] stores an instant as a Modified Julian Date in International
|
||
//! Atomic Time. TAI is chosen as the internal representation because it is
|
||
//! uniform and because every other scale in this crate is either a constant
|
||
//! offset from it or, for UTC, a step offset that can be looked up.
|
||
//!
|
||
//! Storing the unified scale makes comparison and differencing between two
|
||
//! epochs exact regardless of the scales they were created in. The scale is
|
||
//! therefore not part of an epoch's identity; it is a parameter of the
|
||
//! conversion methods that read or write a number.
|
||
|
||
use crate::calendar::{
|
||
Calendar, SECONDS_PER_DAY, mjd_from_ymd, seconds_of_day_from_mjd, ymd_from_mjd,
|
||
};
|
||
use crate::scale::{TAI_MINUS_GPS, TT_MINUS_TAI, TimeScale};
|
||
use apsis_leap::{tai_minus_utc_at_mjd, utc_mjd_from_tai_mjd};
|
||
use core::fmt;
|
||
|
||
/// An instant on the TAI time scale, stored as a Modified Julian Date.
|
||
///
|
||
/// The field is private so that the stored scale is guaranteed. Use
|
||
/// [`Epoch::from_mjd`] or the named constructors to build one.
|
||
///
|
||
/// TAI 时间尺度上的一个瞬时,以修正儒略日(Modified Julian Date)存储。
|
||
///
|
||
/// 字段私有以保证所存标度确定。用 [`Epoch::from_mjd`] 或具名构造函数创建。
|
||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||
pub struct Epoch {
|
||
tai_mjd: f64,
|
||
}
|
||
|
||
impl Epoch {
|
||
/// The instant 2000-01-01T12:00:00 TT, which defines the J2000.0 epoch.
|
||
///
|
||
/// The value is `2451545.0` on the TT scale, offset back to TAI.
|
||
///
|
||
/// 瞬时 2000-01-01T12:00:00 TT,定义了 J2000.0 历元。
|
||
///
|
||
/// 该值在 TT 标度上为 `2451545.0`,并回退到 TAI。
|
||
pub const J2000: Self = Self::from_tai_mjd_const(51_544.5 - TT_MINUS_TAI / SECONDS_PER_DAY);
|
||
|
||
/// Creates an epoch from a Modified Julian Date on the TAI scale.
|
||
///
|
||
/// 由 TAI 标度上的修正儒略日创建历元。
|
||
pub const fn from_tai_mjd(tai_mjd: f64) -> Self {
|
||
Self { tai_mjd }
|
||
}
|
||
|
||
/// Const constructor used by the associated constants.
|
||
///
|
||
/// 供各关联常量使用的 const 构造函数。
|
||
const fn from_tai_mjd_const(tai_mjd: f64) -> Self {
|
||
Self { tai_mjd }
|
||
}
|
||
|
||
/// Creates an epoch from a Modified Julian Date on the given scale.
|
||
///
|
||
/// Returns `None` when the instant cannot be expressed on the requested
|
||
/// scale. This happens for UTC before 1972, where the leap second table
|
||
/// used to convert from UTC does not apply.
|
||
///
|
||
/// 由给定标度上的修正儒略日(Modified Julian Date)创建历元。
|
||
///
|
||
/// 当该瞬时无法在所请求的标度上表达时返回 `None`。这发生在 1972 年之前的
|
||
/// UTC,因为用于从 UTC 换算的闰秒表不适用。
|
||
pub fn from_mjd(mjd: f64, scale: TimeScale) -> Option<Self> {
|
||
if !mjd.is_finite() {
|
||
return None;
|
||
}
|
||
let tai_mjd = match scale {
|
||
TimeScale::Tai => mjd,
|
||
TimeScale::Tt => mjd - TT_MINUS_TAI / SECONDS_PER_DAY,
|
||
TimeScale::Gps => mjd + TAI_MINUS_GPS / SECONDS_PER_DAY,
|
||
TimeScale::Utc => {
|
||
let offset = tai_minus_utc_at_mjd(mjd)?;
|
||
mjd + offset / SECONDS_PER_DAY
|
||
}
|
||
};
|
||
Some(Self { tai_mjd })
|
||
}
|
||
|
||
/// Creates an epoch from a calendar date, interpreting it on the given
|
||
/// scale.
|
||
///
|
||
/// Returns `None` when the fields do not form a valid date, or when the
|
||
/// instant cannot be expressed on the requested scale.
|
||
///
|
||
/// 由日历日期创建历元,并按给定标度解释该日期。
|
||
///
|
||
/// 当各字段不构成有效日期,或该瞬时无法在所请求的标度上表达时返回 `None`。
|
||
pub fn from_calendar(calendar: Calendar, scale: TimeScale) -> Option<Self> {
|
||
if !calendar.is_valid() {
|
||
return None;
|
||
}
|
||
let mjd = mjd_from_ymd(calendar.year, calendar.month, calendar.day)
|
||
+ calendar.seconds_of_day() / SECONDS_PER_DAY;
|
||
Self::from_mjd(mjd, scale)
|
||
}
|
||
|
||
/// Returns the instant as a Modified Julian Date on the TAI scale.
|
||
///
|
||
/// 返回该瞬时在 TAI 标度上的修正儒略日(Modified Julian Date)。
|
||
pub const fn tai_mjd(self) -> f64 {
|
||
self.tai_mjd
|
||
}
|
||
|
||
/// Returns the instant as a Modified Julian Date on the given scale.
|
||
///
|
||
/// Returns `None` when the instant precedes 1972 and the UTC scale is
|
||
/// requested, because the leap second table does not cover that range.
|
||
///
|
||
/// 返回该瞬时在给定标度上的修正儒略日(Modified Julian Date)。
|
||
///
|
||
/// 当该瞬时早于 1972 年且请求 UTC 标度时返回 `None`,因为闰秒表不覆盖该
|
||
/// 区间。
|
||
pub fn mjd(self, scale: TimeScale) -> Option<f64> {
|
||
match scale {
|
||
TimeScale::Tai => Some(self.tai_mjd),
|
||
TimeScale::Tt => Some(self.tai_mjd + TT_MINUS_TAI / SECONDS_PER_DAY),
|
||
TimeScale::Gps => Some(self.tai_mjd - TAI_MINUS_GPS / SECONDS_PER_DAY),
|
||
TimeScale::Utc => utc_mjd_from_tai_mjd(self.tai_mjd),
|
||
}
|
||
}
|
||
|
||
/// Returns the instant as a calendar date on the given scale.
|
||
///
|
||
/// Returns `None` when the scale conversion is not available for this
|
||
/// instant; see [`Epoch::mjd`].
|
||
///
|
||
/// 返回该瞬时在给定标度上的日历日期。
|
||
///
|
||
/// 当该瞬时不可进行标度换算时返回 `None`;参见 [`Epoch::mjd`]。
|
||
pub fn calendar(self, scale: TimeScale) -> Option<Calendar> {
|
||
let mjd = self.mjd(scale)?;
|
||
let (year, month, day) = ymd_from_mjd(mjd);
|
||
let seconds = seconds_of_day_from_mjd(mjd);
|
||
let hour = (seconds / 3_600.0).floor();
|
||
let minute = ((seconds - hour * 3_600.0) / 60.0).floor();
|
||
let second = seconds - hour * 3_600.0 - minute * 60.0;
|
||
Some(Calendar::new(
|
||
year,
|
||
month,
|
||
day,
|
||
hour as u32,
|
||
minute as u32,
|
||
second,
|
||
))
|
||
}
|
||
|
||
/// Returns the signed number of seconds from `self` to `other`.
|
||
///
|
||
/// The result is positive when `other` is later. Because both epochs
|
||
/// are stored on the uniform TAI scale, the difference is exact and
|
||
/// does not depend on the scales used to create either epoch.
|
||
///
|
||
/// 返回从 `self` 到 `other` 的带符号秒数。
|
||
///
|
||
/// 当 `other` 更晚时结果为正。由于两个历元都存于均匀的 TAI 标度,差值
|
||
/// 精确,且与创建二者时所用的标度无关。
|
||
pub fn seconds_until(self, other: Self) -> f64 {
|
||
(other.tai_mjd - self.tai_mjd) * SECONDS_PER_DAY
|
||
}
|
||
|
||
/// Returns an epoch shifted by `seconds`.
|
||
///
|
||
/// The shift is measured in SI seconds on the uniform TAI scale, so it
|
||
/// advances the instant by exactly that much elapsed time. This is the
|
||
/// only shift offered: a shift measured on the UTC clock would differ
|
||
/// when the interval spans an inserted leap second, and the UTC clock
|
||
/// does not have a single well defined numeric value across such an
|
||
/// instant. Callers that need calendar arithmetic on the UTC scale
|
||
/// should convert to a [`Calendar`], adjust it, and convert back.
|
||
///
|
||
/// 返回平移 `seconds` 后的历元。
|
||
///
|
||
/// 平移量以均匀 TAI 标度上的 SI 秒计,故瞬时恰好前进相同的流逝时间。这是
|
||
/// 唯一提供的平移方式:若按 UTC 时钟平移,当区间跨越插入的闰秒时结果会
|
||
/// 不同,而 UTC 时钟在该瞬时前后没有单一良定义的数值。需要按 UTC 标度做
|
||
/// 日历运算的调用方,应先转为 [`Calendar`]、调整后再转回。
|
||
pub fn add_seconds(self, seconds: f64) -> Self {
|
||
Self {
|
||
tai_mjd: self.tai_mjd + seconds / SECONDS_PER_DAY,
|
||
}
|
||
}
|
||
|
||
/// Returns `true` when the stored Modified Julian Date is finite.
|
||
///
|
||
/// 当所存修正儒略日(Modified Julian Date)有限时返回 `true`。
|
||
pub const fn is_finite(self) -> bool {
|
||
self.tai_mjd.is_finite()
|
||
}
|
||
}
|
||
|
||
impl fmt::Display for Epoch {
|
||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||
write!(f, "{} TAI", self.tai_mjd)
|
||
}
|
||
}
|