Skip to content

Commit

Permalink
Add to_time_scale_with_leap_seconds which converts between time sca…
Browse files Browse the repository at this point in the history
…les given a specific leap second provider (passed as reference)

Also reproduce #255 bug with hifitime v4
  • Loading branch information
ChristopherRabotin committed Jun 19, 2024
1 parent 84b8e61 commit b3a70df
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 4 deletions.
30 changes: 26 additions & 4 deletions src/epoch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,15 @@ impl<'de> Deserialize<'de> for Epoch {
// Defines the methods that should be classmethods in Python, but must be redefined as per https://github.com/PyO3/pyo3/issues/1003#issuecomment-844433346
impl Epoch {
#[must_use]
/// Converts self to another time scale
/// Converts self to another time scale, using the leap second provider for any conversion involving leap seconds
///
/// As per the [Rust naming convention](https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv),
/// this borrows an Epoch and returns an owned Epoch.
pub fn to_time_scale(&self, ts: TimeScale) -> Self {
pub fn to_time_scale_with_leap_seconds<L: LeapSecondProvider>(
&self,
ts: TimeScale,
provider: &L,
) -> Self {
if ts == self.time_scale {
// Do nothing, just return a copy
*self
Expand Down Expand Up @@ -159,7 +163,11 @@ impl Epoch {
// Assume this is TAI
let mut tai_assumption = *self;
tai_assumption.time_scale = TimeScale::TAI;
self.duration + tai_assumption.leap_seconds(true).unwrap_or(0.0).seconds()
self.duration
+ tai_assumption
.leap_seconds_with(true, provider)
.unwrap_or(0.0)
.seconds()
}
TimeScale::GPST => self.duration + GPST_REF_EPOCH.to_tai_duration(),
TimeScale::GST => self.duration + GST_REF_EPOCH.to_tai_duration(),
Expand Down Expand Up @@ -221,7 +229,11 @@ impl Epoch {
time_scale: TimeScale::TAI,
};
// TAI = UTC + leap_seconds <=> UTC = TAI - leap_seconds
prime_epoch_offset - epoch.leap_seconds(true).unwrap_or(0.0).seconds()
prime_epoch_offset
- epoch
.leap_seconds_with(true, provider)
.unwrap_or(0.0)
.seconds()
}
TimeScale::GPST => prime_epoch_offset - GPST_REF_EPOCH.to_tai_duration(),
TimeScale::GST => prime_epoch_offset - GST_REF_EPOCH.to_tai_duration(),
Expand All @@ -236,6 +248,16 @@ impl Epoch {
}
}

#[must_use]
/// Converts self to another time scale
///
/// Note that this is a shortcut to to_time_scale_with_leap_seconds with the LatestLeapSeconds.
/// As per the [Rust naming convention](https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv),
/// this borrows an Epoch and returns an owned Epoch.
pub fn to_time_scale(&self, ts: TimeScale) -> Self {
self.to_time_scale_with_leap_seconds(ts, &LatestLeapSeconds::default())
}

#[must_use]
/// Creates a new Epoch from a Duration as the time difference between this epoch and TAI reference epoch.
pub const fn from_tai_duration(duration: Duration) -> Self {
Expand Down
100 changes: 100 additions & 0 deletions tests/epoch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2102,3 +2102,103 @@ fn regression_test_gh_288() {
format!("{}", epoch.to_isoformat())
);
}

#[cfg(feature = "std")]
#[test]
fn regression_test_gh_255() {
use hifitime::leap_seconds::{LeapSecond, LeapSecondProvider};

#[derive(Clone)]
struct LeapTable {
table: Vec<LeapSecond>,
}

impl LeapSecondProvider for LeapTable {
fn entries(&self) -> &[LeapSecond] {
&self.table
}
}

let leap_insert = LeapTable {
table: vec![
LeapSecond::new(50.0, 3.0, true),
LeapSecond::new(100.0, 4.0, true),
],
};
let leap_delete = LeapTable {
table: vec![
LeapSecond::new(50.0, 3.0, true),
LeapSecond::new(100.0, 2.0, true),
],
};

println!();
println!("Insert leap second @ 100, from +3 to +4");
println!("UTC -> TAI -> UTC");
for i in 96..=102 {
let time = Epoch::from_utc_seconds(i as f64);
let tai = time.to_time_scale_with_leap_seconds(TimeScale::TAI, &leap_insert);
let tai_s = tai.duration.to_seconds().round() as i64;
let utc = tai.to_time_scale_with_leap_seconds(TimeScale::UTC, &leap_insert);
let utc_s = utc.duration.to_seconds().round() as i64;
println!(
"{:3} -> {:3} -> {:3}, delta = {}",
i,
tai_s,
utc_s,
utc_s - i
);
if i == 99 {
let time = Epoch::from_tai_seconds(103.0);
let utc = time.to_time_scale_with_leap_seconds(TimeScale::UTC, &leap_insert);
let utc_s = utc.duration.to_seconds().round() as i64;
println!(" -> {:3} -> {:3}, delta = {}", 103, utc_s, 0);
}
}

println!();
println!("Delete leap second @ 100, from +3 to +2");
println!("UTC -> TAI -> UTC");
for i in 96..=102 {
let time = Epoch::from_utc_seconds(i as f64);
let tai = time.to_time_scale_with_leap_seconds(TimeScale::TAI, &leap_delete);
let tai_s = tai.duration.to_seconds().round() as i64;
let utc = tai.to_time_scale_with_leap_seconds(TimeScale::UTC, &leap_delete);
let utc_s = utc.duration.to_seconds().round() as i64;
println!(
"{:3} -> {:3} -> {:3}, delta = {}",
i,
tai_s,
utc_s,
utc_s - i
);
}

// println!();
// println!("=== Proposed fix ===");

// println!();
// println!("Insert leap second @ 100, from +3 to +4");
// println!("UTC -> TAI -> UTC");
// for i in 96..=102 {
// let time = from_utc_seconds(i as f64, leap_insert.clone());
// let tai = time.to_tai_seconds().round() as i64;
// let utc = fixed_to_utc(&time, leap_insert.clone()).round() as i64;
// println!("{:3} -> {:3} -> {:3}, delta = {}", i, tai, utc, utc - i);
// if i == 99 {
// let time = Epoch::from_tai_seconds(103.0);
// let utc = to_utc_seconds(&time, leap_insert.clone()).round() as i64;
// println!(" -> {:3} -> {:3}, delta = {}", 103, utc, 0);
// }
// }

// println!();
// println!("Delete leap second @ 100, from +3 to +2");
// println!("UTC -> TAI -> UTC");
// for i in 96..=102 {
// let time = from_utc_seconds(i as f64, leap_delete.clone());
// let tai = time.to_tai_seconds().round() as i64;
// let utc = fixed_to_utc(&time, leap_delete.clone()).round() as i64;
// println!("{:3} -> {:3} -> {:3}, delta = {}", i, tai, utc, utc - i);
// }
}

0 comments on commit b3a70df

Please sign in to comment.