Skip to main content

deep_time/dt/
julian_date.rs

1use crate::{
2    ATTOS_PER_DAY, ATTOS_PER_HALF_DAY, ATTOS_PER_SEC_I128, Dt, JD_2000_2_451_545_I128, Real, Scale,
3    floor_f,
4};
5
6impl Dt {
7    /// Splits this instant's Julian Date into whole days and a remainder in attoseconds.
8    ///
9    /// ## Important
10    ///
11    /// - Converts to this [`Dt`]'s `target` scale before splitting. Set `target` first
12    ///   if you need JD on a particular scale (e.g. `Scale::TT` or `Scale::TDB`).
13    /// - Assumes this [`Dt`] is counting from the 2000-01-01 noon epoch.
14    /// - When the whole part is negative and there is a non-zero remainder, the 2nd tuple
15    ///   element - fractional attoseconds, is negative too. e.g. a jd of `-1000.25` will
16    ///   return the whole part as `-1000` and the remainder as `-0.25 * ATTOS_PER_DAY` as
17    ///   an `i128`.
18    ///
19    /// ## Examples
20    ///
21    /// ```rust
22    /// use deep_time::{Dt, Scale, days_f};
23    ///
24    /// let dt = Dt::from_jd_f(2_460_782.25, Scale::TAI);
25    /// // 2_460_782 and 0.25 days in attoseconds
26    /// assert_eq!(dt.to_jd(), (2_460_782, days_f!(0.25)));
27    ///
28    /// let dt = Dt::from_jd_f(-1_000.25, Scale::TAI);
29    /// // -1_000 and -0.25 days in attoseconds
30    /// assert_eq!(dt.to_jd(), (-1_000, -days_f!(0.25)));
31    /// ```
32    ///
33    /// ## See also
34    ///
35    /// - [`Dt::to_jd_floor`](../struct.Dt.html#method.to_jd_floor)
36    /// - [`Dt::to_jd_f`](../struct.Dt.html#method.to_jd_f)
37    /// - [`Dt::from_jd`](../struct.Dt.html#method.from_jd)
38    /// - [`Dt::to_mjd`](../struct.Dt.html#method.to_mjd)
39    #[inline(always)]
40    pub const fn to_jd(&self) -> (i128, i128) {
41        self.to(self.target).to_jd_raw()
42    }
43
44    /// Like [`Dt::to_jd`](../struct.Dt.html#method.to_jd), but performs no
45    /// time scale conversions prior to output.
46    ///
47    /// ## See also
48    ///
49    /// - [`Dt::to_jd`](../struct.Dt.html#method.to_jd)
50    /// - [`Dt::to_jd_floor_raw`](../struct.Dt.html#method.to_jd_floor_raw)
51    /// - [`Dt::to_jd_f_raw`](../struct.Dt.html#method.to_jd_f_raw)
52    #[inline]
53    pub const fn to_jd_raw(&self) -> (i128, i128) {
54        let (days, attos) = self.to_days();
55        (JD_2000_2_451_545_I128.saturating_add(days), attos)
56    }
57
58    /// Splits this instant's Julian Date into whole days and a remainder in attoseconds.
59    ///
60    /// ## Important
61    ///
62    /// - Converts to this [`Dt`]'s `target` scale before splitting. Set `target` first
63    ///   if you need JD on a particular scale (e.g. `Scale::TT` or `Scale::TDB`).
64    /// - Assumes this [`Dt`] is counting from the 2000-01-01 noon epoch.
65    /// - The remainder is always zero or positive and less than one day. e.g. a jd of
66    ///   `-1000.25` will return the whole part as `-1001` and the remainder as
67    ///   `0.75 * ATTOS_PER_DAY` as an `i128`.
68    ///
69    /// ## Examples
70    ///
71    /// ```rust
72    /// use deep_time::{Dt, Scale, days_f};
73    ///
74    /// let dt = Dt::from_jd_f(2_460_782.25, Scale::TAI);
75    /// // 2_460_782 and 0.25 days in attoseconds
76    /// assert_eq!(dt.to_jd_floor(), (2_460_782, days_f!(0.25)));
77    ///
78    /// let dt = Dt::from_jd_f(-1_000.25, Scale::TAI);
79    /// // -1_001 and effectively 0.75 days in attoseconds
80    /// assert_eq!(dt.to_jd_floor(), (-1_001, days_f!(0.75)));
81    ///
82    /// let (days, frac) = dt.to_jd_floor();
83    /// let back = Dt::from_jd(days, frac, Scale::TAI);
84    /// assert_eq!(back, dt);
85    /// ```
86    ///
87    /// ## See also
88    ///
89    /// - [`Dt::to_jd`](../struct.Dt.html#method.to_jd)
90    /// - [`Dt::to_jd_f`](../struct.Dt.html#method.to_jd_f)
91    /// - [`Dt::from_jd`](../struct.Dt.html#method.from_jd)
92    #[inline(always)]
93    pub const fn to_jd_floor(&self) -> (i128, i128) {
94        self.to(self.target).to_jd_floor_raw()
95    }
96
97    /// Like [`Dt::to_jd_floor`](../struct.Dt.html#method.to_jd_floor),
98    /// but performs no time scale conversions prior to output.
99    ///
100    /// ## See also
101    ///
102    /// - [`Dt::to_jd_floor`](../struct.Dt.html#method.to_jd_floor)
103    /// - [`Dt::to_jd_raw`](../struct.Dt.html#method.to_jd_raw)
104    #[inline]
105    pub const fn to_jd_floor_raw(&self) -> (i128, i128) {
106        let (days, attos) = self.to_days_floor();
107        (JD_2000_2_451_545_I128.saturating_add(days), attos)
108    }
109
110    /// Returns this instant's Julian Date as a [`Real`].
111    ///
112    /// ## Important
113    ///
114    /// - Converts to this [`Dt`]'s `target` scale first. Set `target` first if you need
115    ///   JD on a particular scale (e.g. `Scale::TT` or `Scale::TDB`).
116    /// - Assumes this [`Dt`] is counting from the 2000-01-01 noon epoch.
117    /// - Same value as [`Dt::to_jd`](../struct.Dt.html#method.to_jd), expressed as a single
118    ///   [`Real`] instead of a `(days, frac_attos)` pair.
119    ///
120    /// ## Examples
121    ///
122    /// ```rust
123    /// use deep_time::{Dt, Scale};
124    ///
125    /// let dt = Dt::from_jd_f(2_460_782.25, Scale::TAI);
126    /// assert_eq!(dt.to_jd_f(), 2_460_782.25);
127    ///
128    /// let dt = Dt::from_jd_f(-1_000.25, Scale::TAI);
129    /// assert_eq!(dt.to_jd_f(), -1_000.25);
130    /// ```
131    ///
132    /// ## See also
133    ///
134    /// - [`Dt::to_jd`](../struct.Dt.html#method.to_jd)
135    /// - [`Dt::to_jd_f_raw`](../struct.Dt.html#method.to_jd_f_raw)
136    /// - [`Dt::from_jd_f`](../struct.Dt.html#method.from_jd_f)
137    #[inline]
138    pub const fn to_jd_f(&self) -> Real {
139        let (days, attos) = self.to_jd();
140        f!(days) + f!(attos) / f!(ATTOS_PER_DAY)
141    }
142
143    /// Like [`Dt::to_jd_f`](../struct.Dt.html#method.to_jd_f),
144    /// but performs no time scale conversions prior to output.
145    ///
146    /// ## See also
147    ///
148    /// - [`Dt::to_jd_f`](../struct.Dt.html#method.to_jd_f)
149    /// - [`Dt::to_jd_raw`](../struct.Dt.html#method.to_jd_raw)
150    #[inline]
151    pub const fn to_jd_f_raw(&self) -> Real {
152        let (days, attos) = self.to_jd_raw();
153        f!(days) + f!(attos) / f!(ATTOS_PER_DAY)
154    }
155
156    /// Splits this instant's Modified Julian Date into whole days and a remainder in attoseconds.
157    ///
158    /// ## Important
159    ///
160    /// - Converts to this [`Dt`]'s `target` scale before splitting. Set `target` first
161    ///   if you need MJD on a particular scale.
162    /// - Assumes this [`Dt`] is counting from the 2000-01-01 noon epoch.
163    /// - MJD and JD relate by `JD = MJD + 2_400_000.5`.
164    /// - When the whole part is negative and there is a non-zero remainder, the 2nd tuple
165    ///   element — fractional attoseconds — is negative too. e.g. an mjd of `-1000.25` built
166    ///   as whole `-1000` plus `-0.25` days in attoseconds will return `(-1000, -0.25 * ATTOS_PER_DAY)`
167    ///   as an `i128`.
168    ///
169    /// ## Examples
170    ///
171    /// ```rust
172    /// use deep_time::{Dt, Scale, days_f};
173    ///
174    /// let dt = Dt::from_mjd(51_544, days_f!(0.5), Scale::TAI);
175    /// // J2000.0 → MJD 51_544.5 as whole 51_544 plus 0.5 days in attoseconds
176    /// assert_eq!(dt.to_mjd(), (51_544, days_f!(0.5)));
177    ///
178    /// let dt = Dt::from_mjd(-1_000, -days_f!(0.25), Scale::TAI);
179    /// // -1_000 and -0.25 days in attoseconds
180    /// assert_eq!(dt.to_mjd(), (-1_000, -days_f!(0.25)));
181    /// ```
182    ///
183    /// ## See also
184    ///
185    /// - [`Dt::to_mjd_floor`](../struct.Dt.html#method.to_mjd_floor)
186    /// - [`Dt::to_mjd_f`](../struct.Dt.html#method.to_mjd_f)
187    /// - [`Dt::from_mjd`](../struct.Dt.html#method.from_mjd)
188    /// - [`Dt::to_jd`](../struct.Dt.html#method.to_jd)
189    #[inline(always)]
190    pub const fn to_mjd(&self) -> (i128, i128) {
191        self.to(self.target).to_mjd_raw()
192    }
193
194    /// Like [`Dt::to_mjd`](../struct.Dt.html#method.to_mjd), but performs no
195    /// time scale conversions prior to output.
196    ///
197    /// ## See also
198    ///
199    /// - [`Dt::to_mjd`](../struct.Dt.html#method.to_mjd)
200    /// - [`Dt::to_mjd_floor_raw`](../struct.Dt.html#method.to_mjd_floor_raw)
201    /// - [`Dt::to_mjd_f_raw`](../struct.Dt.html#method.to_mjd_f_raw)
202    #[inline]
203    pub const fn to_mjd_raw(&self) -> (i128, i128) {
204        let total = self.attos.saturating_sub(Self::MJD_EPOCH.attos);
205        (total / ATTOS_PER_DAY, total % ATTOS_PER_DAY)
206    }
207
208    /// Splits this instant's Modified Julian Date into whole days and a remainder in attoseconds.
209    ///
210    /// ## Important
211    ///
212    /// - Converts to this [`Dt`]'s `target` scale before splitting. Set `target` first
213    ///   if you need MJD on a particular scale.
214    /// - Assumes this [`Dt`] is counting from the 2000-01-01 noon epoch.
215    /// - MJD and JD relate by `JD = MJD + 2_400_000.5`.
216    /// - The remainder is always zero or positive and less than one day. e.g. an mjd of
217    ///   `-1000.25` will return the whole part as `-1001` and the remainder as
218    ///   `0.75 * ATTOS_PER_DAY` as an `i128`.
219    ///
220    /// ## Examples
221    ///
222    /// ```rust
223    /// use deep_time::{Dt, Scale, days_f};
224    ///
225    /// let dt = Dt::from_mjd_f(60_961.25, Scale::TAI);
226    /// // 60_961 and 0.25 days in attoseconds
227    /// assert_eq!(dt.to_mjd_floor(), (60_961, days_f!(0.25)));
228    ///
229    /// let dt = Dt::from_mjd_f(-1_000.25, Scale::TAI);
230    /// // -1_001 and effectively 0.75 days in attoseconds
231    /// assert_eq!(dt.to_mjd_floor(), (-1_001, days_f!(0.75)));
232    ///
233    /// let (days, frac) = dt.to_mjd_floor();
234    /// let back = Dt::from_mjd(days, frac, Scale::TAI);
235    /// assert_eq!(back, dt);
236    /// ```
237    ///
238    /// ## See also
239    ///
240    /// - [`Dt::to_mjd`](../struct.Dt.html#method.to_mjd)
241    /// - [`Dt::to_mjd_f`](../struct.Dt.html#method.to_mjd_f)
242    /// - [`Dt::from_mjd`](../struct.Dt.html#method.from_mjd)
243    #[inline(always)]
244    pub const fn to_mjd_floor(&self) -> (i128, i128) {
245        self.to(self.target).to_mjd_floor_raw()
246    }
247
248    /// Like [`Dt::to_mjd_floor`](../struct.Dt.html#method.to_mjd_floor),
249    /// but performs no time scale conversions prior to output.
250    ///
251    /// ## See also
252    ///
253    /// - [`Dt::to_mjd_floor`](../struct.Dt.html#method.to_mjd_floor)
254    /// - [`Dt::to_mjd_raw`](../struct.Dt.html#method.to_mjd_raw)
255    #[inline]
256    pub const fn to_mjd_floor_raw(&self) -> (i128, i128) {
257        let total = self.attos.saturating_sub(Self::MJD_EPOCH.attos);
258        (
259            total.div_euclid(ATTOS_PER_DAY),
260            total.rem_euclid(ATTOS_PER_DAY),
261        )
262    }
263
264    /// Returns this instant's Modified Julian Date as an [`Real`].
265    ///
266    /// ## Important
267    ///
268    /// - Converts to this [`Dt`]'s `target` scale first. Set `target` first if you need
269    ///   MJD on a particular scale.
270    /// - Assumes this [`Dt`] is counting from the 2000-01-01 noon epoch.
271    /// - Same value as [`Dt::to_mjd`](../struct.Dt.html#method.to_mjd), expressed as a single
272    ///   [`Real`] instead of a `(days, frac_attos)` pair.
273    ///
274    /// ## Examples
275    ///
276    /// ```rust
277    /// use deep_time::{Dt, Scale};
278    ///
279    /// let dt = Dt::from_mjd_f(60_961.25, Scale::TAI);
280    /// assert_eq!(dt.to_mjd_f(), 60_961.25);
281    ///
282    /// let dt = Dt::from_mjd_f(-1_000.25, Scale::TAI);
283    /// assert_eq!(dt.to_mjd_f(), -1_000.25);
284    /// ```
285    ///
286    /// ## See also
287    ///
288    /// - [`Dt::to_mjd`](../struct.Dt.html#method.to_mjd)
289    /// - [`Dt::to_mjd_f_raw`](../struct.Dt.html#method.to_mjd_f_raw)
290    /// - [`Dt::from_mjd_f`](../struct.Dt.html#method.from_mjd_f)
291    #[inline]
292    pub const fn to_mjd_f(&self) -> Real {
293        let (days, attos) = self.to_mjd();
294        f!(days) + f!(attos) / f!(ATTOS_PER_DAY)
295    }
296
297    /// Like [`Dt::to_mjd_f`](../struct.Dt.html#method.to_mjd_f),
298    /// but performs no time scale conversions prior to output.
299    ///
300    /// ## See also
301    ///
302    /// - [`Dt::to_mjd_f`](../struct.Dt.html#method.to_mjd_f)
303    /// - [`Dt::to_mjd_raw`](../struct.Dt.html#method.to_mjd_raw)
304    #[inline]
305    pub const fn to_mjd_f_raw(&self) -> Real {
306        let (days, attos) = self.to_mjd_raw();
307        f!(days) + f!(attos) / f!(ATTOS_PER_DAY)
308    }
309
310    /// Builds a **TAI** [`Dt`] from a Julian Date given as whole days plus attoseconds.
311    ///
312    /// ## Important
313    ///
314    /// This converts from the `on` time scale to `TAI` so for example, an
315    /// `on` scale of `Scale::UTC` will add leap seconds to the end result.
316    ///
317    /// To avoid a time scale conversion use `Scale::TAI` for the `on` argument.
318    ///
319    /// `frac_attos` is in **attoseconds**. Either split style works:
320    ///
321    /// - Truncating / signed remainder (as from [`to_jd`](../struct.Dt.html#method.to_jd)):
322    ///   e.g. `-1000.25` as `jd_days = -1000`, `frac_attos = -0.25 * ATTOS_PER_DAY`.
323    /// - Floor / non-negative remainder (as from
324    ///   [`to_jd_floor`](../struct.Dt.html#method.to_jd_floor)): e.g. `-1000.25` as
325    ///   `jd_days = -1001`, `frac_attos = 0.75 * ATTOS_PER_DAY`.
326    ///
327    /// ## Returns
328    ///
329    /// A [`Dt`] counting attoseconds since the library epoch
330    /// [`Dt::ZERO`](../struct.Dt.html#associatedconstant.ZERO) with its `scale` field set to
331    /// `TAI` and its `target` field set to the `on` arg.
332    ///
333    /// ## Examples
334    ///
335    /// ```rust
336    /// use deep_time::{Dt, Scale, days_f};
337    ///
338    /// // 2_460_782.25 as whole days plus 0.25 days in attoseconds
339    /// let dt = Dt::from_jd(2_460_782, days_f!(0.25), Scale::TAI);
340    /// assert_eq!(dt.to_jd(), (2_460_782, days_f!(0.25)));
341    ///
342    /// // -1_000.25 as whole days plus -0.25 days in attoseconds (signed)
343    /// let dt = Dt::from_jd(-1_000, -days_f!(0.25), Scale::TAI);
344    /// assert_eq!(dt.to_jd(), (-1_000, -days_f!(0.25)));
345    ///
346    /// // same instant as floor split: -1_001 + 0.75 day
347    /// let floor = Dt::from_jd(-1_001, days_f!(0.75), Scale::TAI);
348    /// assert_eq!(dt, floor);
349    ///
350    /// // round-trip a `to_jd_floor` pair
351    /// let (days, frac) = dt.to_jd_floor();
352    /// assert_eq!(Dt::from_jd(days, frac, Scale::TAI), dt);
353    /// ```
354    ///
355    /// ## See also
356    ///
357    /// - [`Dt::from_jd_f`](../struct.Dt.html#method.from_jd_f)
358    /// - [`Dt::to_jd`](../struct.Dt.html#method.to_jd)
359    /// - [`Dt::to_jd_floor`](../struct.Dt.html#method.to_jd_floor)
360    /// - [`Dt::to_i128`](../struct.Dt.html#method.to_i128)
361    /// - [`Dt::from_mjd`](../struct.Dt.html#method.from_mjd)
362    pub const fn from_jd(jd_days: i128, attos: i128, on: Scale) -> Dt {
363        let total_attos = jd_days
364            .saturating_sub(JD_2000_2_451_545_I128)
365            .saturating_mul(ATTOS_PER_DAY)
366            .saturating_add(attos);
367        Dt::new(total_attos, on, on).to_tai()
368    }
369
370    /// Builds a **TAI** [`Dt`] from a Modified Julian Date given as whole days plus attoseconds.
371    ///
372    /// ## Important
373    ///
374    /// This converts from the `on` time scale to `TAI` so for example, an
375    /// `on` scale of `Scale::UTC` will add leap seconds to the end result.
376    ///
377    /// To avoid a time scale conversion use `Scale::TAI` for the `on` argument.
378    ///
379    /// MJD and JD relate by `JD = MJD + 2_400_000.5`.
380    ///
381    /// `attos` is in **attoseconds**. Either split style works:
382    ///
383    /// - Truncating / signed remainder (as from [`to_mjd`](../struct.Dt.html#method.to_mjd)):
384    ///   e.g. `-1000.25` as `mjd_days = -1000`, `attos = -0.25 * ATTOS_PER_DAY`.
385    /// - Floor / non-negative remainder (as from
386    ///   [`to_mjd_floor`](../struct.Dt.html#method.to_mjd_floor)): e.g. `-1000.25` as
387    ///   `mjd_days = -1001`, `attos = 0.75 * ATTOS_PER_DAY`.
388    ///
389    /// ## Returns
390    ///
391    /// A [`Dt`] counting attoseconds since the library epoch
392    /// [`Dt::ZERO`](../struct.Dt.html#associatedconstant.ZERO) with its `scale` field set to
393    /// `TAI` and its `target` field set to the `on` arg.
394    ///
395    /// ## Examples
396    ///
397    /// ```rust
398    /// use deep_time::{Dt, Scale, days_f};
399    ///
400    /// // J2000.0 → MJD 51_544.5 as whole 51_544 plus 0.5 days in attoseconds
401    /// let dt = Dt::from_mjd(51_544, days_f!(0.5), Scale::TAI);
402    /// assert_eq!(dt.to_mjd(), (51_544, days_f!(0.5)));
403    ///
404    /// // -1_000.25 as whole days plus -0.25 days in attoseconds (signed)
405    /// let dt = Dt::from_mjd(-1_000, -days_f!(0.25), Scale::TAI);
406    /// assert_eq!(dt.to_mjd(), (-1_000, -days_f!(0.25)));
407    ///
408    /// // same instant as floor split: -1_001 + 0.75 day
409    /// let floor = Dt::from_mjd(-1_001, days_f!(0.75), Scale::TAI);
410    /// assert_eq!(dt, floor);
411    ///
412    /// // round-trip a `to_mjd_floor` pair
413    /// let (days, frac) = dt.to_mjd_floor();
414    /// assert_eq!(Dt::from_mjd(days, frac, Scale::TAI), dt);
415    /// ```
416    ///
417    /// ## See also
418    ///
419    /// - [`Dt::from_mjd_f`](../struct.Dt.html#method.from_mjd_f)
420    /// - [`Dt::to_mjd`](../struct.Dt.html#method.to_mjd)
421    /// - [`Dt::to_mjd_floor`](../struct.Dt.html#method.to_mjd_floor)
422    /// - [`Dt::from_jd`](../struct.Dt.html#method.from_jd)
423    pub const fn from_mjd(mjd_days: i128, attos: i128, on: Scale) -> Dt {
424        let jd_days = mjd_days.saturating_add(2_400_001);
425        let jd_attos = attos.saturating_sub(ATTOS_PER_HALF_DAY);
426        Self::from_jd(jd_days, jd_attos, on)
427    }
428
429    /// Builds a **TAI** [`Dt`] from a floating-point Julian Date.
430    ///
431    /// ## Important
432    ///
433    /// - The `on` scale becomes this [`Dt`]'s `target`; its `scale` is always `TAI`.
434    ///
435    /// ## Examples
436    ///
437    /// ```rust
438    /// use deep_time::{Dt, Scale, days_f};
439    ///
440    /// let dt = Dt::from_jd_f(2_460_782.25, Scale::TAI);
441    /// // 2_460_782 and 0.25 days in attoseconds
442    /// assert_eq!(dt.to_jd(), (2_460_782, days_f!(0.25)));
443    ///
444    /// let dt = Dt::from_jd_f(-1_000.25, Scale::TAI);
445    /// // -1_000 and -0.25 days in attoseconds
446    /// assert_eq!(dt.to_jd(), (-1_000, -days_f!(0.25)));
447    /// ```
448    ///
449    /// ## See also
450    ///
451    /// - [`Dt::from_jd`](../struct.Dt.html#method.from_jd)
452    /// - [`Dt::to_jd_f`](../struct.Dt.html#method.to_jd_f)
453    /// - [`Dt::from_mjd_f`](../struct.Dt.html#method.from_mjd_f)
454    pub const fn from_jd_f(jd: Real, on: Scale) -> Dt {
455        let jd_days_f = floor_f(jd);
456        let jd_days = jd_days_f as i128;
457
458        let mut frac_day = jd - jd_days_f;
459        if frac_day < 0.0 {
460            frac_day = 0.0;
461        } else if frac_day >= 1.0 {
462            frac_day = 1.0 - f64::EPSILON;
463        }
464
465        let total_sec_f = frac_day * 86_400.0;
466        let whole_sec = floor_f(total_sec_f) as i64;
467        let frac_sec = total_sec_f - (whole_sec as Real);
468
469        let attos_whole: i128 = (whole_sec as i128).saturating_mul(ATTOS_PER_SEC_I128);
470
471        let attos_frac_f = frac_sec * 1_000_000_000_000_000_000.0;
472        let attos_frac: i128 = floor_f(attos_frac_f + 0.5) as i128;
473
474        let mut total_attos: i128 = attos_whole.saturating_add(attos_frac);
475
476        let mut extra_days: i128 = 0;
477        if total_attos >= ATTOS_PER_DAY {
478            extra_days = 1;
479            total_attos = total_attos.saturating_sub(ATTOS_PER_DAY);
480        } else if total_attos < 0 {
481            extra_days = -1;
482            total_attos = total_attos.saturating_add(ATTOS_PER_DAY);
483        }
484
485        let final_jd_days = jd_days.saturating_add(extra_days);
486
487        // Floor-style fraction: non-negative and less than one day.
488        Self::from_jd(final_jd_days, total_attos, on)
489    }
490
491    /// Builds a **TAI** [`Dt`] from a floating-point Modified Julian Date.
492    ///
493    /// ## Important
494    ///
495    /// This converts from the `on` time scale to `TAI` so for example, an
496    /// `on` scale of `Scale::UTC` will add leap seconds to the end result.
497    ///
498    /// To avoid a time scale conversion use `Scale::TAI` for the `on` argument.
499    ///
500    /// ## Returns
501    ///
502    /// A [`Dt`] counting attoseconds since the library epoch
503    /// [`Dt::ZERO`](../struct.Dt.html#associatedconstant.ZERO) with its `scale` field set to
504    /// `TAI` and its `target` field set to the `on` arg.
505    ///
506    /// ## Examples
507    ///
508    /// ```rust
509    /// use deep_time::{Dt, Scale, days_f};
510    ///
511    /// let dt = Dt::from_mjd_f(60_961.25, Scale::TAI);
512    /// // 60_961 and 0.25 days in attoseconds
513    /// assert_eq!(dt.to_mjd_floor(), (60_961, days_f!(0.25)));
514    ///
515    /// let dt = Dt::from_mjd_f(-1_000.25, Scale::TAI);
516    /// // -1_001 and effectively 0.75 days in attoseconds
517    /// assert_eq!(dt.to_mjd_floor(), (-1_001, days_f!(0.75)));
518    /// ```
519    ///
520    /// ## See also
521    ///
522    /// - [`Dt::from_mjd`](../struct.Dt.html#method.from_mjd)
523    /// - [`Dt::from_jd_f`](../struct.Dt.html#method.from_jd_f)
524    /// - [`Dt::to_mjd_f`](../struct.Dt.html#method.to_mjd_f)
525    #[inline(always)]
526    pub const fn from_mjd_f(mjd: Real, on: Scale) -> Dt {
527        Self::from_jd_f(mjd + f!(2_400_000.5), on)
528    }
529}