Skip to content

Commit b3a70df

Browse files
Add to_time_scale_with_leap_seconds which converts between time scales given a specific leap second provider (passed as reference)
Also reproduce #255 bug with hifitime v4
1 parent 84b8e61 commit b3a70df

File tree

2 files changed

+126
-4
lines changed

2 files changed

+126
-4
lines changed

src/epoch/mod.rs

+26-4
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,15 @@ impl<'de> Deserialize<'de> for Epoch {
113113
// Defines the methods that should be classmethods in Python, but must be redefined as per https://github.com/PyO3/pyo3/issues/1003#issuecomment-844433346
114114
impl Epoch {
115115
#[must_use]
116-
/// Converts self to another time scale
116+
/// Converts self to another time scale, using the leap second provider for any conversion involving leap seconds
117117
///
118118
/// 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),
119119
/// this borrows an Epoch and returns an owned Epoch.
120-
pub fn to_time_scale(&self, ts: TimeScale) -> Self {
120+
pub fn to_time_scale_with_leap_seconds<L: LeapSecondProvider>(
121+
&self,
122+
ts: TimeScale,
123+
provider: &L,
124+
) -> Self {
121125
if ts == self.time_scale {
122126
// Do nothing, just return a copy
123127
*self
@@ -159,7 +163,11 @@ impl Epoch {
159163
// Assume this is TAI
160164
let mut tai_assumption = *self;
161165
tai_assumption.time_scale = TimeScale::TAI;
162-
self.duration + tai_assumption.leap_seconds(true).unwrap_or(0.0).seconds()
166+
self.duration
167+
+ tai_assumption
168+
.leap_seconds_with(true, provider)
169+
.unwrap_or(0.0)
170+
.seconds()
163171
}
164172
TimeScale::GPST => self.duration + GPST_REF_EPOCH.to_tai_duration(),
165173
TimeScale::GST => self.duration + GST_REF_EPOCH.to_tai_duration(),
@@ -221,7 +229,11 @@ impl Epoch {
221229
time_scale: TimeScale::TAI,
222230
};
223231
// TAI = UTC + leap_seconds <=> UTC = TAI - leap_seconds
224-
prime_epoch_offset - epoch.leap_seconds(true).unwrap_or(0.0).seconds()
232+
prime_epoch_offset
233+
- epoch
234+
.leap_seconds_with(true, provider)
235+
.unwrap_or(0.0)
236+
.seconds()
225237
}
226238
TimeScale::GPST => prime_epoch_offset - GPST_REF_EPOCH.to_tai_duration(),
227239
TimeScale::GST => prime_epoch_offset - GST_REF_EPOCH.to_tai_duration(),
@@ -236,6 +248,16 @@ impl Epoch {
236248
}
237249
}
238250

251+
#[must_use]
252+
/// Converts self to another time scale
253+
///
254+
/// Note that this is a shortcut to to_time_scale_with_leap_seconds with the LatestLeapSeconds.
255+
/// 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),
256+
/// this borrows an Epoch and returns an owned Epoch.
257+
pub fn to_time_scale(&self, ts: TimeScale) -> Self {
258+
self.to_time_scale_with_leap_seconds(ts, &LatestLeapSeconds::default())
259+
}
260+
239261
#[must_use]
240262
/// Creates a new Epoch from a Duration as the time difference between this epoch and TAI reference epoch.
241263
pub const fn from_tai_duration(duration: Duration) -> Self {

tests/epoch.rs

+100
Original file line numberDiff line numberDiff line change
@@ -2102,3 +2102,103 @@ fn regression_test_gh_288() {
21022102
format!("{}", epoch.to_isoformat())
21032103
);
21042104
}
2105+
2106+
#[cfg(feature = "std")]
2107+
#[test]
2108+
fn regression_test_gh_255() {
2109+
use hifitime::leap_seconds::{LeapSecond, LeapSecondProvider};
2110+
2111+
#[derive(Clone)]
2112+
struct LeapTable {
2113+
table: Vec<LeapSecond>,
2114+
}
2115+
2116+
impl LeapSecondProvider for LeapTable {
2117+
fn entries(&self) -> &[LeapSecond] {
2118+
&self.table
2119+
}
2120+
}
2121+
2122+
let leap_insert = LeapTable {
2123+
table: vec![
2124+
LeapSecond::new(50.0, 3.0, true),
2125+
LeapSecond::new(100.0, 4.0, true),
2126+
],
2127+
};
2128+
let leap_delete = LeapTable {
2129+
table: vec![
2130+
LeapSecond::new(50.0, 3.0, true),
2131+
LeapSecond::new(100.0, 2.0, true),
2132+
],
2133+
};
2134+
2135+
println!();
2136+
println!("Insert leap second @ 100, from +3 to +4");
2137+
println!("UTC -> TAI -> UTC");
2138+
for i in 96..=102 {
2139+
let time = Epoch::from_utc_seconds(i as f64);
2140+
let tai = time.to_time_scale_with_leap_seconds(TimeScale::TAI, &leap_insert);
2141+
let tai_s = tai.duration.to_seconds().round() as i64;
2142+
let utc = tai.to_time_scale_with_leap_seconds(TimeScale::UTC, &leap_insert);
2143+
let utc_s = utc.duration.to_seconds().round() as i64;
2144+
println!(
2145+
"{:3} -> {:3} -> {:3}, delta = {}",
2146+
i,
2147+
tai_s,
2148+
utc_s,
2149+
utc_s - i
2150+
);
2151+
if i == 99 {
2152+
let time = Epoch::from_tai_seconds(103.0);
2153+
let utc = time.to_time_scale_with_leap_seconds(TimeScale::UTC, &leap_insert);
2154+
let utc_s = utc.duration.to_seconds().round() as i64;
2155+
println!(" -> {:3} -> {:3}, delta = {}", 103, utc_s, 0);
2156+
}
2157+
}
2158+
2159+
println!();
2160+
println!("Delete leap second @ 100, from +3 to +2");
2161+
println!("UTC -> TAI -> UTC");
2162+
for i in 96..=102 {
2163+
let time = Epoch::from_utc_seconds(i as f64);
2164+
let tai = time.to_time_scale_with_leap_seconds(TimeScale::TAI, &leap_delete);
2165+
let tai_s = tai.duration.to_seconds().round() as i64;
2166+
let utc = tai.to_time_scale_with_leap_seconds(TimeScale::UTC, &leap_delete);
2167+
let utc_s = utc.duration.to_seconds().round() as i64;
2168+
println!(
2169+
"{:3} -> {:3} -> {:3}, delta = {}",
2170+
i,
2171+
tai_s,
2172+
utc_s,
2173+
utc_s - i
2174+
);
2175+
}
2176+
2177+
// println!();
2178+
// println!("=== Proposed fix ===");
2179+
2180+
// println!();
2181+
// println!("Insert leap second @ 100, from +3 to +4");
2182+
// println!("UTC -> TAI -> UTC");
2183+
// for i in 96..=102 {
2184+
// let time = from_utc_seconds(i as f64, leap_insert.clone());
2185+
// let tai = time.to_tai_seconds().round() as i64;
2186+
// let utc = fixed_to_utc(&time, leap_insert.clone()).round() as i64;
2187+
// println!("{:3} -> {:3} -> {:3}, delta = {}", i, tai, utc, utc - i);
2188+
// if i == 99 {
2189+
// let time = Epoch::from_tai_seconds(103.0);
2190+
// let utc = to_utc_seconds(&time, leap_insert.clone()).round() as i64;
2191+
// println!(" -> {:3} -> {:3}, delta = {}", 103, utc, 0);
2192+
// }
2193+
// }
2194+
2195+
// println!();
2196+
// println!("Delete leap second @ 100, from +3 to +2");
2197+
// println!("UTC -> TAI -> UTC");
2198+
// for i in 96..=102 {
2199+
// let time = from_utc_seconds(i as f64, leap_delete.clone());
2200+
// let tai = time.to_tai_seconds().round() as i64;
2201+
// let utc = fixed_to_utc(&time, leap_delete.clone()).round() as i64;
2202+
// println!("{:3} -> {:3} -> {:3}, delta = {}", i, tai, utc, utc - i);
2203+
// }
2204+
}

0 commit comments

Comments
 (0)