Coverage Report

Created: 2026-09-04 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/hifitime-4.3.1/src/epoch/initializers.rs
Line
Count
Source
1
/*
2
* Hifitime
3
* Copyright (C) 2017-onward Christopher Rabotin <christopher.rabotin@gmail.com> et al. (cf. https://github.com/nyx-space/hifitime/graphs/contributors)
4
* This Source Code Form is subject to the terms of the Mozilla Public
5
* License, v. 2.0. If a copy of the MPL was not distributed with this
6
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
7
*
8
* Documentation: https://nyxspace.com/
9
*/
10
11
use core::str::FromStr;
12
13
use snafu::ResultExt;
14
15
use crate::{
16
    efmt::Format, errors::ParseSnafu, Duration, Epoch, HifitimeError, TimeScale, Unit, Weekday,
17
    ET_OFFSET_US, MJD_J1900, MJD_OFFSET, NANOSECONDS_PER_DAY, UNIX_REF_EPOCH,
18
};
19
20
// Defines the methods that should be classmethods in Python, but must be redefined as per https://github.com/PyO3/pyo3/issues/1003#issuecomment-844433346
21
impl Epoch {
22
    #[must_use]
23
    /// Creates a new Epoch from a Duration as the time difference between this epoch and TAI reference epoch.
24
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TAI))]
25
243
    pub const fn from_tai_duration(duration: Duration) -> Self {
26
243
        Self {
27
243
            duration,
28
243
            time_scale: TimeScale::TAI,
29
243
        }
30
243
    }
31
32
0
    pub fn to_duration_since_j1900(&self) -> Duration {
33
0
        self.to_time_scale(TimeScale::TAI).duration
34
0
    }
35
36
    #[must_use]
37
    /// Creates a new Epoch from its centuries and nanosecond since the TAI reference epoch.
38
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TAI))]
39
0
    pub fn from_tai_parts(centuries: i16, nanoseconds: u64) -> Self {
40
0
        Self::from_tai_duration(Duration::from_parts(centuries, nanoseconds))
41
0
    }
42
43
    #[must_use]
44
    /// Initialize an Epoch from the provided TAI seconds since 1900 January 01 at midnight
45
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TAI))]
46
    #[cfg_attr(kani, kani::requires(seconds.is_finite()))]
47
243
    pub fn from_tai_seconds(seconds: f64) -> Self {
48
243
        Self::from_tai_duration(seconds * Unit::Second)
49
243
    }
50
51
    #[must_use]
52
    /// Initialize an Epoch from the provided TAI days since 1900 January 01 at midnight
53
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TAI))]
54
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
55
0
    pub fn from_tai_days(days: f64) -> Self {
56
0
        Self::from_tai_duration(days * Unit::Day)
57
0
    }
58
59
    #[must_use]
60
    /// Initialize an Epoch from the provided UTC seconds since 1900 January 01 at midnight
61
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::UTC))]
62
26.9k
    pub fn from_utc_duration(duration: Duration) -> Self {
63
26.9k
        Self::from_duration(duration, TimeScale::UTC)
64
26.9k
    }
65
66
    #[must_use]
67
    /// Initialize an Epoch from the provided UTC seconds since 1900 January 01 at midnight
68
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::UTC))]
69
    #[cfg_attr(kani, kani::requires(seconds.is_finite()))]
70
3.07k
    pub fn from_utc_seconds(seconds: f64) -> Self {
71
3.07k
        Self::from_utc_duration(seconds * Unit::Second)
72
3.07k
    }
73
74
    #[must_use]
75
    /// Initialize an Epoch from the provided UTC days since 1900 January 01 at midnight
76
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::UTC))]
77
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
78
0
    pub fn from_utc_days(days: f64) -> Self {
79
0
        Self::from_utc_duration(days * Unit::Day)
80
0
    }
81
82
    #[must_use]
83
    /// Initialize an Epoch from the provided duration since 1980 January 6 at midnight
