use core::str::FromStr;
use snafu::ResultExt;
use crate::{
efmt::Format, errors::ParseSnafu, Duration, Epoch, HifitimeError, TimeScale, Unit, Weekday,
ET_OFFSET_US, MJD_J1900, MJD_OFFSET, NANOSECONDS_PER_DAY, UNIX_REF_EPOCH,
};
impl Epoch {
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TAI))]
pub const fn from_tai_duration(duration: Duration) -> Self {
Self {
duration,
time_scale: TimeScale::TAI,
}
}
pub fn to_duration_since_j1900(&self) -> Duration {
self.to_time_scale(TimeScale::TAI).duration
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TAI))]
pub fn from_tai_parts(centuries: i16, nanoseconds: u64) -> Self {
Self::from_tai_duration(Duration::from_parts(centuries, nanoseconds))
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TAI))]
#[cfg_attr(kani, kani::requires(seconds.is_finite()))]
pub fn from_tai_seconds(seconds: f64) -> Self {
Self::from_tai_duration(seconds * Unit::Second)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TAI))]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_tai_days(days: f64) -> Self {
Self::from_tai_duration(days * Unit::Day)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::UTC))]
pub fn from_utc_duration(duration: Duration) -> Self {
Self::from_duration(duration, TimeScale::UTC)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::UTC))]
#[cfg_attr(kani, kani::requires(seconds.is_finite()))]
pub fn from_utc_seconds(seconds: f64) -> Self {
Self::from_utc_duration(seconds * Unit::Second)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::UTC))]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_utc_days(days: f64) -> Self {
Self::from_utc_duration(days * Unit::Day)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GPST))]
pub fn from_gpst_duration(duration: Duration) -> Self {
Self::from_duration(duration, TimeScale::GPST)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::QZSST))]
pub fn from_qzsst_duration(duration: Duration) -> Self {
Self::from_duration(duration, TimeScale::QZSST)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GST))]
pub fn from_gst_duration(duration: Duration) -> Self {
Self::from_duration(duration, TimeScale::GST)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::BDT))]
pub fn from_bdt_duration(duration: Duration) -> Self {
Self::from_duration(duration, TimeScale::BDT)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TAI))]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_mjd_tai(days: f64) -> Self {
Self::from_mjd_in_time_scale(days, TimeScale::TAI)
}
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == time_scale))]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_mjd_in_time_scale(days: f64, time_scale: TimeScale) -> Self {
Self {
duration: (days - MJD_J1900) * Unit::Day,
time_scale,
}
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::UTC))]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_mjd_utc(days: f64) -> Self {
Self::from_mjd_in_time_scale(days, TimeScale::UTC)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GPST))]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_mjd_gpst(days: f64) -> Self {
Self::from_mjd_in_time_scale(days, TimeScale::GPST)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::QZSST))]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_mjd_qzsst(days: f64) -> Self {
Self::from_mjd_in_time_scale(days, TimeScale::QZSST)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GST))]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_mjd_gst(days: f64) -> Self {
Self::from_mjd_in_time_scale(days, TimeScale::GST)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::BDT))]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_mjd_bdt(days: f64) -> Self {
Self::from_mjd_in_time_scale(days, TimeScale::BDT)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TAI))]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_jde_tai(days: f64) -> Self {
Self::from_jde_in_time_scale(days, TimeScale::TAI)
}
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == time_scale))]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_jde_in_time_scale(days: f64, time_scale: TimeScale) -> Self {
Self {
duration: (days - MJD_J1900 - MJD_OFFSET) * Unit::Day - time_scale.prime_epoch_offset(),
time_scale,
}
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::UTC))]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_jde_utc(days: f64) -> Self {
Self::from_jde_in_time_scale(days, TimeScale::UTC)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GPST))]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_jde_gpst(days: f64) -> Self {
Self::from_jde_in_time_scale(days, TimeScale::GPST)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::QZSST))]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_jde_qzsst(days: f64) -> Self {
Self::from_jde_in_time_scale(days, TimeScale::QZSST)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GST))]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_jde_gst(days: f64) -> Self {
Self::from_jde_in_time_scale(days, TimeScale::GST)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::BDT))]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_jde_bdt(days: f64) -> Self {
Self::from_jde_in_time_scale(days, TimeScale::BDT)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TT))]
#[cfg_attr(kani, kani::requires(seconds.is_finite()))]
pub fn from_tt_seconds(seconds: f64) -> Self {
Self::from_tt_duration(seconds * Unit::Second)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TT))]
pub fn from_tt_duration(duration: Duration) -> Self {
Self::from_duration(duration, TimeScale::TT)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::ET))]
#[cfg_attr(kani, kani::requires(seconds_since_j2000.is_finite()))]
pub fn from_et_seconds(seconds_since_j2000: f64) -> Epoch {
Self::from_et_duration(seconds_since_j2000 * Unit::Second)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::ET))]
pub fn from_et_duration(duration_since_j2000: Duration) -> Self {
Self::from_duration(duration_since_j2000, TimeScale::ET)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TDB))]
#[cfg_attr(kani, kani::requires(seconds_j2000.is_finite()))]
pub fn from_tdb_seconds(seconds_j2000: f64) -> Epoch {
Self::from_tdb_duration(seconds_j2000 * Unit::Second)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TDB))]
pub fn from_tdb_duration(duration_since_j2000: Duration) -> Epoch {
Self::from_duration(duration_since_j2000, TimeScale::TDB)
}
#[must_use]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_jde_et(days: f64) -> Self {
Self::from_jde_tdb(days)
}
#[must_use]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_jde_tdb(days: f64) -> Self {
Self::from_jde_tai(days) - Unit::Microsecond * ET_OFFSET_US
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GPST))]
#[cfg_attr(kani, kani::requires(seconds.is_finite()))]
pub fn from_gpst_seconds(seconds: f64) -> Self {
Self::from_duration(seconds * Unit::Second, TimeScale::GPST)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GPST))]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_gpst_days(days: f64) -> Self {
Self::from_duration(days * Unit::Day, TimeScale::GPST)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GPST))]
pub fn from_gpst_nanoseconds(nanoseconds: u64) -> Self {
Self::from_duration(Duration::from_parts(0, nanoseconds), TimeScale::GPST)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::QZSST))]
#[cfg_attr(kani, kani::requires(seconds.is_finite()))]
pub fn from_qzsst_seconds(seconds: f64) -> Self {
Self::from_duration(seconds * Unit::Second, TimeScale::QZSST)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::QZSST))]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_qzsst_days(days: f64) -> Self {
Self::from_duration(days * Unit::Day, TimeScale::QZSST)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::QZSST))]
pub fn from_qzsst_nanoseconds(nanoseconds: u64) -> Self {
Self::from_duration(Duration::from_parts(0, nanoseconds), TimeScale::QZSST)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GST))]
#[cfg_attr(kani, kani::requires(seconds.is_finite()))]
pub fn from_gst_seconds(seconds: f64) -> Self {
Self::from_duration(seconds * Unit::Second, TimeScale::GST)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GST))]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_gst_days(days: f64) -> Self {
Self::from_duration(days * Unit::Day, TimeScale::GST)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GST))]
pub fn from_gst_nanoseconds(nanoseconds: u64) -> Self {
Self::from_duration(Duration::from_parts(0, nanoseconds), TimeScale::GST)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::BDT))]
#[cfg_attr(kani, kani::requires(seconds.is_finite()))]
pub fn from_bdt_seconds(seconds: f64) -> Self {
Self::from_duration(seconds * Unit::Second, TimeScale::BDT)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::BDT))]
#[cfg_attr(kani, kani::requires(days.is_finite()))]
pub fn from_bdt_days(days: f64) -> Self {
Self::from_duration(days * Unit::Day, TimeScale::BDT)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::BDT))]
pub fn from_bdt_nanoseconds(nanoseconds: u64) -> Self {
Self::from_duration(Duration::from_parts(0, nanoseconds), TimeScale::BDT)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TAI))]
pub fn from_ptp_duration(duration: Duration) -> Self {
Self::from_duration(UNIX_REF_EPOCH.to_utc_duration() + duration, TimeScale::TAI)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TAI))]
#[cfg_attr(kani, kani::requires(seconds.is_finite()))]
pub fn from_ptp_seconds(seconds: f64) -> Self {
Self::from_ptp_duration(seconds * Unit::Second)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TAI))]
pub fn from_ptp_nanoseconds(nanoseconds: u64) -> Self {
Self::from_ptp_duration(Duration::from_parts(0, nanoseconds))
}
#[must_use]
pub fn from_unix_duration(duration: Duration) -> Self {
Self::from_utc_duration(UNIX_REF_EPOCH.to_utc_duration() + duration)
}
#[must_use]
#[cfg_attr(kani, kani::requires(seconds.is_finite()))]
pub fn from_unix_seconds(seconds: f64) -> Self {
Self::from_utc_duration(UNIX_REF_EPOCH.to_utc_duration() + seconds * Unit::Second)
}
#[must_use]
#[cfg_attr(kani, kani::requires(millisecond.is_finite()))]
pub fn from_unix_milliseconds(millisecond: f64) -> Self {
Self::from_utc_duration(UNIX_REF_EPOCH.to_utc_duration() + millisecond * Unit::Millisecond)
}
pub fn from_str_with_format(s_in: &str, format: Format) -> Result<Self, HifitimeError> {
format.parse(s_in)
}
pub fn from_format_str(s_in: &str, format_str: &str) -> Result<Self, HifitimeError> {
Format::from_str(format_str)
.with_context(|_| ParseSnafu {
details: "when using format string",
})?
.parse(s_in)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == time_scale))]
pub fn from_time_of_week(week: u32, nanoseconds: u64, time_scale: TimeScale) -> Self {
let mut nanos = i128::from(nanoseconds);
nanos += i128::from(week) * Weekday::DAYS_PER_WEEK_I128 * i128::from(NANOSECONDS_PER_DAY);
let duration = Duration::from_total_nanoseconds(nanos);
Self::from_duration(duration, time_scale)
}
#[must_use]
#[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::UTC))]
pub fn from_time_of_week_utc(week: u32, nanoseconds: u64) -> Self {
Self::from_time_of_week(week, nanoseconds, TimeScale::UTC)
}
#[must_use]
pub fn from_day_of_year(year: i32, days: f64, time_scale: TimeScale) -> Self {
let start_of_year = Self::from_gregorian(year, 1, 1, 0, 0, 0, 0, time_scale);
start_of_year + (days - 1.0) * Unit::Day
}
}