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}