84
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GPST))]
85
0
    pub fn from_gpst_duration(duration: Duration) -> Self {
86
0
        Self::from_duration(duration, TimeScale::GPST)
87
0
    }
88
89
    #[must_use]
90
    /// Initialize an Epoch from the provided duration since 1980 January 6 at midnight
91
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::QZSST))]
92
0
    pub fn from_qzsst_duration(duration: Duration) -> Self {
93
0
        Self::from_duration(duration, TimeScale::QZSST)
94
0
    }
95
96
    #[must_use]
97
    /// Initialize an Epoch from the provided duration since August 21st 1999 midnight
98
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GST))]
99
0
    pub fn from_gst_duration(duration: Duration) -> Self {
100
0
        Self::from_duration(duration, TimeScale::GST)
101
0
    }
102
103
    #[must_use]
104
    /// Initialize an Epoch from the provided duration since January 1st midnight
105
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::BDT))]
106
0
    pub fn from_bdt_duration(duration: Duration) -> Self {
107
0
        Self::from_duration(duration, TimeScale::BDT)
108
0
    }
109
110
    #[must_use]
111
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TAI))]
112
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
113
205
    pub fn from_mjd_tai(days: f64) -> Self {
114
205
        Self::from_mjd_in_time_scale(days, TimeScale::TAI)
115
205
    }
116
117
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == time_scale))]
118
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
119
1.66k
    pub fn from_mjd_in_time_scale(days: f64, time_scale: TimeScale) -> Self {
120
1.66k
        Self {
121
1.66k
            duration: (days - MJD_J1900) * Unit::Day,
122
1.66k
            time_scale,
123
1.66k
        }
124
1.66k
    }
125
126
    #[must_use]
127
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::UTC))]
128
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
129
0
    pub fn from_mjd_utc(days: f64) -> Self {
130
0
        Self::from_mjd_in_time_scale(days, TimeScale::UTC)
131
0
    }
132
    #[must_use]
133
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GPST))]
134
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
135
0
    pub fn from_mjd_gpst(days: f64) -> Self {
136
0
        Self::from_mjd_in_time_scale(days, TimeScale::GPST)
137
0
    }
138
    #[must_use]
139
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::QZSST))]
140
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
141
0
    pub fn from_mjd_qzsst(days: f64) -> Self {
142
0
        Self::from_mjd_in_time_scale(days, TimeScale::QZSST)
143
0
    }
144
    #[must_use]
145
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GST))]
146
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
147
0
    pub fn from_mjd_gst(days: f64) -> Self {
148
0
        Self::from_mjd_in_time_scale(days, TimeScale::GST)
149
0
    }
150
    #[must_use]
151
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::BDT))]
152
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
153
0
    pub fn from_mjd_bdt(days: f64) -> Self {
154
0
        Self::from_mjd_in_time_scale(days, TimeScale::BDT)
155
0
    }
156
157
    #[must_use]
158
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TAI))]
159
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
160
982
    pub fn from_jde_tai(days: f64) -> Self {
161
982
        Self::from_jde_in_time_scale(days, TimeScale::TAI)
162
982
    }
163
164
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == time_scale))]
165
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
166
1.45k
    pub fn from_jde_in_time_scale(days: f64, time_scale: TimeScale) -> Self {
167
1.45k
        Self {
168
1.45k
            duration: (days - MJD_J1900 - MJD_OFFSET) * Unit::Day - time_scale.prime_epoch_offset(),
169
1.45k
            time_scale,
170
1.45k
        }
171
1.45k
    }
172
173
    #[must_use]
174
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::UTC))]
175
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
176
476
    pub fn from_jde_utc(days: f64) -> Self {
177
476
        Self::from_jde_in_time_scale(days, TimeScale::UTC)
178
476
    }
179
    #[must_use]
180
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GPST))]
181
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
182
0
    pub fn from_jde_gpst(days: f64) -> Self {
183
0
        Self::from_jde_in_time_scale(days, TimeScale::GPST)
184
0
    }
185
    #[must_use]
