Skip to main content

deep_time/
macros.rs

1//! Crate-root convenience macros (`dt!`, `from_sec!`, `from_sec_f!`, `from_ns!`, `from_ms!`, `from_ymd!`, …).
2//!
3//! Optional scale labels use Python-style keyword arguments on **count** macros
4//! (`dt!`, `from_sec!`, `from_sec_f!`, `from_ns!`, `from_ms!`, …):
5//!
6//! - `on=<scale>` — stored as the [`Dt`](struct.Dt.html)'s `scale` field (and as
7//!   `target` when `target=` is omitted).
8//! - `target=<scale>` — stored as the `target` field; `on` defaults to
9//!   [`Scale::TAI`](enum.Scale.html#variant.TAI).
10//!
11//! Either keyword may appear alone or together, in either order.
12//!
13//! [`from_ymd!`](macro.from_ymd.html) only takes a single civil `on=` scale (see
14//! that macro); use [`.target(…)`](struct.Dt.html#method.target) afterward if the
15//! `target` field should differ.
16
17/// Builds a [`Dt`](struct.Dt.html) from total attoseconds with optional scale labels.
18///
19/// Sugar for [`Dt::new`](struct.Dt.html#method.new). Does **not** perform time-scale
20/// conversion.
21///
22/// ## Defaults
23///
24/// | Omitted | Default |
25/// |---------|---------|
26/// | `on` and `target` | both [`Scale::TAI`](enum.Scale.html#variant.TAI) |
27/// | only `on=s` | `target=s` |
28/// | only `target=t` | `on=TAI` |
29///
30/// ## Forms
31///
32/// | Form | Meaning |
33/// |------|---------|
34/// | `dt!(attos)` | both scales TAI |
35/// | `dt!(attos, on=s)` | scale and target `s` |
36/// | `dt!(attos, target=t)` | scale TAI, target `t` |
37/// | `dt!(attos, on=s, target=t)` | either order |
38/// | `dt!(attos, target=t, on=s)` | either order |
39///
40/// ## Examples
41///
42/// ```
43/// use deep_time::Scale;
44/// use deep_time::dt;
45///
46/// let a = dt!(1_000_000_000_000_000_000);
47/// let b = dt!(0, on=Scale::UTC);
48/// let c = dt!(0, on=Scale::TAI, target=Scale::UTC);
49/// let d = dt!(0, target=Scale::UTC, on=Scale::TAI);
50/// let e = dt!(0, target=Scale::UTC);
51///
52/// assert_eq!(a, deep_time::Dt::new(1_000_000_000_000_000_000, Scale::TAI, Scale::TAI));
53/// assert_eq!(b, deep_time::Dt::new(0, Scale::UTC, Scale::UTC));
54/// assert_eq!(c, deep_time::Dt::new(0, Scale::TAI, Scale::UTC));
55/// assert_eq!(d, c);
56/// assert_eq!(e, deep_time::Dt::new(0, Scale::TAI, Scale::UTC));
57/// ```
58#[macro_export]
59macro_rules! dt {
60    ($attos:expr, on=$scale:expr, target=$target:expr) => {
61        $crate::Dt::new($attos, $scale, $target)
62    };
63    ($attos:expr, target=$target:expr, on=$scale:expr) => {
64        $crate::Dt::new($attos, $scale, $target)
65    };
66    ($attos:expr, on=$scale:expr) => {
67        $crate::Dt::new($attos, $scale, $scale)
68    };
69    ($attos:expr, target=$target:expr) => {
70        $crate::Dt::new($attos, $crate::Scale::TAI, $target)
71    };
72    ($attos:expr) => {
73        $crate::Dt::new($attos, $crate::Scale::TAI, $crate::Scale::TAI)
74    };
75}
76
77/// Builds a [`Dt`](struct.Dt.html) from whole seconds and an optional signed
78/// sub-second remainder (attoseconds).
79///
80/// Sugar for [`Dt::from_sec_and_frac`](struct.Dt.html#method.from_sec_and_frac). Does
81/// **not** perform time-scale conversion.
82///
83/// ## Defaults
84///
85/// | Omitted | Default |
86/// |---------|---------|
87/// | fraction | `0` |
88/// | `on` and `target` | both [`Scale::TAI`](enum.Scale.html#variant.TAI) |
89/// | only `on=s` | `target=s` |
90/// | only `target=t` | `on=TAI` |
91///
92/// ## Forms
93///
94/// ```text
95/// from_sec!(sec)
96/// from_sec!(sec, frac)
97/// from_sec!(sec, on=s)
98/// from_sec!(sec, target=t)
99/// from_sec!(sec, on=s, target=t)
100/// from_sec!(sec, target=t, on=s)
101/// from_sec!(sec, frac, on=s)
102/// from_sec!(sec, frac, target=t)
103/// from_sec!(sec, frac, on=s, target=t)
104/// from_sec!(sec, frac, target=t, on=s)
105/// ```
106///
107/// ## Examples
108///
109/// ```
110/// use deep_time::Scale;
111/// use deep_time::{dt, from_sec};
112///
113/// let a = from_sec!(1);
114/// let b = from_sec!(1, 300_000_000_000_000_000);
115/// let c = from_sec!(-1, -300_000_000_000_000_000, on=Scale::TAI);
116/// let d = from_sec!(0, on=Scale::UTC);
117/// let e = from_sec!(1, on=Scale::TAI, target=Scale::UTC);
118/// let f = from_sec!(1, target=Scale::UTC, on=Scale::TAI);
119///
120/// assert_eq!(a, deep_time::Dt::from_sec_and_frac(1, 0, Scale::TAI, Scale::TAI));
121/// assert_eq!(b, deep_time::Dt::from_sec_and_frac(1, 300_000_000_000_000_000, Scale::TAI, Scale::TAI));
122/// assert_eq!(c, deep_time::Dt::from_sec_and_frac(-1, -300_000_000_000_000_000, Scale::TAI, Scale::TAI));
123/// assert_eq!(d, deep_time::Dt::from_sec_and_frac(0, 0, Scale::UTC, Scale::UTC));
124/// assert_eq!(e, deep_time::Dt::from_sec_and_frac(1, 0, Scale::TAI, Scale::UTC));
125/// assert_eq!(f, e);
126///
127/// let signed = from_sec!(-1, -300_000_000_000_000_000);
128/// let floor = from_sec!(-2, 700_000_000_000_000_000);
129/// assert_eq!(signed, floor);
130/// assert_eq!(signed, dt!(-1_300_000_000_000_000_000));
131/// ```
132#[macro_export]
133macro_rules! from_sec {
134    ($sec:expr, $frac:expr, on=$scale:expr, target=$target:expr) => {
135        $crate::Dt::from_sec_and_frac($sec, $frac, $scale, $target)
136    };
137    ($sec:expr, $frac:expr, target=$target:expr, on=$scale:expr) => {
138        $crate::Dt::from_sec_and_frac($sec, $frac, $scale, $target)
139    };
140    ($sec:expr, on=$scale:expr, target=$target:expr) => {
141        $crate::Dt::from_sec_and_frac($sec, 0, $scale, $target)
142    };
143    ($sec:expr, target=$target:expr, on=$scale:expr) => {
144        $crate::Dt::from_sec_and_frac($sec, 0, $scale, $target)
145    };
146    ($sec:expr, $frac:expr, on=$scale:expr) => {
147        $crate::Dt::from_sec_and_frac($sec, $frac, $scale, $scale)
148    };
149    ($sec:expr, $frac:expr, target=$target:expr) => {
150        $crate::Dt::from_sec_and_frac($sec, $frac, $crate::Scale::TAI, $target)
151    };
152    ($sec:expr, on=$scale:expr) => {
153        $crate::Dt::from_sec_and_frac($sec, 0, $scale, $scale)
154    };
155    ($sec:expr, target=$target:expr) => {
156        $crate::Dt::from_sec_and_frac($sec, 0, $crate::Scale::TAI, $target)
157    };
158    ($sec:expr, $frac:expr) => {
159        $crate::Dt::from_sec_and_frac($sec, $frac, $crate::Scale::TAI, $crate::Scale::TAI)
160    };
161    ($sec:expr) => {
162        $crate::Dt::from_sec_and_frac($sec, 0, $crate::Scale::TAI, $crate::Scale::TAI)
163    };
164}
165
166/// Builds a [`Dt`](struct.Dt.html) from a floating-point seconds count with optional
167/// scale labels.
168///
169/// Sugar for [`Dt::from_sec_f`](struct.Dt.html#method.from_sec_f). Does **not** perform
170/// time-scale conversion.
171///
172/// ## Defaults
173///
174/// | Omitted | Default |
175/// |---------|---------|
176/// | `on` and `target` | both [`Scale::TAI`](enum.Scale.html#variant.TAI) |
177/// | only `on=s` | `target=s` |
178/// | only `target=t` | `on=TAI` |
179///
180/// ## Forms
181///
182/// | Form | Meaning |
183/// |------|---------|
184/// | `from_sec_f!(sec)` | both scales TAI |
185/// | `from_sec_f!(sec, on=s)` | scale and target `s` |
186/// | `from_sec_f!(sec, target=t)` | scale TAI, target `t` |
187/// | `from_sec_f!(sec, on=s, target=t)` | either order |
188/// | `from_sec_f!(sec, target=t, on=s)` | either order |
189///
190/// ## Examples
191///
192/// ```
193/// use deep_time::Scale;
194/// use deep_time::from_sec_f;
195///
196/// let a = from_sec_f!(5.5);
197/// let b = from_sec_f!(0.0, on=Scale::UTC);
198/// let c = from_sec_f!(1.0, on=Scale::TAI, target=Scale::UTC);
199/// let d = from_sec_f!(1.0, target=Scale::UTC, on=Scale::TAI);
200///
201/// assert_eq!(a, deep_time::Dt::from_sec_f(5.5, Scale::TAI, Scale::TAI));
202/// assert_eq!(b, deep_time::Dt::from_sec_f(0.0, Scale::UTC, Scale::UTC));
203/// assert_eq!(c, deep_time::Dt::from_sec_f(1.0, Scale::TAI, Scale::UTC));
204/// assert_eq!(d, c);
205/// ```
206#[macro_export]
207macro_rules! from_sec_f {
208    ($sec:expr, on=$scale:expr, target=$target:expr) => {
209        $crate::Dt::from_sec_f($sec, $scale, $target)
210    };
211    ($sec:expr, target=$target:expr, on=$scale:expr) => {
212        $crate::Dt::from_sec_f($sec, $scale, $target)
213    };
214    ($sec:expr, on=$scale:expr) => {
215        $crate::Dt::from_sec_f($sec, $scale, $scale)
216    };
217    ($sec:expr, target=$target:expr) => {
218        $crate::Dt::from_sec_f($sec, $crate::Scale::TAI, $target)
219    };
220    ($sec:expr) => {
221        $crate::Dt::from_sec_f($sec, $crate::Scale::TAI, $crate::Scale::TAI)
222    };
223}
224
225/// Builds a [`Dt`](struct.Dt.html) from whole nanoseconds and an optional signed
226/// fractional remainder in attoseconds.
227///
228/// Sugar for [`Dt::from_ns`](struct.Dt.html#method.from_ns). Does **not** perform
229/// time-scale conversion.
230///
231/// ## Defaults
232///
233/// | Omitted | Default |
234/// |---------|---------|
235/// | fraction | `0` |
236/// | `on` and `target` | both [`Scale::TAI`](enum.Scale.html#variant.TAI) |
237/// | only `on=s` | `target=s` |
238/// | only `target=t` | `on=TAI` |
239///
240/// ## Forms
241///
242/// ```text
243/// from_ns!(ns)
244/// from_ns!(ns, frac)
245/// from_ns!(ns, on=s)
246/// from_ns!(ns, target=t)
247/// from_ns!(ns, on=s, target=t)
248/// from_ns!(ns, target=t, on=s)
249/// from_ns!(ns, frac, on=s)
250/// from_ns!(ns, frac, target=t)
251/// from_ns!(ns, frac, on=s, target=t)
252/// from_ns!(ns, frac, target=t, on=s)
253/// ```
254///
255/// ## Examples
256///
257/// ```
258/// use deep_time::Scale;
259/// use deep_time::from_ns;
260///
261/// // 1 ns = 10⁹ attoseconds; 0.3 ns = 300_000_000 attoseconds
262/// let a = from_ns!(1);
263/// let b = from_ns!(1, 300_000_000);
264/// let c = from_ns!(-1, -300_000_000, on=Scale::TAI);
265/// let d = from_ns!(0, on=Scale::UTC);
266/// let e = from_ns!(1, on=Scale::TAI, target=Scale::UTC);
267/// let f = from_ns!(1, target=Scale::UTC);
268///
269/// assert_eq!(a, deep_time::Dt::from_ns(1, 0, Scale::TAI, Scale::TAI));
270/// assert_eq!(b, deep_time::Dt::from_ns(1, 300_000_000, Scale::TAI, Scale::TAI));
271/// assert_eq!(c, deep_time::Dt::from_ns(-1, -300_000_000, Scale::TAI, Scale::TAI));
272/// assert_eq!(d, deep_time::Dt::from_ns(0, 0, Scale::UTC, Scale::UTC));
273/// assert_eq!(e, deep_time::Dt::from_ns(1, 0, Scale::TAI, Scale::UTC));
274/// assert_eq!(f, e);
275///
276/// let signed = from_ns!(-1, -300_000_000);
277/// let floor = from_ns!(-2, 700_000_000);
278/// assert_eq!(signed, floor);
279/// assert_eq!(signed, deep_time::Dt::from_ns(-1, -300_000_000, Scale::TAI, Scale::TAI));
280/// ```
281#[macro_export]
282macro_rules! from_ns {
283    ($ns:expr, $frac:expr, on=$scale:expr, target=$target:expr) => {
284        $crate::Dt::from_ns($ns, $frac, $scale, $target)
285    };
286    ($ns:expr, $frac:expr, target=$target:expr, on=$scale:expr) => {
287        $crate::Dt::from_ns($ns, $frac, $scale, $target)
288    };
289    ($ns:expr, on=$scale:expr, target=$target:expr) => {
290        $crate::Dt::from_ns($ns, 0, $scale, $target)
291    };
292    ($ns:expr, target=$target:expr, on=$scale:expr) => {
293        $crate::Dt::from_ns($ns, 0, $scale, $target)
294    };
295    ($ns:expr, $frac:expr, on=$scale:expr) => {
296        $crate::Dt::from_ns($ns, $frac, $scale, $scale)
297    };
298    ($ns:expr, $frac:expr, target=$target:expr) => {
299        $crate::Dt::from_ns($ns, $frac, $crate::Scale::TAI, $target)
300    };
301    ($ns:expr, on=$scale:expr) => {
302        $crate::Dt::from_ns($ns, 0, $scale, $scale)
303    };
304    ($ns:expr, target=$target:expr) => {
305        $crate::Dt::from_ns($ns, 0, $crate::Scale::TAI, $target)
306    };
307    ($ns:expr, $frac:expr) => {
308        $crate::Dt::from_ns($ns, $frac, $crate::Scale::TAI, $crate::Scale::TAI)
309    };
310    ($ns:expr) => {
311        $crate::Dt::from_ns($ns, 0, $crate::Scale::TAI, $crate::Scale::TAI)
312    };
313}
314
315/// Builds a [`Dt`](struct.Dt.html) from whole milliseconds and an optional signed
316/// fractional remainder in attoseconds.
317///
318/// Sugar for [`Dt::from_ms`](struct.Dt.html#method.from_ms). Does **not** perform
319/// time-scale conversion.
320///
321/// ## Defaults
322///
323/// | Omitted | Default |
324/// |---------|---------|
325/// | fraction | `0` |
326/// | `on` and `target` | both [`Scale::TAI`](enum.Scale.html#variant.TAI) |
327/// | only `on=s` | `target=s` |
328/// | only `target=t` | `on=TAI` |
329///
330/// ## Forms
331///
332/// ```text
333/// from_ms!(ms)
334/// from_ms!(ms, frac)
335/// from_ms!(ms, on=s)
336/// from_ms!(ms, target=t)
337/// from_ms!(ms, on=s, target=t)
338/// from_ms!(ms, target=t, on=s)
339/// from_ms!(ms, frac, on=s)
340/// from_ms!(ms, frac, target=t)
341/// from_ms!(ms, frac, on=s, target=t)
342/// from_ms!(ms, frac, target=t, on=s)
343/// ```
344///
345/// ## Examples
346///
347/// ```
348/// use deep_time::Scale;
349/// use deep_time::from_ms;
350///
351/// // 1 ms = 10¹⁵ attoseconds; 0.3 ms = 300_000_000_000_000 attoseconds
352/// let a = from_ms!(1);
353/// let b = from_ms!(1, 300_000_000_000_000);
354/// let c = from_ms!(-1, -300_000_000_000_000, on=Scale::TAI);
355/// let d = from_ms!(0, on=Scale::UTC);
356/// let e = from_ms!(1, on=Scale::TAI, target=Scale::UTC);
357/// let f = from_ms!(1, target=Scale::UTC);
358///
359/// assert_eq!(a, deep_time::Dt::from_ms(1, 0, Scale::TAI, Scale::TAI));
360/// assert_eq!(b, deep_time::Dt::from_ms(1, 300_000_000_000_000, Scale::TAI, Scale::TAI));
361/// assert_eq!(c, deep_time::Dt::from_ms(-1, -300_000_000_000_000, Scale::TAI, Scale::TAI));
362/// assert_eq!(d, deep_time::Dt::from_ms(0, 0, Scale::UTC, Scale::UTC));
363/// assert_eq!(e, deep_time::Dt::from_ms(1, 0, Scale::TAI, Scale::UTC));
364/// assert_eq!(f, e);
365///
366/// let signed = from_ms!(-1, -300_000_000_000_000);
367/// let floor = from_ms!(-2, 700_000_000_000_000);
368/// assert_eq!(signed, floor);
369/// assert_eq!(signed, deep_time::Dt::from_ms(-1, -300_000_000_000_000, Scale::TAI, Scale::TAI));
370/// ```
371#[macro_export]
372macro_rules! from_ms {
373    ($ms:expr, $frac:expr, on=$scale:expr, target=$target:expr) => {
374        $crate::Dt::from_ms($ms, $frac, $scale, $target)
375    };
376    ($ms:expr, $frac:expr, target=$target:expr, on=$scale:expr) => {
377        $crate::Dt::from_ms($ms, $frac, $scale, $target)
378    };
379    ($ms:expr, on=$scale:expr, target=$target:expr) => {
380        $crate::Dt::from_ms($ms, 0, $scale, $target)
381    };
382    ($ms:expr, target=$target:expr, on=$scale:expr) => {
383        $crate::Dt::from_ms($ms, 0, $scale, $target)
384    };
385    ($ms:expr, $frac:expr, on=$scale:expr) => {
386        $crate::Dt::from_ms($ms, $frac, $scale, $scale)
387    };
388    ($ms:expr, $frac:expr, target=$target:expr) => {
389        $crate::Dt::from_ms($ms, $frac, $crate::Scale::TAI, $target)
390    };
391    ($ms:expr, on=$scale:expr) => {
392        $crate::Dt::from_ms($ms, 0, $scale, $scale)
393    };
394    ($ms:expr, target=$target:expr) => {
395        $crate::Dt::from_ms($ms, 0, $crate::Scale::TAI, $target)
396    };
397    ($ms:expr, $frac:expr) => {
398        $crate::Dt::from_ms($ms, $frac, $crate::Scale::TAI, $crate::Scale::TAI)
399    };
400    ($ms:expr) => {
401        $crate::Dt::from_ms($ms, 0, $crate::Scale::TAI, $crate::Scale::TAI)
402    };
403}
404
405/// Builds a [`Dt`](struct.Dt.html) from a Gregorian calendar date and optional time.
406///
407/// Sugar for [`Dt::from_ymd`](struct.Dt.html#method.from_ymd).
408///
409/// Date fields are positional (`y`, `m`, `d`). Time is optional: put a
410/// **semicolon after the day**, then hour (required if `;` is present), then
411/// optional minute, second, and attoseconds. An optional `on=` civil scale may
412/// follow.
413///
414/// | Omitted field | Default |
415/// |---------------|---------|
416/// | month | `1` |
417/// | day | `1` |
418/// | time (no `;`) | `0, 0, 0, 0` |
419/// | minute / second / attos after `; h` | `0` |
420/// | `on` | [`Scale::UTC`](enum.Scale.html#variant.UTC) |
421///
422/// The resulting [`Dt`](struct.Dt.html)'s `target` field is set from that civil
423/// scale (the `on=` value, or UTC when omitted), as
424/// [`from_ymd`](struct.Dt.html#method.from_ymd) does. There is no `target=` on
425/// this macro — chain [`.target(…)`](struct.Dt.html#method.target) if needed.
426///
427/// ## Forms
428///
429/// ```text
430/// from_ymd!(y)
431/// from_ymd!(y, m)
432/// from_ymd!(y, m, d)
433/// from_ymd!(y, m, d, on=Scale::TAI)
434/// from_ymd!(y, m, d; h)
435/// from_ymd!(y, m, d; h, min)
436/// from_ymd!(y, m, d; h, min, sec)
437/// from_ymd!(y, m, d; h, min, sec, attos)
438/// from_ymd!(y, m, d; h, min, sec, attos, on=Scale::UTC)
439/// ```
440///
441/// ## Examples
442///
443/// ```
444/// use deep_time::Scale;
445/// use deep_time::from_ymd;
446///
447/// assert_eq!(
448///     from_ymd!(1970),
449///     deep_time::Dt::UNIX_EPOCH,
450/// );
451/// assert_eq!(
452///     from_ymd!(2026, 6, 16),
453///     deep_time::Dt::from_ymd(2026, 6, 16, Scale::UTC, 0, 0, 0, 0),
454/// );
455/// assert_eq!(
456///     from_ymd!(2000, 1, 1; 12, on=Scale::TAI),
457///     deep_time::Dt::ZERO,
458/// );
459/// assert_eq!(
460///     from_ymd!(2000, 1, 1; 12, 0, 0, 123_456_789, on=Scale::UTC),
461///     deep_time::Dt::from_ymd(2000, 1, 1, Scale::UTC, 12, 0, 0, 123_456_789),
462/// );
463///
464/// // different target field after construction
465/// let _ = from_ymd!(2020, 1, 1, on=Scale::UTC).target(Scale::TAI);
466/// ```
467#[macro_export]
468macro_rules! from_ymd {
469    // Time section: `d; h …` — `on=` arms before bare so `on=…` is not an `:expr`.
470    ($y:expr, $m:expr, $d:expr; $h:expr, $min:expr, $s:expr, $attos:expr, on=$scale:expr) => {
471        $crate::Dt::from_ymd($y, $m, $d, $scale, $h, $min, $s, $attos)
472    };
473    ($y:expr, $m:expr, $d:expr; $h:expr, $min:expr, $s:expr, on=$scale:expr) => {
474        $crate::Dt::from_ymd($y, $m, $d, $scale, $h, $min, $s, 0)
475    };
476    ($y:expr, $m:expr, $d:expr; $h:expr, $min:expr, on=$scale:expr) => {
477        $crate::Dt::from_ymd($y, $m, $d, $scale, $h, $min, 0, 0)
478    };
479    ($y:expr, $m:expr, $d:expr; $h:expr, on=$scale:expr) => {
480        $crate::Dt::from_ymd($y, $m, $d, $scale, $h, 0, 0, 0)
481    };
482    ($y:expr, $m:expr, $d:expr; $h:expr, $min:expr, $s:expr, $attos:expr) => {
483        $crate::Dt::from_ymd($y, $m, $d, $crate::Scale::UTC, $h, $min, $s, $attos)
484    };
485    ($y:expr, $m:expr, $d:expr; $h:expr, $min:expr, $s:expr) => {
486        $crate::Dt::from_ymd($y, $m, $d, $crate::Scale::UTC, $h, $min, $s, 0)
487    };
488    ($y:expr, $m:expr, $d:expr; $h:expr, $min:expr) => {
489        $crate::Dt::from_ymd($y, $m, $d, $crate::Scale::UTC, $h, $min, 0, 0)
490    };
491    ($y:expr, $m:expr, $d:expr; $h:expr) => {
492        $crate::Dt::from_ymd($y, $m, $d, $crate::Scale::UTC, $h, 0, 0, 0)
493    };
494
495    // Date only (+ optional on=)
496    ($y:expr, $m:expr, $d:expr, on=$scale:expr) => {
497        $crate::Dt::from_ymd($y, $m, $d, $scale, 0, 0, 0, 0)
498    };
499    ($y:expr, $m:expr, on=$scale:expr) => {
500        $crate::Dt::from_ymd($y, $m, 1, $scale, 0, 0, 0, 0)
501    };
502    ($y:expr, on=$scale:expr) => {
503        $crate::Dt::from_ymd($y, 1, 1, $scale, 0, 0, 0, 0)
504    };
505    ($y:expr, $m:expr, $d:expr) => {
506        $crate::Dt::from_ymd($y, $m, $d, $crate::Scale::UTC, 0, 0, 0, 0)
507    };
508    ($y:expr, $m:expr) => {
509        $crate::Dt::from_ymd($y, $m, 1, $crate::Scale::UTC, 0, 0, 0, 0)
510    };
511    ($y:expr) => {
512        $crate::Dt::from_ymd($y, 1, 1, $crate::Scale::UTC, 0, 0, 0, 0)
513    };
514}