deep_time/dt/constructors.rs
1use crate::{
2 ATTOS_PER_DAY, ATTOS_PER_FS_I128, ATTOS_PER_HOUR, ATTOS_PER_MIN, ATTOS_PER_MS_I128,
3 ATTOS_PER_NS_I128, ATTOS_PER_PS_I128, ATTOS_PER_SEC_I128, ATTOS_PER_US_I128, Dt, Real,
4 SEC_PER_DAY_I64, SEC_PER_WEEK, Scale, TAI_SECS_1970_MIDNIGHT_TO_2000_NOON,
5};
6
7impl Dt {
8 /// The library’s internal reference epoch.
9 ///
10 /// - **2000-01-01 12:00:00 TAI**.
11 /// - 0 attoseconds
12 /// - The vast majority of conversion functions in the library expect the given
13 /// [`Dt`] to be an attoseconds count since this epoch.
14 pub const ZERO: Self = Self::new(0, Scale::TAI, Scale::TAI);
15
16 /// UNIX epoch.
17 ///
18 /// - 1970-01-01 00:00:00 TAI.
19 /// - Stored here on the **TAI** timescale as an offset from [`Self::ZERO`].
20 /// - -946_728_000_000_000_000_000_000_000 attoseconds
21 /// - Does not take into account historical UTC offsets from the "rubber time" era.
22 /// - The library's epoch for time scales during conversions is 2000-01-01 12:00:00.
23 pub const UNIX_EPOCH: Self = Self::new(
24 -(TAI_SECS_1970_MIDNIGHT_TO_2000_NOON as i128) * ATTOS_PER_SEC_I128,
25 Scale::TAI,
26 Scale::UTC,
27 );
28
29 /// NTP epoch.
30 ///
31 /// - 1900-01-01 00:00:00 UTC.
32 /// - Stored here on the **TAI** timescale as an offset from [`Self::ZERO`].
33 /// - -3_155_716_800_000_000_000_000_000_000 attoseconds
34 /// - The library's epoch for time scales during conversions is 2000-01-01 12:00:00.
35 pub const NTP_EPOCH: Self =
36 Self::new(-3155716800000000000000000000i128, Scale::TAI, Scale::TAI);
37
38 /// TT/TCG/TCB/TDB epoch.
39 ///
40 /// - 1977-01-01 00:00:00 TAI.
41 /// - Stored here on the **TAI** timescale as an offset from [`Self::ZERO`].
42 /// - -725_803_200_000_000_000_000_000_000 attoseconds
43 /// - The library's epoch for time scales during conversions is 2000-01-01 12:00:00.
44 pub const TAI_1977_EPOCH: Self =
45 Self::new(-725803200000000000000000000i128, Scale::TAI, Scale::TAI);
46
47 /// Chandra X-ray Center (CXC) Time epoch.
48 ///
49 /// - 1998-01-01 00:00:00 TT.
50 /// - Stored here on the **TAI** timescale as an offset from [`Self::ZERO`].
51 /// - -63_115_232_184_000_000_000_000_000_000 attoseconds
52 /// - The library's epoch for time scales during conversions is 2000-01-01 12:00:00.
53 pub const CXC_EPOCH: Self = Self::new(-63115232184000000000000000i128, Scale::TAI, Scale::TT);
54
55 /// GPS/Galileo Experiment (GALEX) Time epoch.
56 ///
57 /// - 1980-01-06 00:00:00 UTC.
58 /// - Stored here on the **TAI** timescale as an offset from [`Self::ZERO`].
59 /// - -630_763_181_000_000_000_000_000_000 attoseconds
60 /// - The library's epoch for time scales during conversions is 2000-01-01 12:00:00.
61 pub const GPS_EPOCH: Self = Self::new(-630763181000000000000000000i128, Scale::TAI, Scale::GPS);
62
63 /// Galileo System Time (GST) epoch.
64 ///
65 /// - 1999-08-22 00:00:00 GST.
66 /// - Stored here on the **TAI** timescale as an offset from [`Self::ZERO`].
67 /// - -11_447_981_000_000_000_000_000_000 attoseconds
68 /// - The library's epoch for time scales during conversions is 2000-01-01 12:00:00.
69 pub const GALILEO_EPOCH: Self =
70 Self::new(-11447981000000000000000000i128, Scale::TAI, Scale::GST);
71
72 /// BeiDou Time (BDT) epoch.
73 ///
74 /// - 2006-01-01 00:00:00 UTC.
75 /// - Stored here on the **TAI** timescale as an offset from [`Self::ZERO`].
76 /// - 189_345_633_000_000_000_000_000_000 attoseconds
77 /// - The library's epoch for time scales during conversions is 2000-01-01 12:00:00.
78 pub const BDT_EPOCH: Self = Self::new(189345633000000000000000000i128, Scale::TAI, Scale::BDT);
79
80 /// CCSDS epoch (used in CCSDS time codes such as CUC).
81 ///
82 /// - 1958-01-01 00:00:00 TAI.
83 /// - Stored here on the **TAI** timescale as an offset from [`Self::ZERO`].
84 /// - -1_325_419_200_000_000_000_000_000_000 attoseconds
85 /// - The library's epoch for time scales during conversions is 2000-01-01 12:00:00.
86 pub const CCSDS_EPOCH: Self = Self::new(
87 -1_325_419_200_000_000_000_000_000_000i128,
88 Scale::TAI,
89 Scale::TAI,
90 );
91
92 /// Maximum representable duration.
93 pub const MAX: Self = Self::new(i128::MAX, Scale::TAI, Scale::TAI);
94
95 /// Minimum (most negative) representable duration.
96 pub const MIN: Self = Self::new(i128::MIN, Scale::TAI, Scale::TAI);
97
98 /// 19 seconds.
99 pub const SEC_19: Self = Self::new(19i128 * ATTOS_PER_SEC_I128, Scale::TAI, Scale::TAI);
100
101 /// 33 seconds.
102 pub const SEC_33: Self = Self::new(33i128 * ATTOS_PER_SEC_I128, Scale::TAI, Scale::TAI);
103
104 /// 37 seconds.
105 pub const SEC_37: Self = Self::new(37i128 * ATTOS_PER_SEC_I128, Scale::TAI, Scale::TAI);
106
107 /// One days worth of attoseconds.
108 pub const ONE_DAY: Self = Self::new(
109 (SEC_PER_DAY_I64 as i128) * ATTOS_PER_SEC_I128,
110 Scale::TAI,
111 Scale::TAI,
112 );
113
114 /// Creates a new [`Dt`] from a total number of attoseconds since the librarys
115 /// epoch **2000-01-01 12:00:00 TAI**.
116 ///
117 /// Does **not** perform any time scale conversions.
118 ///
119 /// ## Examples
120 ///
121 /// ```rust
122 /// use deep_time::{Dt, Scale};
123 ///
124 /// // current scale TAI, target scale UTC
125 /// let a = Dt::new(0, Scale::TAI, Scale::UTC);
126 ///
127 /// // equivalent to direct construction
128 /// let b = Dt { attos: 0, scale: Scale::TAI, target: Scale::UTC };
129 ///
130 /// assert_eq!(a, b);
131 /// ```
132 #[inline(always)]
133 pub const fn new(attos: i128, scale: Scale, target: Scale) -> Dt {
134 Self {
135 attos,
136 scale,
137 target,
138 }
139 }
140
141 /// Creates a new [`Dt`] from a total number of seconds since the librarys
142 /// epoch **2000-01-01 12:00:00 TAI**.
143 ///
144 /// Does **not** perform any time scale conversions.
145 #[inline(always)]
146 pub const fn new_sec(sec: i128, scale: Scale, target: Scale) -> Dt {
147 Self {
148 attos: Dt::sec_to_attos(sec),
149 scale,
150 target,
151 }
152 }
153
154 /// Creates a new [`Dt`] from a total number of seconds as a float
155 /// since the librarys epoch **2000-01-01 12:00:00 TAI**.
156 ///
157 /// - Does **not** perform any time scale conversions.
158 /// - Fractional seconds represented by any decimals.
159 #[inline(always)]
160 pub const fn new_f(sec: Real, scale: Scale, target: Scale) -> Dt {
161 Self {
162 attos: Dt::sec_f_to_attos(sec),
163 scale,
164 target,
165 }
166 }
167
168 /// Creates a new [`Dt`] from a total number of attoseconds (signed i128) without
169 /// performing any time scale conversions.
170 ///
171 /// - This is an easy way to create a duration.
172 /// - The returned [`Dt`] has its `scale` and `target` fields set to
173 /// `Scale::TAI`.
174 #[inline(always)]
175 pub const fn span(attos: i128) -> Dt {
176 Dt::new(attos, Scale::TAI, Scale::TAI)
177 }
178
179 /// Creates a [`Dt`] from a floating-point number of seconds without performing
180 /// any time scale conversions.
181 ///
182 /// - This is an easy way to create a duration or a seconds count that doesn't
183 /// include any time scale conversions, just holds the seconds count as is.
184 /// - The returned [`Dt`] has its `scale` and `target` fields set to
185 /// `Scale::TAI`.
186 #[inline(always)]
187 pub const fn span_f(sec: Real) -> Dt {
188 Self::from_sec_f(sec, Scale::TAI)
189 }
190
191 /// Low level constructor from total attoseconds since a given epoch.
192 ///
193 /// Simply adds the total attoseconds to the epoch. Does not perform
194 /// any time scale conversions.
195 ///
196 /// ## Examples
197 ///
198 /// ```rust
199 /// use deep_time::{Dt, Scale};
200 ///
201 /// // A leap second from the middle of the table (36 leap seconds accumulated)
202 /// let original = Dt::from_ymd(2015, 6, 30, Scale::UTC, 23, 59, 60, 123_456_789_000_000_000);
203 ///
204 /// // Round-trip through canonical attoseconds
205 /// let canon = original.to_diff_raw(Dt::UNIX_EPOCH).to_attos();
206 /// let roundtrip1 = Dt::from_diff_raw(canon, Dt::UNIX_EPOCH);
207 ///
208 /// assert_eq!(original, roundtrip1, "Canonical round-trip failed");
209 /// ```
210 #[inline]
211 pub const fn from_diff_raw(attos: i128, epoch: Dt) -> Dt {
212 epoch.add(Dt::new(attos, epoch.scale, epoch.target))
213 }
214
215 /// Returns a [`Dt`] on the TAI time scale, after having been **converted** to TAI from
216 /// the given `scale`.
217 ///
218 /// - **Requires** a seconds and attoseconds count such that would be returned from the
219 /// functions [`Dt::to_sec64_floor`](../struct.Dt.html#method.to_sec64_floor) and
220 /// **[`Dt::to_sec_ufrac`](../struct.Dt.html#method.to_sec_ufrac)**.
221 /// - The returned object's `scale` field is set to TAI and its `target` field is set to
222 /// the given `scale` arg.
223 /// - The `sec` should be from the epoch TAI 2000-01-01 12:00:00.
224 ///
225 /// This function performs a time scale conversion from the given `scale` to **TAI**,
226 /// if you don't want any time scale conversion to take place then either use
227 /// `Scale::TAI` as an arg or use any of the following constructors:
228 ///
229 /// - [`Dt::new`](../struct.Dt.html#method.new)
230 /// - [`Dt::new_sec`](../struct.Dt.html#method.new_sec)
231 /// - [`Dt::new_f`](../struct.Dt.html#method.new_f)
232 /// - [`Dt::span`](../struct.Dt.html#method.span)
233 /// - [`Dt::span_f`](../struct.Dt.html#method.span_f)
234 /// - [`Dt::from_tai_sec`](../struct.Dt.html#method.from_tai_sec)
235 #[inline(always)]
236 pub fn from_sec_and_ufrac(sec: i64, attos: u64, scale: Scale) -> Dt {
237 if attos == 0 {
238 Dt::from_attos((sec as i128) * ATTOS_PER_SEC_I128, scale)
239 } else {
240 Dt::from_attos((sec as i128) * ATTOS_PER_SEC_I128 + (attos as i128), scale)
241 }
242 }
243
244 /// Builds a [`Dt`] from whole seconds plus a sub-second attoseconds remainder.
245 ///
246 /// - `sec` — whole seconds only (no fraction). Use
247 /// [`Dt::to_sec64`](../struct.Dt.html#method.to_sec64)
248 /// to obtain this from an existing [`Dt`].
249 /// - `attos` — the signed sub-second remainder in attoseconds, as returned by
250 /// [`Dt::to_sec_frac`](../struct.Dt.html#method.to_sec_frac).
251 /// For a total of `1.3` s: `sec = 1`, `attos = 300_000_000_000_000_000`.
252 /// For `-1.3` s: `sec = -1`, `attos = -300_000_000_000_000_000`.
253 /// For `-0.5` s: `sec = 0`, `attos = -500_000_000_000_000_000`.
254 ///
255 /// This whole/remainder split differs from
256 /// [`Dt::to_sec64_floor`](../struct.Dt.html#method.to_sec64_floor)
257 /// +
258 /// [`Dt::to_sec_ufrac`](../struct.Dt.html#method.to_sec_ufrac).
259 /// Use
260 /// [`from_sec_and_ufrac`](../struct.Dt.html#method.from_sec_and_ufrac)
261 /// for that pairing.
262 ///
263 /// ## Examples
264 ///
265 /// ```rust
266 /// use deep_time::{Dt, Scale};
267 ///
268 /// let dt = Dt::span(1_300_000_000_000_000_000);
269 /// assert_eq!(
270 /// Dt::from_sec_and_frac(1, 300_000_000_000_000_000, Scale::TAI),
271 /// dt,
272 /// );
273 ///
274 /// let dt = Dt::span(-1_300_000_000_000_000_000);
275 /// assert_eq!(
276 /// Dt::from_sec_and_frac(-1, -300_000_000_000_000_000, Scale::TAI),
277 /// dt,
278 /// );
279 ///
280 /// let dt = Dt::span(-500_000_000_000_000_000);
281 /// assert_eq!(
282 /// Dt::from_sec_and_frac(0, -500_000_000_000_000_000, Scale::TAI),
283 /// dt,
284 /// );
285 /// ```
286 ///
287 /// The result is stored on TAI and converted from `scale`.
288 /// `sec` is measured from the library epoch: 2000-01-01 12:00:00 TAI.
289 ///
290 /// To avoid scale conversion, pass `Scale::TAI`, or use one of:
291 ///
292 /// - [`Dt::new`](../struct.Dt.html#method.new)
293 /// - [`Dt::new_sec`](../struct.Dt.html#method.new_sec)
294 /// - [`Dt::new_f`](../struct.Dt.html#method.new_f)
295 /// - [`Dt::span`](../struct.Dt.html#method.span)
296 /// - [`Dt::span_f`](../struct.Dt.html#method.span_f)
297 /// - [`Dt::from_tai_sec`](../struct.Dt.html#method.from_tai_sec)
298 #[inline(always)]
299 pub fn from_sec_and_frac(sec: i64, attos: i64, scale: Scale) -> Dt {
300 Dt::from_attos((sec as i128) * ATTOS_PER_SEC_I128 + (attos as i128), scale)
301 }
302
303 /// Returns a [`Dt`] on the TAI time scale, after having been **converted** to TAI from
304 /// the given `scale`.
305 ///
306 /// - Requires a total attoseconds value.
307 /// - The value should be from the epoch TAI 2000-01-01 12:00:00.
308 /// - The returned object's `scale` field is set to TAI and its `target` field is set to
309 /// the given `scale` arg.
310 ///
311 /// This function performs a time scale conversion from the given `scale` to **TAI**,
312 /// if you don't want any time scale conversion to take place then either use
313 /// `Scale::TAI` as an arg or use any of the following constructors:
314 ///
315 /// - [`Dt::new`](../struct.Dt.html#method.new)
316 /// - [`Dt::new_sec`](../struct.Dt.html#method.new_sec)
317 /// - [`Dt::new_f`](../struct.Dt.html#method.new_f)
318 /// - [`Dt::span`](../struct.Dt.html#method.span)
319 /// - [`Dt::span_f`](../struct.Dt.html#method.span_f)
320 /// - [`Dt::from_tai_sec`](../struct.Dt.html#method.from_tai_sec)
321 #[inline(always)]
322 pub const fn from_attos(attos: i128, scale: Scale) -> Dt {
323 Dt::new(attos, scale, scale).to_tai()
324 }
325
326 /// Returns a [`Dt`] on the TAI time scale, after having been **converted** to TAI from
327 /// the given `scale`.
328 ///
329 /// - Requires a total attoseconds value.
330 /// - The value should be from the epoch TAI 2000-01-01 12:00:00.
331 /// - The returned object's `scale` field is set to TAI and its `target` field is set to
332 /// the given `scale` arg.
333 ///
334 /// This function performs a time scale conversion from the given `scale` to **TAI**,
335 /// if you don't want any time scale conversion to take place then either use
336 /// `Scale::TAI` as an arg or use any of the following constructors:
337 ///
338 /// - [`Dt::new`](../struct.Dt.html#method.new)
339 /// - [`Dt::new_sec`](../struct.Dt.html#method.new_sec)
340 /// - [`Dt::new_f`](../struct.Dt.html#method.new_f)
341 /// - [`Dt::span`](../struct.Dt.html#method.span)
342 /// - [`Dt::span_f`](../struct.Dt.html#method.span_f)
343 /// - [`Dt::from_tai_sec`](../struct.Dt.html#method.from_tai_sec)
344 #[inline(always)]
345 pub const fn from_attos_with_target(attos: i128, scale: Scale, target: Scale) -> Dt {
346 Dt::new(attos, scale, target).to_tai()
347 }
348
349 /// Creates a new [`Dt`] from a total number of seconds (signed i128) without
350 /// performing any time scale conversions.
351 #[inline(always)]
352 pub const fn from_tai_sec(sec: i128) -> Dt {
353 Self::from_attos(sec.saturating_mul(ATTOS_PER_SEC_I128), Scale::TAI)
354 }
355
356 /// Returns a [`Dt`] on the TAI time scale, after having been **converted** to TAI from
357 /// the given `scale`.
358 ///
359 /// - Requires a total seconds value.
360 /// - The value should be from the epoch TAI 2000-01-01 12:00:00.
361 /// - The returned object's `scale` field is set to TAI and its `target` field is set to
362 /// the given `scale` arg.
363 ///
364 /// This function performs a time scale conversion from the given `scale` to **TAI**,
365 /// if you don't want any time scale conversion to take place then either use
366 /// `Scale::TAI` as an arg or use any of the following constructors:
367 ///
368 /// - [`Dt::new`](../struct.Dt.html#method.new)
369 /// - [`Dt::new_sec`](../struct.Dt.html#method.new_sec)
370 /// - [`Dt::new_f`](../struct.Dt.html#method.new_f)
371 /// - [`Dt::span`](../struct.Dt.html#method.span)
372 /// - [`Dt::span_f`](../struct.Dt.html#method.span_f)
373 /// - [`Dt::from_tai_sec`](../struct.Dt.html#method.from_tai_sec)
374 #[inline(always)]
375 pub const fn from_sec(sec: i128, scale: Scale) -> Dt {
376 Self::from_attos(sec.saturating_mul(ATTOS_PER_SEC_I128), scale)
377 }
378
379 /// Builds a [`Dt`] from whole milliseconds plus a non-negative fractional part in attoseconds.
380 ///
381 /// Pairs with [`to_ms_floor`](../struct.Dt.html#method.to_ms_floor): the fractional part is
382 /// always **added**, even when `ms` is negative (Euclidean / floor split).
383 ///
384 /// - Values are measured from the epoch TAI 2000-01-01 12:00:00.
385 /// - The result is stored on TAI after conversion from `scale`.
386 #[inline(always)]
387 pub const fn from_ms_floor(ms: i128, frac_attos: u128, scale: Scale) -> Dt {
388 let attos = Dt::unit_and_attos_to_attos(ms, frac_attos, ATTOS_PER_MS_I128);
389 Self::from_attos(attos, scale)
390 }
391
392 /// Builds a [`Dt`] from whole microseconds plus a non-negative fractional part in attoseconds.
393 ///
394 /// Pairs with [`to_us_floor`](../struct.Dt.html#method.to_us_floor): the fractional part is
395 /// always **added**, even when `us` is negative (Euclidean / floor split).
396 ///
397 /// - Values are measured from the epoch TAI 2000-01-01 12:00:00.
398 /// - The result is stored on TAI after conversion from `scale`.
399 #[inline(always)]
400 pub const fn from_us_floor(us: i128, frac_attos: u128, scale: Scale) -> Dt {
401 let attos = Dt::unit_and_attos_to_attos(us, frac_attos, ATTOS_PER_US_I128);
402 Self::from_attos(attos, scale)
403 }
404
405 /// Builds a [`Dt`] from whole nanoseconds plus a non-negative fractional part in attoseconds.
406 ///
407 /// Pairs with [`to_ns_floor`](../struct.Dt.html#method.to_ns_floor): the fractional part is
408 /// always **added**, even when `ns` is negative (Euclidean / floor split).
409 ///
410 /// - Values are measured from the epoch TAI 2000-01-01 12:00:00.
411 /// - The result is stored on TAI after conversion from `scale`.
412 #[inline(always)]
413 pub const fn from_ns_floor(ns: i128, frac_attos: u128, scale: Scale) -> Dt {
414 let attos = Dt::unit_and_attos_to_attos(ns, frac_attos, ATTOS_PER_NS_I128);
415 Self::from_attos(attos, scale)
416 }
417
418 /// Builds a [`Dt`] from whole picoseconds plus a non-negative fractional part in attoseconds.
419 ///
420 /// Pairs with [`to_ps_floor`](../struct.Dt.html#method.to_ps_floor): the fractional part is
421 /// always **added**, even when `ps` is negative (Euclidean / floor split).
422 ///
423 /// - Values are measured from the epoch TAI 2000-01-01 12:00:00.
424 /// - The result is stored on TAI after conversion from `scale`.
425 #[inline(always)]
426 pub const fn from_ps_floor(ps: i128, frac_attos: u128, scale: Scale) -> Dt {
427 let attos = Dt::unit_and_attos_to_attos(ps, frac_attos, ATTOS_PER_PS_I128);
428 Self::from_attos(attos, scale)
429 }
430
431 /// Builds a [`Dt`] from whole femtoseconds plus a non-negative fractional part in attoseconds.
432 ///
433 /// Pairs with [`to_fs_floor`](../struct.Dt.html#method.to_fs_floor): the fractional part is
434 /// always **added**, even when `fs` is negative (Euclidean / floor split).
435 ///
436 /// - Values are measured from the epoch TAI 2000-01-01 12:00:00.
437 /// - The result is stored on TAI after conversion from `scale`.
438 #[inline(always)]
439 pub const fn from_fs_floor(fs: i128, frac_attos: u128, scale: Scale) -> Dt {
440 let attos = Dt::unit_and_attos_to_attos(fs, frac_attos, ATTOS_PER_FS_I128);
441 Self::from_attos(attos, scale)
442 }
443
444 /// Builds a [`Dt`] from whole minutes plus a non-negative fractional part in attoseconds.
445 ///
446 /// Pairs with [`to_mins_floor`](../struct.Dt.html#method.to_mins_floor): the fractional part is
447 /// always **added**, even when `m` is negative (Euclidean / floor split).
448 ///
449 /// - Values are measured from the epoch TAI 2000-01-01 12:00:00.
450 /// - The result is stored on TAI after conversion from `scale`.
451 #[inline(always)]
452 pub const fn from_mins_floor(n: i128, frac_attos: u128, scale: Scale) -> Dt {
453 let attos = Dt::unit_and_attos_to_attos(n, frac_attos, ATTOS_PER_MIN);
454 Self::from_attos(attos, scale)
455 }
456
457 /// Builds a [`Dt`] from whole hours plus a non-negative fractional part in attoseconds.
458 ///
459 /// Pairs with [`to_hours_floor`](../struct.Dt.html#method.to_hours_floor): the fractional part is
460 /// always **added**, even when `h` is negative (Euclidean / floor split).
461 ///
462 /// - Values are measured from the epoch TAI 2000-01-01 12:00:00.
463 /// - The result is stored on TAI after conversion from `scale`.
464 #[inline(always)]
465 pub const fn from_hours_floor(n: i128, frac_attos: u128, scale: Scale) -> Dt {
466 let attos = Dt::unit_and_attos_to_attos(n, frac_attos, ATTOS_PER_HOUR);
467 Self::from_attos(attos, scale)
468 }
469
470 /// Builds a [`Dt`] from whole days plus a non-negative fractional part in attoseconds.
471 ///
472 /// Pairs with [`to_days_floor`](../struct.Dt.html#method.to_days_floor): the fractional part is
473 /// always **added**, even when `d` is negative (Euclidean / floor split).
474 ///
475 /// - Uses `86400` seconds per day.
476 /// - Values are measured from the epoch TAI 2000-01-01 12:00:00.
477 /// - The result is stored on TAI after conversion from `scale`.
478 #[inline(always)]
479 pub const fn from_days_floor(d: i128, frac_attos: u128, scale: Scale) -> Dt {
480 let attos = Dt::unit_and_attos_to_attos(d, frac_attos, ATTOS_PER_DAY);
481 Self::from_attos(attos, scale)
482 }
483
484 /// Returns a [`Dt`] on the TAI time scale, after having been **converted** to TAI from
485 /// the given `scale`.
486 ///
487 /// - Convenience wrapper around
488 /// [`Dt::from_sec`](../struct.Dt.html#method.from_sec).
489 /// - Uses `604800` seconds per week in the calculation.
490 #[inline(always)]
491 pub const fn from_weeks(n: i128, scale: Scale) -> Dt {
492 Dt::from_sec(n.saturating_mul(SEC_PER_WEEK as i128), scale)
493 }
494
495 /// Returns a [`Dt`] on the TAI time scale, after having been **converted** to TAI from
496 /// the given `scale`.
497 ///
498 /// - Convenience wrapper around
499 /// [`Dt::from_sec`](../struct.Dt.html#method.from_sec).
500 /// - Uses `31_557_600` in the calculation.
501 #[inline(always)]
502 pub const fn from_years(n: i128, scale: Scale) -> Dt {
503 Dt::from_sec(n.saturating_mul(31_557_600), scale)
504 }
505
506 /// Returns an instant that is this duration **before** zero attoseconds on `scale`.
507 ///
508 /// Zero attoseconds is the library epoch **2000-01-01 12:00:00** (see
509 /// [`Dt::ZERO`](../struct.Dt.html#associatedconstant.ZERO)). This method is `const` and
510 /// does **not** read the system clock.
511 ///
512 /// For wall-clock “N units ago”, use [`Dt::ago`](../struct.Dt.html#method.ago)
513 /// (requires `std`, or WASM with `js`).
514 ///
515 /// ## Examples
516 ///
517 /// ```rust
518 /// use deep_time::{Dt, Scale, TimeTraits};
519 ///
520 /// let t = 5.sec().before_zero(Scale::TAI);
521 /// assert_eq!(t, Dt::ZERO.sub(5.sec()));
522 /// assert_eq!(t.to_sec(), -5);
523 /// ```
524 ///
525 /// ## See also
526 ///
527 /// - [`Dt::after_zero`](../struct.Dt.html#method.after_zero)
528 /// - [`Dt::ago`](../struct.Dt.html#method.ago)
529 /// - [`Dt::from_attos`](../struct.Dt.html#method.from_attos)
530 #[inline(always)]
531 pub const fn before_zero(self, scale: Scale) -> Dt {
532 Dt::from_attos(0, scale).sub(self)
533 }
534
535 /// Returns an instant that is this duration **after** zero attoseconds on `scale`.
536 ///
537 /// Zero attoseconds is the library epoch **2000-01-01 12:00:00** (see
538 /// [`Dt::ZERO`](../struct.Dt.html#associatedconstant.ZERO)). This method is `const` and
539 /// does **not** read the system clock.
540 ///
541 /// For wall-clock “N units from now”, use
542 /// [`Dt::from_now`](../struct.Dt.html#method.from_now) (requires `std`, or WASM with
543 /// `js`).
544 ///
545 /// ## Examples
546 ///
547 /// ```rust
548 /// use deep_time::{Dt, Scale, TimeTraits};
549 ///
550 /// let t = 5.sec().after_zero(Scale::TAI);
551 /// assert_eq!(t, Dt::ZERO.add(5.sec()));
552 /// assert_eq!(t.to_sec(), 5);
553 /// ```
554 ///
555 /// ## See also
556 ///
557 /// - [`Dt::before_zero`](../struct.Dt.html#method.before_zero)
558 /// - [`Dt::from_now`](../struct.Dt.html#method.from_now)
559 /// - [`Dt::from_attos`](../struct.Dt.html#method.from_attos)
560 #[inline(always)]
561 pub const fn after_zero(self, scale: Scale) -> Dt {
562 Dt::from_attos(0, scale).add(self)
563 }
564
565 /// Returns the negation of this [`Dt`].
566 #[inline(always)]
567 pub const fn neg(self) -> Dt {
568 Dt::new(-self.attos, self.scale, self.target)
569 }
570
571 /// Returns the positive of this [`Dt`].
572 #[inline(always)]
573 pub const fn abs(self) -> Dt {
574 Dt::new(self.attos.saturating_abs(), self.scale, self.target)
575 }
576
577 /// Creates a [`Dt`] from a floating-point number of seconds.
578 ///
579 /// - Assumes the value is on the given scale.
580 /// - Converts the value to TAI from the given `scale`.
581 /// - The returned [`Dt`] is on the TAI time scale - its `scale`
582 /// field is `TAI` and its `target` field is the provided time
583 /// scale argument.
584 ///
585 /// ## Examples
586 ///
587 /// ```rust
588 /// use deep_time::{Dt, Scale};
589 ///
590 /// let seconds = 5.5;
591 ///
592 /// // use TAI for no conversions
593 /// let duration = Dt::from_sec_f(seconds, Scale::TAI);
594 ///
595 /// assert_eq!(duration.to_sec_f(), seconds);
596 /// ```
597 #[inline]
598 pub const fn from_sec_f(sec: Real, scale: Scale) -> Dt {
599 if sec.is_nan() {
600 return Self::ZERO;
601 } else if sec.is_infinite() {
602 return if sec.is_sign_positive() {
603 Self::MAX
604 } else {
605 Self::MIN
606 };
607 }
608 Self::from_attos(Self::sec_f_to_attos(sec), scale)
609 }
610
611 /// High-precision conversion from [`Real`] seconds to total attoseconds (i128).
612 ///
613 /// - Uses IEEE 754 bit extraction + exact integer multiplication by 5^18.
614 /// - Returns the rounded integer (round-to-nearest, ties away from zero).
615 pub const fn sec_f_to_attos(sec: Real) -> i128 {
616 if sec == 0.0 {
617 return 0;
618 }
619
620 let bits = sec.to_bits();
621 let is_negative = (bits >> 63) != 0;
622 let biased_exp = ((bits >> 52) & 0x7ff) as i32;
623 let mantissa = bits & 0x000f_ffff_ffff_ffff;
624
625 let (sig, exp) = if biased_exp == 0 {
626 if mantissa == 0 {
627 return 0;
628 }
629 (mantissa as u128, -1022i32 - 52)
630 } else {
631 let sig = ((1u64 << 52) | mantissa) as u128;
632 (sig, biased_exp - 1023 - 52)
633 };
634
635 const FIVE_POW_18: u128 = 3_814_697_265_625; // 5^18 exactly
636 let product = sig * FIVE_POW_18;
637 let total_exp = exp + 18;
638
639 // Safe saturation / underflow guards (prevents invalid shifts >= 128)
640 if total_exp > 120 {
641 return if is_negative { i128::MIN } else { i128::MAX };
642 }
643 if total_exp < -97 {
644 return 0;
645 }
646
647 let abs_total = if total_exp >= 0 {
648 let shift = total_exp as u32;
649 if product > (u128::MAX >> shift) {
650 if is_negative { i128::MIN } else { i128::MAX }
651 } else {
652 let shifted = product << shift;
653 if shifted > i128::MAX as u128 {
654 if is_negative { i128::MIN } else { i128::MAX }
655 } else {
656 shifted as i128
657 }
658 }
659 } else {
660 let shift = (-total_exp) as u32;
661 let int_part = (product >> shift) as i128;
662
663 // Round to nearest, half away from zero (on the absolute value)
664 let mask = (1u128 << shift) - 1;
665 let rem = product & mask;
666 if rem > (mask >> 1) {
667 int_part + 1
668 } else {
669 int_part
670 }
671 };
672
673 if is_negative { -abs_total } else { abs_total }
674 }
675
676 /// Returns the current system time as TAI from 2000-01-01 12:00:00.
677 ///
678 /// This method is only available when the `std` feature is enabled and the target
679 /// is not WASM with the `js` feature.
680 #[cfg(all(feature = "std", not(all(target_arch = "wasm32", feature = "js"))))]
681 pub fn now() -> Dt {
682 let now = std::time::SystemTime::now();
683
684 let (secs, nanos): (i64, i64) = match now.duration_since(std::time::UNIX_EPOCH) {
685 Ok(dur) => (dur.as_secs() as i64, dur.subsec_nanos() as i64),
686 Err(e) => {
687 let dur = e.duration();
688 (-(dur.as_secs() as i64), -(dur.subsec_nanos() as i64))
689 }
690 };
691
692 Dt::from_diff_and_scale(
693 Dt::new(Dt::sec_to_attos(secs as i128), Scale::TAI, Scale::UTC),
694 Dt::UNIX_EPOCH,
695 false,
696 )
697 .add(Dt::from_ns_floor(nanos as i128, 0, Scale::TAI))
698 }
699
700 /// Returns the current system time as TAI from 2000-01-01 12:00:00.
701 /// (browser WASM version using JavaScript’s `Date.now()`).
702 #[cfg(all(target_arch = "wasm32", feature = "js"))]
703 pub fn now() -> Dt {
704 let ms: f64 = js_sys::Date::now();
705 let secs = (ms / 1000.0).floor() as i128;
706 let nanos = ((ms % 1000.0) * 1_000_000.0) as i128;
707 Dt::from_diff_and_scale(
708 Dt::new(Dt::sec_to_attos(secs), Scale::TAI, Scale::UTC),
709 Dt::UNIX_EPOCH,
710 false,
711 )
712 .add(Dt::from_ns_floor(nanos as i128, 0, Scale::TAI))
713 }
714
715 /// Returns an instant that is this duration **before** the current system time.
716 ///
717 /// Subtracts `self` from [`Dt::now`](../struct.Dt.html#method.now). Available under
718 /// the same conditions as that method: the `std` feature (non-WASM-js), or WASM with
719 /// the `js` feature.
720 ///
721 /// For a `const` offset from the library epoch (no system clock), use
722 /// [`Dt::before_zero`](../struct.Dt.html#method.before_zero).
723 ///
724 /// ## Examples
725 ///
726 /// ```rust
727 /// # #[cfg(feature = "std")]
728 /// # {
729 /// use deep_time::{Dt, TimeTraits};
730 ///
731 /// // ~3 days in the past relative to the system clock
732 /// let past = 3.days().ago();
733 /// assert!(past < Dt::now());
734 /// # }
735 /// ```
736 ///
737 /// ## See also
738 ///
739 /// - [`Dt::from_now`](../struct.Dt.html#method.from_now)
740 /// - [`Dt::before_zero`](../struct.Dt.html#method.before_zero)
741 /// - [`Dt::now`](../struct.Dt.html#method.now)
742 #[cfg(any(
743 all(feature = "std", not(all(target_arch = "wasm32", feature = "js"))),
744 all(target_arch = "wasm32", feature = "js"),
745 ))]
746 #[inline]
747 pub fn ago(self) -> Dt {
748 Dt::now().sub(self)
749 }
750
751 /// Returns an instant that is this duration **after** the current system time.
752 ///
753 /// Adds `self` to [`Dt::now`](../struct.Dt.html#method.now). Available under the same
754 /// conditions as that method: the `std` feature (non-WASM-js), or WASM with the `js`
755 /// feature.
756 ///
757 /// For a `const` offset from the library epoch (no system clock), use
758 /// [`Dt::after_zero`](../struct.Dt.html#method.after_zero).
759 ///
760 /// ## Examples
761 ///
762 /// ```rust
763 /// # #[cfg(feature = "std")]
764 /// # {
765 /// use deep_time::{Dt, TimeTraits};
766 ///
767 /// // ~3 days in the future relative to the system clock
768 /// let future = 3.days().from_now();
769 /// assert!(future > Dt::now());
770 /// # }
771 /// ```
772 ///
773 /// ## See also
774 ///
775 /// - [`Dt::ago`](../struct.Dt.html#method.ago)
776 /// - [`Dt::after_zero`](../struct.Dt.html#method.after_zero)
777 /// - [`Dt::now`](../struct.Dt.html#method.now)
778 #[cfg(any(
779 all(feature = "std", not(all(target_arch = "wasm32", feature = "js"))),
780 all(target_arch = "wasm32", feature = "js"),
781 ))]
782 #[inline]
783 pub fn from_now(self) -> Dt {
784 Dt::now().add(self)
785 }
786}