186
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::QZSST))]
187
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
188
0
    pub fn from_jde_qzsst(days: f64) -> Self {
189
0
        Self::from_jde_in_time_scale(days, TimeScale::QZSST)
190
0
    }
191
    #[must_use]
192
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GST))]
193
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
194
0
    pub fn from_jde_gst(days: f64) -> Self {
195
0
        Self::from_jde_in_time_scale(days, TimeScale::GST)
196
0
    }
197
    #[must_use]
198
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::BDT))]
199
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
200
0
    pub fn from_jde_bdt(days: f64) -> Self {
201
0
        Self::from_jde_in_time_scale(days, TimeScale::BDT)
202
0
    }
203
204
    #[must_use]
205
    /// Initialize an Epoch from the provided TT seconds (approximated to 32.184s delta from TAI)
206
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TT))]
207
    #[cfg_attr(kani, kani::requires(seconds.is_finite()))]
208
37
    pub fn from_tt_seconds(seconds: f64) -> Self {
209
37
        Self::from_tt_duration(seconds * Unit::Second)
210
37
    }
211
212
    #[must_use]
213
    /// Initialize an Epoch from the provided TT seconds (approximated to 32.184s delta from TAI)
214
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TT))]
215
37
    pub fn from_tt_duration(duration: Duration) -> Self {
216
37
        Self::from_duration(duration, TimeScale::TT)
217
37
    }
218
219
    #[must_use]
220
    /// Initialize an Epoch from the Ephemeris Time seconds past 2000 JAN 01 (J2000 reference)
221
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::ET))]
222
    #[cfg_attr(kani, kani::requires(seconds_since_j2000.is_finite()))]
223
156k
    pub fn from_et_seconds(seconds_since_j2000: f64) -> Epoch {
224
156k
        Self::from_et_duration(seconds_since_j2000 * Unit::Second)
225
156k
    }
226
227
    /// Initializes an Epoch from the duration between J2000 and the current epoch as per NAIF SPICE.
228
    ///
229
    /// # Limitation
230
    /// This method uses a Newton Raphson iteration to find the appropriate TAI duration. This method is only accuracy to a few nanoseconds.
231
    /// Hence, when calling `as_et_duration()` and re-initializing it with `from_et_duration` you may have a few nanoseconds of difference (expect less than 10 ns).
232
    ///
233
    /// # Warning
234
    /// The et2utc function of NAIF SPICE will assume that there are 9 leap seconds before 01 JAN 1972,
235
    /// as this date introduces 10 leap seconds. At the time of writing, this does _not_ seem to be in
236
    /// line with IERS and the documentation in the leap seconds list.
237
    ///
238
    /// In order to match SPICE, the as_et_duration() function will manually get rid of that difference.
239
    #[must_use]
240
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::ET))]
241
156k
    pub fn from_et_duration(duration_since_j2000: Duration) -> Self {
242
156k
        Self::from_duration(duration_since_j2000, TimeScale::ET)
243
156k
    }
244
245
    #[must_use]
246
    /// Initialize an Epoch from Dynamic Barycentric Time (TDB) seconds past 2000 JAN 01 midnight (difference than SPICE)
247
    /// NOTE: This uses the ESA algorithm, which is a notch more complicated than the SPICE algorithm, but more precise.
248
    /// In fact, SPICE algorithm is precise +/- 30 microseconds for a century whereas ESA algorithm should be exactly correct.
249
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TDB))]
250
    #[cfg_attr(kani, kani::requires(seconds_j2000.is_finite()))]
251
110
    pub fn from_tdb_seconds(seconds_j2000: f64) -> Epoch {
252
110
        Self::from_tdb_duration(seconds_j2000 * Unit::Second)
253
110
    }
254
255
    #[must_use]
256
    /// Initialize from Dynamic Barycentric Time (TDB) (same as SPICE ephemeris time) whose epoch is 2000 JAN 01 noon TAI.
257
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TDB))]
258
110
    pub fn from_tdb_duration(duration_since_j2000: Duration) -> Epoch {
259
110
        Self::from_duration(duration_since_j2000, TimeScale::TDB)
260
110
    }
