diff --git a/src/epoch/mod.rs b/src/epoch/mod.rs index 8c34ba0..c021341 100644 --- a/src/epoch/mod.rs +++ b/src/epoch/mod.rs @@ -33,8 +33,7 @@ use crate::leap_seconds::{LatestLeapSeconds, LeapSecondProvider}; use crate::Weekday; use crate::{ EpochError, MonthName, TimeScale, TimeUnits, BDT_REF_EPOCH, ET_EPOCH_S, GPST_REF_EPOCH, - GST_REF_EPOCH, J1900_OFFSET, J2000_TO_J1900_DURATION, MJD_OFFSET, NANOSECONDS_PER_DAY, - QZSST_REF_EPOCH, UNIX_REF_EPOCH, + GST_REF_EPOCH, MJD_J1900, MJD_OFFSET, NANOSECONDS_PER_DAY, QZSST_REF_EPOCH, UNIX_REF_EPOCH, }; use core::cmp::Eq; use core::str::FromStr; @@ -356,7 +355,7 @@ impl Epoch { days.is_finite(), "Attempted to initialize Epoch with non finite number" ); - Self::from_tai_duration((days - J1900_OFFSET) * Unit::Day) + Self::from_tai_duration((days - MJD_J1900) * Unit::Day) } fn from_mjd_in_time_scale(days: f64, time_scale: TimeScale) -> Self { @@ -396,7 +395,7 @@ impl Epoch { days.is_finite(), "Attempted to initialize Epoch with non finite number" ); - Self::from_tai_duration((days - J1900_OFFSET - MJD_OFFSET) * Unit::Day) + Self::from_tai_duration((days - MJD_J1900 - MJD_OFFSET) * Unit::Day) } fn from_jde_in_time_scale(days: f64, time_scale: TimeScale) -> Self { @@ -820,7 +819,7 @@ impl Epoch { #[must_use] /// Returns this epoch as a duration in the requested units in MJD TAI pub fn to_mjd_tai(&self, unit: Unit) -> f64 { - (self.to_tai_duration() + Unit::Day * J1900_OFFSET).to_unit(unit) + (self.to_tai_duration() + Unit::Day * MJD_J1900).to_unit(unit) } #[must_use] @@ -832,7 +831,7 @@ impl Epoch { #[must_use] /// Returns the Modified Julian Date in the provided unit in UTC. pub fn to_mjd_utc(&self, unit: Unit) -> f64 { - (self.to_utc_duration() + Unit::Day * J1900_OFFSET).to_unit(unit) + (self.to_utc_duration() + Unit::Day * MJD_J1900).to_unit(unit) } #[must_use] @@ -858,7 +857,7 @@ impl Epoch { #[must_use] /// Returns the Julian Days from epoch 01 Jan -4713 12:00 (noon) as a Duration pub fn to_jde_tai_duration(&self) -> Duration { - self.to_tai_duration() + Unit::Day * J1900_OFFSET + Unit::Day * MJD_OFFSET + self.to_tai_duration() + Unit::Day * MJD_J1900 + Unit::Day * MJD_OFFSET } #[must_use] @@ -876,7 +875,7 @@ impl Epoch { #[must_use] /// Returns the Julian days in UTC as a `Duration` pub fn to_jde_utc_duration(&self) -> Duration { - self.to_utc_duration() + Unit::Day * (J1900_OFFSET + MJD_OFFSET) + self.to_utc_duration() + Unit::Day * (MJD_J1900 + MJD_OFFSET) } #[must_use] @@ -923,7 +922,7 @@ impl Epoch { #[must_use] pub fn to_jde_tt_duration(&self) -> Duration { - self.to_tt_duration() + Unit::Day * (J1900_OFFSET + MJD_OFFSET) + self.to_tt_duration() + Unit::Day * (MJD_J1900 + MJD_OFFSET) } #[must_use] @@ -934,7 +933,7 @@ impl Epoch { #[must_use] pub fn to_mjd_tt_duration(&self) -> Duration { - self.to_tt_duration() + Unit::Day * J1900_OFFSET + self.to_tt_duration() + Unit::Day * MJD_J1900 } #[must_use] @@ -1075,13 +1074,6 @@ impl Epoch { self.to_et_duration().to_seconds() } - #[must_use] - /// Returns the Ephemeris Time in duration past 1900 JAN 01 at noon. - /// **Only** use this if the subsequent computation expect J1900 seconds. - pub fn to_et_duration_since_j1900(&self) -> Duration { - self.to_et_duration() + J2000_TO_J1900_DURATION - } - #[must_use] /// Returns the duration between J2000 and the current epoch as per NAIF SPICE. /// @@ -1120,13 +1112,6 @@ impl Epoch { self.to_tdb_duration().to_seconds() } - #[must_use] - /// Returns the Dynamics Barycentric Time (TDB) as a high precision Duration with reference epoch of 1900 JAN 01 at noon. - /// **Only** use this if the subsequent computation expect J1900 seconds. - pub fn to_tdb_duration_since_j1900(&self) -> Duration { - self.to_tdb_duration() + J2000_TO_J1900_DURATION - } - #[must_use] /// Returns the Ephemeris Time JDE past epoch pub fn to_jde_et_days(&self) -> f64 { @@ -1136,7 +1121,7 @@ impl Epoch { #[must_use] pub fn to_jde_et_duration(&self) -> Duration { self.to_et_duration() - + Unit::Day * (J1900_OFFSET + MJD_OFFSET) + + Unit::Day * (MJD_J1900 + MJD_OFFSET) + TimeScale::ET.prime_epoch_offset() } @@ -1148,7 +1133,7 @@ impl Epoch { #[must_use] pub fn to_jde_tdb_duration(&self) -> Duration { self.to_tdb_duration() - + Unit::Day * (J1900_OFFSET + MJD_OFFSET) + + Unit::Day * (MJD_J1900 + MJD_OFFSET) + TimeScale::TDB.prime_epoch_offset() } diff --git a/src/lib.rs b/src/lib.rs index 1a81db4..369d9e2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,25 +10,18 @@ * Documentation: https://nyxspace.com/ */ -pub const J1900_NAIF: f64 = 2_415_020.0; -pub const J2000_NAIF: f64 = 2_451_545.0; -/// `J1900_OFFSET` determines the offset in julian days between 01 Jan 1900 at midnight and the -/// Modified Julian Day at 17 November 1858. -/// NOTE: Julian days "start" at noon so that astronomical observations throughout the night -/// happen at the same Julian day. Note however that the Modified Julian Date (MJD) starts at -/// midnight, not noon, cf. . -pub const J1900_OFFSET: f64 = 15_020.0; -/// `J2000_OFFSET` determines the offset in julian days between 01 Jan 2000 at **noon** and the -/// Modified Julian Day at 17 November 1858. -pub const J2000_OFFSET: f64 = 51_544.5; +/// Julian date for the J1900 epoch, as per NAIF SPICE. +pub const JD_J1900: f64 = 2_415_020.0; +/// Julian date for the J2000 epoch, as per NAIF SPICE. +pub const JD_J2000: f64 = 2_451_545.0; +/// Julian days between 01 Jan 1900 at midnight and the Modified Julian Day at 17 November 1858. +pub const MJD_J1900: f64 = 15_020.0; +/// Julian days between 01 Jan 2000 at **noon** and the Modified Julian Day at 17 November 1858. +pub const MJD_J2000: f64 = 51_544.5; /// The Ephemeris Time epoch, in seconds pub const ET_EPOCH_S: i64 = 3_155_716_800; /// Modified Julian Date in seconds as defined [here](http://tycho.usno.navy.mil/mjd.html). MJD epoch is Modified Julian Day at 17 November 1858 at midnight. pub const MJD_OFFSET: f64 = 2_400_000.5; -/// The JDE offset in days -pub const JDE_OFFSET_DAYS: f64 = J1900_OFFSET + MJD_OFFSET; -/// The JDE offset in seconds -pub const JDE_OFFSET_SECONDS: f64 = JDE_OFFSET_DAYS * SECONDS_PER_DAY; /// `DAYS_PER_YEAR` corresponds to the number of days per year in the Julian calendar. pub const DAYS_PER_YEAR: f64 = 365.25; /// `DAYS_PER_YEAR_NLD` corresponds to the number of days per year **without leap days**. @@ -55,12 +48,6 @@ pub const SECONDS_PER_TROPICAL_YEAR: f64 = 31_556_925.974_7; /// `SECONDS_PER_SIDEREAL_YEAR` corresponds to the number of seconds per sidereal year from [NIST](https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors/nist-guide-si-appendix-b9#TIME). pub const SECONDS_PER_SIDEREAL_YEAR: f64 = 31_558_150.0; -/// The duration between J2000 and J1900 is exactly one century, both references start at noon. -pub const J2000_TO_J1900_DURATION: Duration = Duration { - centuries: 1, - nanoseconds: 0, -}; - // Epoch formatting module is called `efmt` to avoid collision with `std::fmt` and `core::fmt`. pub mod efmt; mod parser; diff --git a/tests/epoch.rs b/tests/epoch.rs index 02300b9..955f1ef 100644 --- a/tests/epoch.rs +++ b/tests/epoch.rs @@ -4,9 +4,8 @@ extern crate core; use hifitime::{ is_gregorian_valid, Duration, Epoch, EpochError, ParsingError, TimeScale, TimeUnits, Unit, Weekday, BDT_REF_EPOCH, DAYS_GPS_TAI_OFFSET, DAYS_PER_YEAR, GPST_REF_EPOCH, GST_REF_EPOCH, - J1900_OFFSET, J1900_REF_EPOCH, J2000_OFFSET, J2000_REF_EPOCH, J2000_TO_J1900_DURATION, - MJD_OFFSET, SECONDS_BDT_TAI_OFFSET, SECONDS_GPS_TAI_OFFSET, SECONDS_GST_TAI_OFFSET, - SECONDS_PER_DAY, + J1900_REF_EPOCH, J2000_REF_EPOCH, JD_J2000, MJD_J1900, MJD_J2000, MJD_OFFSET, + SECONDS_BDT_TAI_OFFSET, SECONDS_GPS_TAI_OFFSET, SECONDS_GST_TAI_OFFSET, SECONDS_PER_DAY, }; use hifitime::efmt::{Format, Formatter}; @@ -25,15 +24,15 @@ fn test_const_ops() { // Tests that multiplying a constant with a unit returns the correct number in that same unit let mjd_offset = MJD_OFFSET * Unit::Day; assert!((mjd_offset.to_unit(Unit::Day) - MJD_OFFSET).abs() < f64::EPSILON); - let j2000_offset = J2000_OFFSET * Unit::Day; - assert!((j2000_offset.to_unit(Unit::Day) - J2000_OFFSET).abs() < f64::EPSILON); + let j2000_mjd = MJD_J2000 * Unit::Day; + assert!((j2000_mjd.to_unit(Unit::Day) - MJD_J2000).abs() < f64::EPSILON); } #[allow(clippy::float_equality_without_abs)] #[test] fn utc_epochs() { - assert!(Epoch::from_mjd_tai(J1900_OFFSET).to_tai_seconds() < EPSILON); - assert!((Epoch::from_mjd_tai(J1900_OFFSET).to_mjd_tai_days() - J1900_OFFSET).abs() < EPSILON); + assert!(Epoch::from_mjd_tai(MJD_J1900).to_tai_seconds() < EPSILON); + assert!((Epoch::from_mjd_tai(MJD_J1900).to_mjd_tai_days() - MJD_J1900).abs() < EPSILON); // Tests are chronological dates. // All of the following examples are cross validated against NASA HEASARC, @@ -422,11 +421,6 @@ fn gpst() { GPST_REF_EPOCH.duration ); - // assert_eq!( - // GPST_REF_EPOCH.to_utc_seconds(), - // Epoch::from_gregorian_utc_at_midnight(1980, 1, 6).to_tai_seconds() - // ); - assert!( GPST_REF_EPOCH.to_gpst_seconds().abs() < EPSILON, "The number of seconds from the GPS epoch was not 0: {}", @@ -1918,7 +1912,7 @@ fn test_to_tai_time_scale() { let j2000_ref = J2000_REF_EPOCH; assert_eq!(j2000_ref, j2000_ref.to_time_scale(TimeScale::TAI)); let j2000_to_j1900 = j2000_ref - j1900_ref; - assert_eq!(j2000_to_j1900, J2000_TO_J1900_DURATION); + assert_eq!(j2000_to_j1900, Duration::from_parts(1, 0)); } #[cfg(feature = "std")] @@ -2044,3 +2038,28 @@ fn regression_test_gh_209() { let t = Epoch::from_time_of_week(1982, 604800000000000 - 19000000000, TimeScale::GPST); println!("{t}"); } + +#[cfg(feature = "std")] +#[test] +fn regression_test_gh_282() { + assert_eq!( + "1900-01-01T00:00:00 UTC", + format!("{}", Epoch::from_utc_duration(0_f64.nanoseconds())) + ); + // NOTE: Hifitime's prime epoch is midnight on 1900-01-01, but the Julian date J1900 is at noon. + // This explains why the calculation returns `0.5` with a zero duration UTC epoch. + assert_eq!( + "2415020.5", + format!( + "{}", + Epoch::from_utc_duration(0_f64.nanoseconds()).to_jde_utc_days() + ) + ); + assert_eq!( + "2000-01-01T12:00:00 TAI", + format!( + "{}", + Epoch::from_jde_tai(JD_J2000).to_gregorian_str(hifitime::TimeScale::TAI) + ) + ); +}