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}