261
262
    #[must_use]
263
    /// Initialize from the JDE days
264
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
265
31
    pub fn from_jde_et(days: f64) -> Self {
266
31
        Self::from_jde_tdb(days)
267
31
    }
268
269
    #[must_use]
270
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
271
    /// Initialize from Dynamic Barycentric Time (TDB) (same as SPICE ephemeris time) in JD days
272
643
    pub fn from_jde_tdb(days: f64) -> Self {
273
643
        Self::from_jde_tai(days) - Unit::Microsecond * ET_OFFSET_US
274
643
    }
275
276
    #[must_use]
277
    /// Initialize an Epoch from the number of seconds since the GPS Time Epoch,
278
    /// defined as UTC midnight of January 5th to 6th 1980 (cf. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29>).
279
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GPST))]
280
    #[cfg_attr(kani, kani::requires(seconds.is_finite()))]
281
0
    pub fn from_gpst_seconds(seconds: f64) -> Self {
282
0
        Self::from_duration(seconds * Unit::Second, TimeScale::GPST)
283
0
    }
284
285
    #[must_use]
286
    /// Initialize an Epoch from the number of days since the GPS Time Epoch,
287
    /// defined as UTC midnight of January 5th to 6th 1980 (cf. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29>).
288
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GPST))]
289
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
290
0
    pub fn from_gpst_days(days: f64) -> Self {
291
0
        Self::from_duration(days * Unit::Day, TimeScale::GPST)
292
0
    }
293
294
    #[must_use]
295
    /// Initialize an Epoch from the number of nanoseconds since the GPS Time Epoch,
296
    /// defined as UTC midnight of January 5th to 6th 1980 (cf. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29>).
297
    /// This may be useful for time keeping devices that use GPS as a time source.
298
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GPST))]
299
0
    pub fn from_gpst_nanoseconds(nanoseconds: u64) -> Self {
300
0
        Self::from_duration(Duration::from_parts(0, nanoseconds), TimeScale::GPST)
301
0
    }
302
303
    #[must_use]
304
    /// Initialize an Epoch from the number of seconds since the QZSS Time Epoch,
305
    /// defined as UTC midnight of January 5th to 6th 1980 (cf. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29>).
306
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::QZSST))]
307
    #[cfg_attr(kani, kani::requires(seconds.is_finite()))]
308
0
    pub fn from_qzsst_seconds(seconds: f64) -> Self {
309
0
        Self::from_duration(seconds * Unit::Second, TimeScale::QZSST)
310
0
    }
311
312
    #[must_use]
313
    /// Initialize an Epoch from the number of days since the QZSS Time Epoch,
314
    /// defined as UTC midnight of January 5th to 6th 1980 (cf. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29>).
315
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::QZSST))]
316
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
317
0
    pub fn from_qzsst_days(days: f64) -> Self {
318
0
        Self::from_duration(days * Unit::Day, TimeScale::QZSST)
319
0
    }
320
321
    #[must_use]
322
    /// Initialize an Epoch from the number of nanoseconds since the QZSS Time Epoch,
323
    /// defined as UTC midnight of January 5th to 6th 1980 (cf. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29>).
324
    /// This may be useful for time keeping devices that use QZSS as a time source.
325
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::QZSST))]
326
0
    pub fn from_qzsst_nanoseconds(nanoseconds: u64) -> Self {
327
0
        Self::from_duration(Duration::from_parts(0, nanoseconds), TimeScale::QZSST)
328
0
    }
329
330
    #[must_use]
331
    /// Initialize an Epoch from the number of seconds since the GST Time Epoch,
332
    /// starting August 21st 1999 midnight (UTC)
333
    /// (cf. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS>).
334
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GST))]
335
    #[cfg_attr(kani, kani::requires(seconds.is_finite()))]
336
0
    pub fn from_gst_seconds(seconds: f64) -> Self {
337
0
        Self::from_duration(seconds * Unit::Second, TimeScale::GST)
338
0
    }
