Skip to main content

deep_time/
macros.rs

1//! Crate-root convenience macros (`dt!`, `from_sec!`, `from_sec_f!`, `from_ns!`,
2//! `from_ms!`, `from_jd!`, `from_jd_f!`, `from_mjd!`, `from_mjd_f!`, `from_ymd!`,
3//!  …).
4//!
5//! Optional scale labels use Python-style keyword arguments on **count** macros
6//! (`dt!`, `from_sec!`, `from_sec_f!`, `from_ns!`, `from_ms!`, …):
7//!
8//! - `on=<scale>` — stored as the [`Dt`](crate::Dt)'s `scale` field (and as
9//!   `target` when `target=` is omitted).
10//! - `target=<scale>` — stored as the `target` field; `on` defaults to
11//!   [`Scale::TAI`](crate::Scale::TAI).
12//!
13//! Either keyword may appear alone or together, in either order.
14//!
15//! [`from_jd!`](crate::from_jd), [`from_jd_f!`](crate::from_jd_f),
16//! [`from_mjd!`](crate::from_mjd), [`from_mjd_f!`](crate::from_mjd_f), and
17//! [`from_ymd!`](crate::from_ymd) only take
18//! a single `on=` scale (see each
19//! macro); use [`.target(…)`](crate::Dt::target) afterward if the
20//! `target` field should differ.
21
22/// Builds a [`Dt`](crate::Dt) from total attoseconds with optional scale labels.
23///
24/// Sugar for [`Dt::new`](crate::Dt::new). Does **not** perform time-scale
25/// conversion.
26///
27/// ## Defaults
28///
29/// | Omitted | Default |
30/// |---------|---------|
31/// | `on` and `target` | both [`Scale::TAI`](crate::Scale::TAI) |
32/// | only `on=s` | `target=s` |
33/// | only `target=t` | `on=TAI` |
34///
35/// ## Forms
36///
37/// | Form | Meaning |
38/// |------|---------|
39/// | `dt!(attos)` | both scales TAI |
40/// | `dt!(attos, on=s)` | scale and target `s` |
41/// | `dt!(attos, target=t)` | scale TAI, target `t` |
42/// | `dt!(attos, on=s, target=t)` | either order |
43/// | `dt!(attos, target=t, on=s)` | either order |
44///
45/// ## Examples
46///
47/// ```rust
48/// use deep_time::{Dt, Scale, dt};
49///
50/// let a = dt!(Dt::sec_to_attos(1));
51/// let b = dt!(0, on=Scale::UTC);
52/// let c = dt!(0, on=Scale::TAI, target=Scale::UTC);
53/// let d = dt!(0, target=Scale::UTC, on=Scale::TAI);
54/// let e = dt!(0, target=Scale::UTC);
55///
56/// assert_eq!(a, Dt::new(Dt::sec_to_attos(1), Scale::TAI, Scale::TAI));
57/// assert_eq!(b, Dt::new(0, Scale::UTC, Scale::UTC));
58/// assert_eq!(c, Dt::new(0, Scale::TAI, Scale::UTC));
59/// assert_eq!(d, c);
60/// assert_eq!(e, Dt::new(0, Scale::TAI, Scale::UTC));
61/// ```
62#[macro_export]
63macro_rules! dt {
64    ($attos:expr, on=$scale:expr, target=$target:expr) => {
65        $crate::Dt::new($attos, $scale, $target)
66    };
67    ($attos:expr, target=$target:expr, on=$scale:expr) => {
68        $crate::Dt::new($attos, $scale, $target)
69    };
70    ($attos:expr, on=$scale:expr) => {
71        $crate::Dt::new($attos, $scale, $scale)
72    };
73    ($attos:expr, target=$target:expr) => {
74        $crate::Dt::new($attos, $crate::Scale::TAI, $target)
75    };
76    ($attos:expr) => {
77        $crate::Dt::new($attos, $crate::Scale::TAI, $crate::Scale::TAI)
78    };
79}
80
81/// Builds a [`Dt`](crate::Dt) from whole seconds and an optional signed
82/// sub-second remainder (attoseconds).
83///
84/// Sugar for [`Dt::from_sec_and_frac`](crate::Dt::from_sec_and_frac). Does
85/// **not** perform time-scale conversion.
86///
87/// The fractional remainder is in **attoseconds** — use
88/// [`Dt::ms_to_attos`](crate::Dt::ms_to_attos) (or another `*_to_attos`
89/// helper) instead of writing the attosecond literal by hand.
90///
91/// ## Defaults
92///
93/// | Omitted | Default |
94/// |---------|---------|
95/// | fraction | `0` |
96/// | `on` and `target` | both [`Scale::TAI`](crate::Scale::TAI) |
97/// | only `on=s` | `target=s` |
98/// | only `target=t` | `on=TAI` |
99///
100/// ## Forms
101///
102/// ```text
103/// from_sec!(sec)
104/// from_sec!(sec, frac)
105/// from_sec!(sec, on=s)
106/// from_sec!(sec, target=t)
107/// from_sec!(sec, on=s, target=t)
108/// from_sec!(sec, target=t, on=s)
109/// from_sec!(sec, frac, on=s)
110/// from_sec!(sec, frac, target=t)
111/// from_sec!(sec, frac, on=s, target=t)
112/// from_sec!(sec, frac, target=t, on=s)
113/// ```
114///
115/// ## Examples
116///
117/// ```
118/// use deep_time::{Dt, Scale, from_sec};
119///
120/// // 1.3 s → whole seconds + 300 ms remainder
121/// let a = from_sec!(1);
122/// let b = from_sec!(1, Dt::ms_to_attos(300));
123/// let c = from_sec!(-1, Dt::ms_to_attos(-300), on=Scale::TAI);
124/// let d = from_sec!(0, on=Scale::UTC);
125/// let e = from_sec!(1, on=Scale::TAI, target=Scale::UTC);
126/// let f = from_sec!(1, target=Scale::UTC, on=Scale::TAI);
127///
128/// assert_eq!(a, Dt::from_sec_and_frac(1, 0, Scale::TAI, Scale::TAI));
129/// assert_eq!(b, Dt::from_sec_and_frac(1, Dt::ms_to_attos(300), Scale::TAI, Scale::TAI));
130/// assert_eq!(c, Dt::from_sec_and_frac(-1, Dt::ms_to_attos(-300), Scale::TAI, Scale::TAI));
131/// assert_eq!(d, Dt::from_sec_and_frac(0, 0, Scale::UTC, Scale::UTC));
132/// assert_eq!(e, Dt::from_sec_and_frac(1, 0, Scale::TAI, Scale::UTC));
133/// assert_eq!(f, e);
134///
135/// let signed = from_sec!(-1, Dt::ms_to_attos(-300));
136/// let floor = from_sec!(-2, Dt::ms_to_attos(700));
137/// assert_eq!(signed, floor);
138/// assert_eq!(signed, Dt::from_sec_and_frac(-1, Dt::ms_to_attos(-300), Scale::TAI, Scale::TAI));
139/// ```
140#[macro_export]
141macro_rules! from_sec {
142    ($sec:expr, $frac:expr, on=$scale:expr, target=$target:expr) => {
143        $crate::Dt::from_sec_and_frac($sec, $frac, $scale, $target)
144    };
145    ($sec:expr, $frac:expr, target=$target:expr, on=$scale:expr) => {
146        $crate::Dt::from_sec_and_frac($sec, $frac, $scale, $target)
147    };
148    ($sec:expr, on=$scale:expr, target=$target:expr) => {
149        $crate::Dt::from_sec_and_frac($sec, 0, $scale, $target)
150    };
151    ($sec:expr, target=$target:expr, on=$scale:expr) => {
152        $crate::Dt::from_sec_and_frac($sec, 0, $scale, $target)
153    };
154    ($sec:expr, $frac:expr, on=$scale:expr) => {
155        $crate::Dt::from_sec_and_frac($sec, $frac, $scale, $scale)
156    };
157    ($sec:expr, $frac:expr, target=$target:expr) => {
158        $crate::Dt::from_sec_and_frac($sec, $frac, $crate::Scale::TAI, $target)
159    };
160    ($sec:expr, on=$scale:expr) => {
161        $crate::Dt::from_sec_and_frac($sec, 0, $scale, $scale)
162    };
163    ($sec:expr, target=$target:expr) => {
164        $crate::Dt::from_sec_and_frac($sec, 0, $crate::Scale::TAI, $target)
165    };
166    ($sec:expr, $frac:expr) => {
167        $crate::Dt::from_sec_and_frac($sec, $frac, $crate::Scale::TAI, $crate::Scale::TAI)
168    };
169    ($sec:expr) => {
170        $crate::Dt::from_sec_and_frac($sec, 0, $crate::Scale::TAI, $crate::Scale::TAI)
171    };
172}
173
174/// Builds a [`Dt`](crate::Dt) from a floating-point seconds count with optional
175/// scale labels.
176///
177/// Sugar for [`Dt::from_sec_f`](crate::Dt::from_sec_f). Does **not** perform
178/// time-scale conversion.
179///
180/// ## Defaults
181///
182/// | Omitted | Default |
183/// |---------|---------|
184/// | `on` and `target` | both [`Scale::TAI`](crate::Scale::TAI) |
185/// | only `on=s` | `target=s` |
186/// | only `target=t` | `on=TAI` |
187///
188/// ## Forms
189///
190/// | Form | Meaning |
191/// |------|---------|
192/// | `from_sec_f!(sec)` | both scales TAI |
193/// | `from_sec_f!(sec, on=s)` | scale and target `s` |
194/// | `from_sec_f!(sec, target=t)` | scale TAI, target `t` |
195/// | `from_sec_f!(sec, on=s, target=t)` | either order |
196/// | `from_sec_f!(sec, target=t, on=s)` | either order |
197///
198/// ## Examples
199///
200/// ```
201/// use deep_time::Scale;
202/// use deep_time::from_sec_f;
203///
204/// let a = from_sec_f!(5.5);
205/// let b = from_sec_f!(0.0, on=Scale::UTC);
206/// let c = from_sec_f!(1.0, on=Scale::TAI, target=Scale::UTC);
207/// let d = from_sec_f!(1.0, target=Scale::UTC, on=Scale::TAI);
208///
209/// assert_eq!(a, deep_time::Dt::from_sec_f(5.5, Scale::TAI, Scale::TAI));
210/// assert_eq!(b, deep_time::Dt::from_sec_f(0.0, Scale::UTC, Scale::UTC));
211/// assert_eq!(c, deep_time::Dt::from_sec_f(1.0, Scale::TAI, Scale::UTC));
212/// assert_eq!(d, c);
213/// ```
214#[macro_export]
215macro_rules! from_sec_f {
216    ($sec:expr, on=$scale:expr, target=$target:expr) => {
217        $crate::Dt::from_sec_f($sec, $scale, $target)
218    };
219    ($sec:expr, target=$target:expr, on=$scale:expr) => {
220        $crate::Dt::from_sec_f($sec, $scale, $target)
221    };
222    ($sec:expr, on=$scale:expr) => {
223        $crate::Dt::from_sec_f($sec, $scale, $scale)
224    };
225    ($sec:expr, target=$target:expr) => {
226        $crate::Dt::from_sec_f($sec, $crate::Scale::TAI, $target)
227    };
228    ($sec:expr) => {
229        $crate::Dt::from_sec_f($sec, $crate::Scale::TAI, $crate::Scale::TAI)
230    };
231}
232
233/// Builds a [`Dt`](crate::Dt) from whole nanoseconds and an optional signed
234/// fractional remainder in attoseconds.
235///
236/// Sugar for [`Dt::from_ns`](crate::Dt::from_ns). Does **not** perform
237/// time-scale conversion.
238///
239/// The fractional remainder is in **attoseconds** — use
240/// [`Dt::ps_to_attos`](crate::Dt::ps_to_attos) (or another `*_to_attos`
241/// helper) instead of writing the attosecond literal by hand.
242///
243/// ## Defaults
244///
245/// | Omitted | Default |
246/// |---------|---------|
247/// | fraction | `0` |
248/// | `on` and `target` | both [`Scale::TAI`](crate::Scale::TAI) |
249/// | only `on=s` | `target=s` |
250/// | only `target=t` | `on=TAI` |
251///
252/// ## Forms
253///
254/// ```text
255/// from_ns!(ns)
256/// from_ns!(ns, frac)
257/// from_ns!(ns, on=s)
258/// from_ns!(ns, target=t)
259/// from_ns!(ns, on=s, target=t)
260/// from_ns!(ns, target=t, on=s)
261/// from_ns!(ns, frac, on=s)
262/// from_ns!(ns, frac, target=t)
263/// from_ns!(ns, frac, on=s, target=t)
264/// from_ns!(ns, frac, target=t, on=s)
265/// ```
266///
267/// ## Examples
268///
269/// ```
270/// use deep_time::{Dt, Scale, from_ns};
271///
272/// // 1.3 ns → whole nanoseconds + 300 ps remainder
273/// let a = from_ns!(1);
274/// let b = from_ns!(1, Dt::ps_to_attos(300));
275/// let c = from_ns!(-1, Dt::ps_to_attos(-300), on=Scale::TAI);
276/// let d = from_ns!(0, on=Scale::UTC);
277/// let e = from_ns!(1, on=Scale::TAI, target=Scale::UTC);
278/// let f = from_ns!(1, target=Scale::UTC);
279///
280/// assert_eq!(a, Dt::from_ns(1, 0, Scale::TAI, Scale::TAI));
281/// assert_eq!(b, Dt::from_ns(1, Dt::ps_to_attos(300), Scale::TAI, Scale::TAI));
282/// assert_eq!(c, Dt::from_ns(-1, Dt::ps_to_attos(-300), Scale::TAI, Scale::TAI));
283/// assert_eq!(d, Dt::from_ns(0, 0, Scale::UTC, Scale::UTC));
284/// assert_eq!(e, Dt::from_ns(1, 0, Scale::TAI, Scale::UTC));
285/// assert_eq!(f, e);
286///
287/// let signed = from_ns!(-1, Dt::ps_to_attos(-300));
288/// let floor = from_ns!(-2, Dt::ps_to_attos(700));
289/// assert_eq!(signed, floor);
290/// assert_eq!(signed, Dt::from_ns(-1, Dt::ps_to_attos(-300), Scale::TAI, Scale::TAI));
291/// ```
292#[macro_export]
293macro_rules! from_ns {
294    ($ns:expr, $frac:expr, on=$scale:expr, target=$target:expr) => {
295        $crate::Dt::from_ns($ns, $frac, $scale, $target)
296    };
297    ($ns:expr, $frac:expr, target=$target:expr, on=$scale:expr) => {
298        $crate::Dt::from_ns($ns, $frac, $scale, $target)
299    };
300    ($ns:expr, on=$scale:expr, target=$target:expr) => {
301        $crate::Dt::from_ns($ns, 0, $scale, $target)
302    };
303    ($ns:expr, target=$target:expr, on=$scale:expr) => {
304        $crate::Dt::from_ns($ns, 0, $scale, $target)
305    };
306    ($ns:expr, $frac:expr, on=$scale:expr) => {
307        $crate::Dt::from_ns($ns, $frac, $scale, $scale)
308    };
309    ($ns:expr, $frac:expr, target=$target:expr) => {
310        $crate::Dt::from_ns($ns, $frac, $crate::Scale::TAI, $target)
311    };
312    ($ns:expr, on=$scale:expr) => {
313        $crate::Dt::from_ns($ns, 0, $scale, $scale)
314    };
315    ($ns:expr, target=$target:expr) => {
316        $crate::Dt::from_ns($ns, 0, $crate::Scale::TAI, $target)
317    };
318    ($ns:expr, $frac:expr) => {
319        $crate::Dt::from_ns($ns, $frac, $crate::Scale::TAI, $crate::Scale::TAI)
320    };
321    ($ns:expr) => {
322        $crate::Dt::from_ns($ns, 0, $crate::Scale::TAI, $crate::Scale::TAI)
323    };
324}
325
326/// Builds a [`Dt`](crate::Dt) from whole milliseconds and an optional signed
327/// fractional remainder in attoseconds.
328///
329/// Sugar for [`Dt::from_ms`](crate::Dt::from_ms). Does **not** perform
330/// time-scale conversion.
331///
332/// The fractional remainder is in **attoseconds** — use
333/// [`Dt::us_to_attos`](crate::Dt::us_to_attos) (or another `*_to_attos`
334/// helper) instead of writing the attosecond literal by hand.
335///
336/// ## Defaults
337///
338/// | Omitted | Default |
339/// |---------|---------|
340/// | fraction | `0` |
341/// | `on` and `target` | both [`Scale::TAI`](crate::Scale::TAI) |
342/// | only `on=s` | `target=s` |
343/// | only `target=t` | `on=TAI` |
344///
345/// ## Forms
346///
347/// ```text
348/// from_ms!(ms)
349/// from_ms!(ms, frac)
350/// from_ms!(ms, on=s)
351/// from_ms!(ms, target=t)
352/// from_ms!(ms, on=s, target=t)
353/// from_ms!(ms, target=t, on=s)
354/// from_ms!(ms, frac, on=s)
355/// from_ms!(ms, frac, target=t)
356/// from_ms!(ms, frac, on=s, target=t)
357/// from_ms!(ms, frac, target=t, on=s)
358/// ```
359///
360/// ## Examples
361///
362/// ```
363/// use deep_time::{Dt, Scale, from_ms};
364///
365/// // 1.3 ms → whole milliseconds + 300 µs remainder
366/// let a = from_ms!(1);
367/// let b = from_ms!(1, Dt::us_to_attos(300));
368/// let c = from_ms!(-1, Dt::us_to_attos(-300), on=Scale::TAI);
369/// let d = from_ms!(0, on=Scale::UTC);
370/// let e = from_ms!(1, on=Scale::TAI, target=Scale::UTC);
371/// let f = from_ms!(1, target=Scale::UTC);
372///
373/// assert_eq!(a, Dt::from_ms(1, 0, Scale::TAI, Scale::TAI));
374/// assert_eq!(b, Dt::from_ms(1, Dt::us_to_attos(300), Scale::TAI, Scale::TAI));
375/// assert_eq!(c, Dt::from_ms(-1, Dt::us_to_attos(-300), Scale::TAI, Scale::TAI));
376/// assert_eq!(d, Dt::from_ms(0, 0, Scale::UTC, Scale::UTC));
377/// assert_eq!(e, Dt::from_ms(1, 0, Scale::TAI, Scale::UTC));
378/// assert_eq!(f, e);
379///
380/// let signed = from_ms!(-1, Dt::us_to_attos(-300));
381/// let floor = from_ms!(-2, Dt::us_to_attos(700));
382/// assert_eq!(signed, floor);
383/// assert_eq!(signed, Dt::from_ms(-1, Dt::us_to_attos(-300), Scale::TAI, Scale::TAI));
384/// ```
385#[macro_export]
386macro_rules! from_ms {
387    ($ms:expr, $frac:expr, on=$scale:expr, target=$target:expr) => {
388        $crate::Dt::from_ms($ms, $frac, $scale, $target)
389    };
390    ($ms:expr, $frac:expr, target=$target:expr, on=$scale:expr) => {
391        $crate::Dt::from_ms($ms, $frac, $scale, $target)
392    };
393    ($ms:expr, on=$scale:expr, target=$target:expr) => {
394        $crate::Dt::from_ms($ms, 0, $scale, $target)
395    };
396    ($ms:expr, target=$target:expr, on=$scale:expr) => {
397        $crate::Dt::from_ms($ms, 0, $scale, $target)
398    };
399    ($ms:expr, $frac:expr, on=$scale:expr) => {
400        $crate::Dt::from_ms($ms, $frac, $scale, $scale)
401    };
402    ($ms:expr, $frac:expr, target=$target:expr) => {
403        $crate::Dt::from_ms($ms, $frac, $crate::Scale::TAI, $target)
404    };
405    ($ms:expr, on=$scale:expr) => {
406        $crate::Dt::from_ms($ms, 0, $scale, $scale)
407    };
408    ($ms:expr, target=$target:expr) => {
409        $crate::Dt::from_ms($ms, 0, $crate::Scale::TAI, $target)
410    };
411    ($ms:expr, $frac:expr) => {
412        $crate::Dt::from_ms($ms, $frac, $crate::Scale::TAI, $crate::Scale::TAI)
413    };
414    ($ms:expr) => {
415        $crate::Dt::from_ms($ms, 0, $crate::Scale::TAI, $crate::Scale::TAI)
416    };
417}
418
419/// Builds a **TAI** [`Dt`](crate::Dt) from a Julian Date (whole days plus
420/// optional attosecond remainder).
421///
422/// Sugar for [`Dt::from_jd`](crate::Dt::from_jd).
423///
424/// Converts from the `on` scale to `TAI` when `on` is not TAI (e.g.
425/// `on=Scale::UTC` applies leap seconds). `on` defaults to
426/// [`Scale::TAI`](crate::Scale::TAI), omit it to skip conversion.
427///
428/// There is no `target=` on this macro — the returned [`Dt`](crate::Dt)'s
429/// `target` is set from `on` (or TAI when omitted), as
430/// [`from_jd`](crate::Dt::from_jd) does. Chain
431/// [`.target(…)`](crate::Dt::target) if needed.
432///
433/// The fractional remainder is in **attoseconds** — use a `*_to_attos` helper
434/// (e.g. a day fraction built from [`ATTOS_PER_DAY`](crate::consts::ATTOS_PER_DAY))
435/// instead of hand-counting zeros when convenient.
436///
437/// ## Defaults
438///
439/// | Omitted | Default |
440/// |---------|---------|
441/// | fraction | `0` |
442/// | `on` | [`Scale::TAI`](crate::Scale::TAI) |
443///
444/// ## Forms
445///
446/// ```text
447/// from_jd!(jd_days)
448/// from_jd!(jd_days, frac)
449/// from_jd!(jd_days, on=s)
450/// from_jd!(jd_days, frac, on=s)
451/// ```
452///
453/// ## Examples
454///
455/// ```
456/// use deep_time::{Dt, Scale, consts::ATTOS_PER_DAY, from_jd};
457///
458/// // 2_460_782.25
459/// let a = from_jd!(2_460_782);
460/// let b = from_jd!(2_460_782, ATTOS_PER_DAY / 4);
461/// let c = from_jd!(2_460_782, ATTOS_PER_DAY / 4, on=Scale::TAI);
462/// let d = from_jd!(2_460_782, on=Scale::UTC);
463///
464/// assert_eq!(a, Dt::from_jd(2_460_782, 0, Scale::TAI));
465/// assert_eq!(b, Dt::from_jd(2_460_782, ATTOS_PER_DAY / 4, Scale::TAI));
466/// assert_eq!(c, b);
467/// assert_eq!(d, Dt::from_jd(2_460_782, 0, Scale::UTC));
468///
469/// // -1_000.25 (signed remainder)
470/// let neg = from_jd!(-1_000, -ATTOS_PER_DAY / 4);
471/// assert_eq!(neg, Dt::from_jd(-1_000, -ATTOS_PER_DAY / 4, Scale::TAI));
472/// assert_eq!(neg.to_jd(), (-1_000, -ATTOS_PER_DAY / 4));
473/// ```
474#[macro_export]
475macro_rules! from_jd {
476    // `on=` arms before bare `$frac:expr` so `on=…` is not taken as an expr.
477    ($jd_days:expr, $frac:expr, on=$scale:expr) => {
478        $crate::Dt::from_jd($jd_days, $frac, $scale)
479    };
480    ($jd_days:expr, on=$scale:expr) => {
481        $crate::Dt::from_jd($jd_days, 0, $scale)
482    };
483    ($jd_days:expr, $frac:expr) => {
484        $crate::Dt::from_jd($jd_days, $frac, $crate::Scale::TAI)
485    };
486    ($jd_days:expr) => {
487        $crate::Dt::from_jd($jd_days, 0, $crate::Scale::TAI)
488    };
489}
490
491/// Builds a **TAI** [`Dt`](crate::Dt) from a floating-point Julian Date.
492///
493/// Sugar for [`Dt::from_jd_f`](crate::Dt::from_jd_f).
494///
495/// Converts from the `on` scale to `TAI` when `on` is not TAI (e.g.
496/// `on=Scale::UTC` applies leap seconds). `on` defaults to
497/// [`Scale::TAI`](crate::Scale::TAI), omit it to skip conversion.
498///
499/// There is no `target=` on this macro — the returned [`Dt`](crate::Dt)'s
500/// `target` is set from `on` (or TAI when omitted), as
501/// [`from_jd_f`](crate::Dt::from_jd_f) does. Chain
502/// [`.target(…)`](crate::Dt::target) if needed.
503///
504/// ## Defaults
505///
506/// | Omitted | Default |
507/// |---------|---------|
508/// | `on` | [`Scale::TAI`](crate::Scale::TAI) |
509///
510/// ## Forms
511///
512/// ```text
513/// from_jd_f!(jd)
514/// from_jd_f!(jd, on=s)
515/// ```
516///
517/// ## Examples
518///
519/// ```
520/// use deep_time::{Dt, Scale, consts::ATTOS_PER_DAY, from_jd_f};
521///
522/// // 2_460_782.25
523/// let a = from_jd_f!(2_460_782.25);
524/// let b = from_jd_f!(2_460_782.25, on=Scale::TAI);
525/// let c = from_jd_f!(2_460_782.0, on=Scale::UTC);
526///
527/// assert_eq!(a, Dt::from_jd_f(2_460_782.25, Scale::TAI));
528/// assert_eq!(b, a);
529/// assert_eq!(c, Dt::from_jd_f(2_460_782.0, Scale::UTC));
530/// assert_eq!(a.to_jd(), (2_460_782, ATTOS_PER_DAY / 4));
531///
532/// // -1_000.25 (signed remainder)
533/// let neg = from_jd_f!(-1_000.25);
534/// assert_eq!(neg, Dt::from_jd_f(-1_000.25, Scale::TAI));
535/// assert_eq!(neg.to_jd(), (-1_000, -ATTOS_PER_DAY / 4));
536/// ```
537#[macro_export]
538macro_rules! from_jd_f {
539    ($jd:expr, on=$scale:expr) => {
540        $crate::Dt::from_jd_f($jd, $scale)
541    };
542    ($jd:expr) => {
543        $crate::Dt::from_jd_f($jd, $crate::Scale::TAI)
544    };
545}
546
547/// Builds a **TAI** [`Dt`](crate::Dt) from a Modified Julian Date (whole days
548/// plus optional attosecond remainder).
549///
550/// Sugar for [`Dt::from_mjd`](crate::Dt::from_mjd).
551///
552/// Converts from the `on` scale to `TAI` when `on` is not TAI (e.g.
553/// `on=Scale::UTC` applies leap seconds). `on` defaults to
554/// [`Scale::TAI`](crate::Scale::TAI), omit it to skip conversion.
555///
556/// There is no `target=` on this macro — the returned [`Dt`](crate::Dt)'s
557/// `target` is set from `on` (or TAI when omitted), as
558/// [`from_mjd`](crate::Dt::from_mjd) does. Chain
559/// [`.target(…)`](crate::Dt::target) if needed.
560///
561/// MJD and JD relate by `JD = MJD + 2_400_000.5`.
562///
563/// The fractional remainder is in **attoseconds** — use a `*_to_attos` helper
564/// (e.g. a day fraction built from [`ATTOS_PER_DAY`](crate::consts::ATTOS_PER_DAY))
565/// instead of hand-counting zeros when convenient.
566///
567/// ## Defaults
568///
569/// | Omitted | Default |
570/// |---------|---------|
571/// | fraction | `0` |
572/// | `on` | [`Scale::TAI`](crate::Scale::TAI) |
573///
574/// ## Forms
575///
576/// ```text
577/// from_mjd!(mjd_days)
578/// from_mjd!(mjd_days, frac)
579/// from_mjd!(mjd_days, on=s)
580/// from_mjd!(mjd_days, frac, on=s)
581/// ```
582///
583/// ## Examples
584///
585/// ```
586/// use deep_time::{Dt, Scale, consts::ATTOS_PER_DAY, from_mjd};
587///
588/// // 60_961.25
589/// let a = from_mjd!(60_961);
590/// let b = from_mjd!(60_961, ATTOS_PER_DAY / 4);
591/// let c = from_mjd!(60_961, ATTOS_PER_DAY / 4, on=Scale::TAI);
592/// let d = from_mjd!(60_961, on=Scale::UTC);
593///
594/// assert_eq!(a, Dt::from_mjd(60_961, 0, Scale::TAI));
595/// assert_eq!(b, Dt::from_mjd(60_961, ATTOS_PER_DAY / 4, Scale::TAI));
596/// assert_eq!(c, b);
597/// assert_eq!(d, Dt::from_mjd(60_961, 0, Scale::UTC));
598///
599/// // -1_000.25 as -1_001 + 0.75 day
600/// let neg = from_mjd!(-1_001, 3 * ATTOS_PER_DAY / 4);
601/// assert_eq!(neg, Dt::from_mjd(-1_001, 3 * ATTOS_PER_DAY / 4, Scale::TAI));
602/// assert_eq!(neg.to_mjd(), (-1_001, 3 * ATTOS_PER_DAY / 4));
603/// ```
604#[macro_export]
605macro_rules! from_mjd {
606    // `on=` arms before bare `$frac:expr` so `on=…` is not taken as an expr.
607    ($mjd_days:expr, $frac:expr, on=$scale:expr) => {
608        $crate::Dt::from_mjd($mjd_days, $frac, $scale)
609    };
610    ($mjd_days:expr, on=$scale:expr) => {
611        $crate::Dt::from_mjd($mjd_days, 0, $scale)
612    };
613    ($mjd_days:expr, $frac:expr) => {
614        $crate::Dt::from_mjd($mjd_days, $frac, $crate::Scale::TAI)
615    };
616    ($mjd_days:expr) => {
617        $crate::Dt::from_mjd($mjd_days, 0, $crate::Scale::TAI)
618    };
619}
620
621/// Builds a **TAI** [`Dt`](crate::Dt) from a floating-point Modified Julian Date.
622///
623/// Sugar for [`Dt::from_mjd_f`](crate::Dt::from_mjd_f).
624///
625/// Converts from the `on` scale to `TAI` when `on` is not TAI (e.g.
626/// `on=Scale::UTC` applies leap seconds). `on` defaults to
627/// [`Scale::TAI`](crate::Scale::TAI), omit it to skip conversion.
628///
629/// There is no `target=` on this macro — the returned [`Dt`](crate::Dt)'s
630/// `target` is set from `on` (or TAI when omitted), as
631/// [`from_mjd_f`](crate::Dt::from_mjd_f) does. Chain
632/// [`.target(…)`](crate::Dt::target) if needed.
633///
634/// MJD and JD relate by `JD = MJD + 2_400_000.5`.
635///
636/// ## Defaults
637///
638/// | Omitted | Default |
639/// |---------|---------|
640/// | `on` | [`Scale::TAI`](crate::Scale::TAI) |
641///
642/// ## Forms
643///
644/// ```text
645/// from_mjd_f!(mjd)
646/// from_mjd_f!(mjd, on=s)
647/// ```
648///
649/// ## Examples
650///
651/// ```
652/// use deep_time::{Dt, Scale, consts::ATTOS_PER_DAY, from_mjd_f};
653///
654/// // 60_961.25
655/// let a = from_mjd_f!(60_961.25);
656/// let b = from_mjd_f!(60_961.25, on=Scale::TAI);
657/// let c = from_mjd_f!(60_961.0, on=Scale::UTC);
658///
659/// assert_eq!(a, Dt::from_mjd_f(60_961.25, Scale::TAI));
660/// assert_eq!(b, a);
661/// assert_eq!(c, Dt::from_mjd_f(60_961.0, Scale::UTC));
662/// assert_eq!(a.to_mjd(), (60_961, ATTOS_PER_DAY / 4));
663///
664/// // -1_000.25 as -1_001 + 0.75 day
665/// let neg = from_mjd_f!(-1_000.25);
666/// assert_eq!(neg, Dt::from_mjd_f(-1_000.25, Scale::TAI));
667/// assert_eq!(neg.to_mjd(), (-1_001, 3 * ATTOS_PER_DAY / 4));
668/// ```
669#[macro_export]
670macro_rules! from_mjd_f {
671    ($mjd:expr, on=$scale:expr) => {
672        $crate::Dt::from_mjd_f($mjd, $scale)
673    };
674    ($mjd:expr) => {
675        $crate::Dt::from_mjd_f($mjd, $crate::Scale::TAI)
676    };
677}
678
679/// Builds a [`Dt`](crate::Dt) from a Gregorian calendar date and optional time.
680///
681/// Sugar for [`Dt::from_ymd`](crate::Dt::from_ymd).
682///
683/// Date fields are positional (`y`, `m`, `d`). Time is optional: put a
684/// **semicolon after the day**, then hour (required if `;` is present), then
685/// optional minute, second, and attoseconds. An optional `on=` civil scale may
686/// follow.
687///
688/// | Omitted field | Default |
689/// |---------------|---------|
690/// | month | `1` |
691/// | day | `1` |
692/// | time (no `;`) | `0, 0, 0, 0` |
693/// | minute / second / attos after `; h` | `0` |
694/// | `on` | [`Scale::UTC`](crate::Scale::UTC) |
695///
696/// The resulting [`Dt`](crate::Dt)'s `target` field is set from that civil
697/// scale (the `on=` value, or UTC when omitted), as
698/// [`from_ymd`](crate::Dt::from_ymd) does. There is no `target=` on
699/// this macro — chain [`.target(…)`](crate::Dt::target) if needed.
700///
701/// ## Forms
702///
703/// ```text
704/// from_ymd!(y)
705/// from_ymd!(y, m)
706/// from_ymd!(y, m, d)
707/// from_ymd!(y, m, d, on=Scale::TAI)
708/// from_ymd!(y, m, d; h)
709/// from_ymd!(y, m, d; h, min)
710/// from_ymd!(y, m, d; h, min, sec)
711/// from_ymd!(y, m, d; h, min, sec, attos)
712/// from_ymd!(y, m, d; h, min, sec, attos, on=Scale::UTC)
713/// ```
714///
715/// ## Examples
716///
717/// ```
718/// use deep_time::Scale;
719/// use deep_time::from_ymd;
720///
721/// assert_eq!(
722///     from_ymd!(1970),
723///     deep_time::Dt::UNIX_EPOCH,
724/// );
725/// assert_eq!(
726///     from_ymd!(2026, 6, 16),
727///     deep_time::Dt::from_ymd(2026, 6, 16, Scale::UTC, 0, 0, 0, 0),
728/// );
729/// assert_eq!(
730///     from_ymd!(2000, 1, 1; 12, on=Scale::TAI),
731///     deep_time::Dt::ZERO,
732/// );
733/// assert_eq!(
734///     from_ymd!(2000, 1, 1; 12, 0, 0, 123_456_789, on=Scale::UTC),
735///     deep_time::Dt::from_ymd(2000, 1, 1, Scale::UTC, 12, 0, 0, 123_456_789),
736/// );
737///
738/// // different target field after construction
739/// let _ = from_ymd!(2020, 1, 1, on=Scale::UTC).target(Scale::TAI);
740/// ```
741#[macro_export]
742macro_rules! from_ymd {
743    // Time section: `d; h …` — `on=` arms before bare so `on=…` is not an `:expr`.
744    ($y:expr, $m:expr, $d:expr; $h:expr, $min:expr, $s:expr, $attos:expr, on=$scale:expr) => {
745        $crate::Dt::from_ymd($y, $m, $d, $scale, $h, $min, $s, $attos)
746    };
747    ($y:expr, $m:expr, $d:expr; $h:expr, $min:expr, $s:expr, on=$scale:expr) => {
748        $crate::Dt::from_ymd($y, $m, $d, $scale, $h, $min, $s, 0)
749    };
750    ($y:expr, $m:expr, $d:expr; $h:expr, $min:expr, on=$scale:expr) => {
751        $crate::Dt::from_ymd($y, $m, $d, $scale, $h, $min, 0, 0)
752    };
753    ($y:expr, $m:expr, $d:expr; $h:expr, on=$scale:expr) => {
754        $crate::Dt::from_ymd($y, $m, $d, $scale, $h, 0, 0, 0)
755    };
756    ($y:expr, $m:expr, $d:expr; $h:expr, $min:expr, $s:expr, $attos:expr) => {
757        $crate::Dt::from_ymd($y, $m, $d, $crate::Scale::UTC, $h, $min, $s, $attos)
758    };
759    ($y:expr, $m:expr, $d:expr; $h:expr, $min:expr, $s:expr) => {
760        $crate::Dt::from_ymd($y, $m, $d, $crate::Scale::UTC, $h, $min, $s, 0)
761    };
762    ($y:expr, $m:expr, $d:expr; $h:expr, $min:expr) => {
763        $crate::Dt::from_ymd($y, $m, $d, $crate::Scale::UTC, $h, $min, 0, 0)
764    };
765    ($y:expr, $m:expr, $d:expr; $h:expr) => {
766        $crate::Dt::from_ymd($y, $m, $d, $crate::Scale::UTC, $h, 0, 0, 0)
767    };
768
769    // Date only (+ optional on=)
770    ($y:expr, $m:expr, $d:expr, on=$scale:expr) => {
771        $crate::Dt::from_ymd($y, $m, $d, $scale, 0, 0, 0, 0)
772    };
773    ($y:expr, $m:expr, on=$scale:expr) => {
774        $crate::Dt::from_ymd($y, $m, 1, $scale, 0, 0, 0, 0)
775    };
776    ($y:expr, on=$scale:expr) => {
777        $crate::Dt::from_ymd($y, 1, 1, $scale, 0, 0, 0, 0)
778    };
779    ($y:expr, $m:expr, $d:expr) => {
780        $crate::Dt::from_ymd($y, $m, $d, $crate::Scale::UTC, 0, 0, 0, 0)
781    };
782    ($y:expr, $m:expr) => {
783        $crate::Dt::from_ymd($y, $m, 1, $crate::Scale::UTC, 0, 0, 0, 0)
784    };
785    ($y:expr) => {
786        $crate::Dt::from_ymd($y, 1, 1, $crate::Scale::UTC, 0, 0, 0, 0)
787    };
788}