339
340
    #[must_use]
341
    /// Initialize an Epoch from the number of days since the GST Time Epoch,
342
    /// starting August 21st 1999 midnight (UTC)
343
    /// (cf. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS>)
344
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GST))]
345
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
346
0
    pub fn from_gst_days(days: f64) -> Self {
347
0
        Self::from_duration(days * Unit::Day, TimeScale::GST)
348
0
    }
349
350
    #[must_use]
351
    /// Initialize an Epoch from the number of nanoseconds since the GPS Time Epoch,
352
    /// starting August 21st 1999 midnight (UTC)
353
    /// (cf. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS>)
354
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::GST))]
355
0
    pub fn from_gst_nanoseconds(nanoseconds: u64) -> Self {
356
0
        Self::from_duration(Duration::from_parts(0, nanoseconds), TimeScale::GST)
357
0
    }
358
359
    #[must_use]
360
    /// Initialize an Epoch from the number of seconds since the BDT Time Epoch,
361
    /// starting on January 1st 2006 (cf. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS>)
362
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::BDT))]
363
    #[cfg_attr(kani, kani::requires(seconds.is_finite()))]
364
0
    pub fn from_bdt_seconds(seconds: f64) -> Self {
365
0
        Self::from_duration(seconds * Unit::Second, TimeScale::BDT)
366
0
    }
367
368
    #[must_use]
369
    /// Initialize an Epoch from the number of days since the BDT Time Epoch,
370
    /// starting on January 1st 2006 (cf. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS>)
371
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::BDT))]
372
    #[cfg_attr(kani, kani::requires(days.is_finite()))]
373
0
    pub fn from_bdt_days(days: f64) -> Self {
374
0
        Self::from_duration(days * Unit::Day, TimeScale::BDT)
375
0
    }
376
377
    #[must_use]
378
    /// Initialize an Epoch from the number of nanoseconds since the BDT Time Epoch,
379
    /// starting on January 1st 2006 (cf. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS>).
380
    /// This may be useful for time keeping devices that use BDT as a time source.
381
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::BDT))]
382
0
    pub fn from_bdt_nanoseconds(nanoseconds: u64) -> Self {
383
0
        Self::from_duration(Duration::from_parts(0, nanoseconds), TimeScale::BDT)
384
0
    }
385
386
    #[must_use]
387
    /// Initialize an Epoch from the provided IEEE 1588-2008 (PTPv2) duration since TAI midnight 1970 January 01.
388
    /// PTP uses the TAI timescale but with the Unix Epoch for compatibility with unix systems.
389
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TAI))]
390
0
    pub fn from_ptp_duration(duration: Duration) -> Self {
391
0
        Self::from_duration(UNIX_REF_EPOCH.to_utc_duration() + duration, TimeScale::TAI)
392
0
    }
393
394
    #[must_use]
395
    /// Initialize an Epoch from the provided IEEE 1588-2008 (PTPv2) second timestamp since TAI midnight 1970 January 01.
396
    /// PTP uses the TAI timescale but with the Unix Epoch for compatibility with unix systems.
397
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TAI))]
398
    #[cfg_attr(kani, kani::requires(seconds.is_finite()))]
399
0
    pub fn from_ptp_seconds(seconds: f64) -> Self {
400
0
        Self::from_ptp_duration(seconds * Unit::Second)
401
0
    }
402
403
    #[must_use]
404
    /// Initialize an Epoch from the provided IEEE 1588-2008 (PTPv2) nanoseconds timestamp since TAI midnight 1970 January 01.
405
    /// PTP uses the TAI timescale but with the Unix Epoch for compatibility with unix systems.
406
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::TAI))]
407
0
    pub fn from_ptp_nanoseconds(nanoseconds: u64) -> Self {
408
0
        Self::from_ptp_duration(Duration::from_parts(0, nanoseconds))
409
0
    }
410
411
    #[must_use]
412
    /// Initialize an Epoch from the provided duration since UTC midnight 1970 January 01.
413
23.9k
    pub fn from_unix_duration(duration: Duration) -> Self {
414
23.9k
        Self::from_utc_duration(UNIX_REF_EPOCH.to_utc_duration() + duration)
415
23.9k
    }
416
417
    #[must_use]
418
    /// Initialize an Epoch from the provided UNIX second timestamp since UTC midnight 1970 January 01.
419
    #[cfg_attr(kani, kani::requires(seconds.is_finite()))]
420
0
    pub fn from_unix_seconds(seconds: f64) -> Self {
421
0
        Self::from_utc_duration(UNIX_REF_EPOCH.to_utc_duration() + seconds * Unit::Second)
422
0
    }
423
    #[must_use]
424
    /// Initialize an Epoch from the provided UNIX millisecond timestamp since UTC midnight 1970 January 01.
425
    #[cfg_attr(kani, kani::requires(millisecond.is_finite()))]
426
0
    pub fn from_unix_milliseconds(millisecond: f64) -> Self {
427
0
        Self::from_utc_duration(UNIX_REF_EPOCH.to_utc_duration() + millisecond * Unit::Millisecond)
428
0
    }
429
430
    /// Initializes an Epoch from the provided Format.
431
0
    pub fn from_str_with_format(s_in: &str, format: Format) -> Result<Self, HifitimeError> {
432
0
        format.parse(s_in)
433
0
    }
434
435
    /// Initializes an Epoch from the Format as a string.
436
0
    pub fn from_format_str(s_in: &str, format_str: &str) -> Result<Self, HifitimeError> {
437
0
        Format::from_str(format_str)
438
0
            .with_context(|_| ParseSnafu {
439
                details: "when using format string",
440
0
            })?
441
0
            .parse(s_in)
442
0
    }
443
444
    /// Builds an Epoch from given `week`: elapsed weeks counter into the desired Time scale, and the amount of nanoseconds within that week.
445
    /// For example, this is how GPS vehicles describe a GPST epoch.
446
    ///
447
    /// Note that this constructor relies on 128 bit integer math and may be slow on embedded devices.
448
    #[must_use]
449
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == time_scale))]
450
0
    pub fn from_time_of_week(week: u32, nanoseconds: u64, time_scale: TimeScale) -> Self {
451
0
        let mut nanos = i128::from(nanoseconds);
452
0
        nanos += i128::from(week) * Weekday::DAYS_PER_WEEK_I128 * i128::from(NANOSECONDS_PER_DAY);
453
0
        let duration = Duration::from_total_nanoseconds(nanos);
454
0
        Self::from_duration(duration, time_scale)
455
0
    }
456
457
    #[must_use]
458
    /// Builds a UTC Epoch from given `week`: elapsed weeks counter and "ns" amount of nanoseconds since closest Sunday Midnight.
459
    #[cfg_attr(kani, kani::ensures(|result| result.time_scale == crate::TimeScale::UTC))]
460
0
    pub fn from_time_of_week_utc(week: u32, nanoseconds: u64) -> Self {
461
0
        Self::from_time_of_week(week, nanoseconds, TimeScale::UTC)
462
0
    }
463
464
    #[must_use]
465
    /// Builds an Epoch from the provided year, days in the year, and a time scale.
466
    ///
467
    /// # Limitations
468
    /// In the TDB or ET time scales, there may be an error of up to 750 nanoseconds when initializing an Epoch this way.
469
    /// This is because we first initialize the epoch in Gregorian scale and then apply the TDB/ET offset, but that offset actually depends on the precise time.
470
    ///
471
    /// # Day couting behavior
472
    ///
473
    /// The day counter starts at 01, in other words, 01 January is day 1 of the counter, as per the GPS specificiations.
474
    ///
475
0
    pub fn from_day_of_year(year: i32, days: f64, time_scale: TimeScale) -> Self {
476
0
        let start_of_year = Self::from_gregorian(year, 1, 1, 0, 0, 0, 0, time_scale);
477
0
        start_of_year + (days - 1.0) * Unit::Day
478
0
    }
479
}