Skip to main content

deep_time/
macros.rs

1//! Macros for easy unit conversion and
2//! [`Dt`](../struct.Dt.html) construction.
3//!
4//! Each macro expands to a call on an equivalent [`Dt`](../struct.Dt.html)
5//! method.
6//!
7//! ## Overview
8//!
9//! ### Unit → attoseconds
10//!
11//! Returns total attoseconds as `i128`.
12//!
13//! - [`fs!`]
14//! - [`ps!`]
15//! - [`ns!`]
16//! - [`us!`]
17//! - [`ms!`]
18//! - [`sec!`]
19//! - [`sec_f!`]
20//! - [`mins!`]
21//! - [`hours!`]
22//! - [`days!`]
23//! - [`days_f!`]
24//! - [`weeks!`]
25//! - [`weeks_f!`]
26//!
27//! ### Attoseconds → unit
28//!
29//! Returns a whole-unit count as `i128`, or a lossy
30//! [`Real`](../type.Real.html) for the `_f` forms.
31//!
32//! - [`as_fs!`]
33//! - [`as_ps!`]
34//! - [`as_ns!`]
35//! - [`as_us!`]
36//! - [`as_ms!`]
37//! - [`as_sec!`]
38//! - [`as_sec_f!`]
39//! - [`as_mins!`]
40//! - [`as_hours!`]
41//! - [`as_days!`]
42//! - [`as_days_f!`]
43//! - [`as_weeks!`]
44//! - [`as_weeks_f!`]
45//!
46//! ### Instant / duration
47//!
48//! Returns a [`Dt`](../struct.Dt.html).
49//!
50//! - [`dt!`]
51//! - [`from_sec!`]
52//! - [`from_sec_f!`]
53//! - [`from_ms!`]
54//! - [`from_us!`]
55//! - [`from_ns!`]
56//! - [`from_ps!`]
57//! - [`from_fs!`]
58//! - [`from_days_f!`]
59//! - [`from_ymd!`]
60//! - [`from_jd!`]
61//! - [`from_jd_f!`]
62//! - [`from_mjd!`]
63//! - [`from_mjd_f!`]
64//!
65//! ## Import paths
66//!
67//! Prefer importing macros from this module:
68//!
69//! ```
70//! use deep_time::macros::{from_ns, ms, sec};
71//! ```
72//!
73//! `use deep_time::macros::*` brings in every macro listed above.
74//! Prefer explicit imports to avoid short-name clashes.
75//!
76//! ## How they work
77//!
78//! - **Attosecond storage unit.** Forward converters such as [`ms!`] and
79//!   [`sec!`] return total attoseconds as `i128`. Reverse converters such as
80//!   [`as_ms!`] and [`as_sec!`] take total attoseconds and return a count in
81//!   the named unit.
82//! - **Truncation toward zero.** Integer reverse converters use ordinary
83//!   `i128` division (`attos / unit`). Any leftover below one whole unit is
84//!   dropped, and the result moves toward zero—not toward −∞. For example,
85//!   −0.5 s as whole seconds is `0`, and −1.5 s is `-1`. The floating reverse
86//!   converters ([`as_sec_f!`], [`as_days_f!`]) are lossy
87//!   [`Real`](crate::Real) casts instead.
88//! - **Signed remainders on constructors.** Macros such as [`from_sec!`] and
89//!   [`from_ns!`] accept an optional fractional remainder in **attoseconds**.
90//!   Both signs of the remainder are valid; the total is
91//!   `whole × unit + frac` (with saturating arithmetic on the underlying
92//!   method). The same total can often be written with a signed remainder or
93//!   as a floor-style split with a non-negative remainder.
94//! - **Scale labels vs conversion.** Most `from_*` macros only set the
95//!   returned [`Dt`](../struct.Dt.html)'s `scale` / `target` fields; they do
96//!   **not** convert the attosecond count between time scales. [`from_ymd!`],
97//!   [`from_jd!`], [`from_jd_f!`], [`from_mjd!`], and [`from_mjd_f!`] are the
98//!   exceptions: they build a [`Dt`](../struct.Dt.html) on TAI (converting
99//!   from the `on=` scale when needed) and set `target` from `on=`, as
100//!   documented on each macro.
101//! - **`const` contexts.** Expansions call `const` methods on
102//!   [`Dt`](../struct.Dt.html), so these macros work in `const` contexts where
103//!   the underlying method does.
104//!
105//! ## Examples
106//!
107//! ```
108//! use deep_time::{Dt, Scale};
109//! use deep_time::macros::{as_sec, dt, from_sec, from_ymd, ms, ns, sec};
110//!
111//! // Unit helpers avoid hand-counting zeros in attosecond literals.
112//! assert_eq!(ms!(300), Dt::ms_to_attos(300));
113//! assert_eq!(ns!(1), 1_000_000_000);
114//!
115//! // Whole seconds + sub-second remainder (remainder is attoseconds).
116//! let a = from_sec!(1, ms!(300));
117//! assert_eq!(a, dt!(1_300_000_000_000_000_000));
118//!
119//! // Reverse conversion truncates toward zero.
120//! assert_eq!(as_sec!(sec!(1) + ms!(900)), 1);
121//! assert_eq!(as_sec!(-ms!(500)), 0);
122//! assert_eq!(as_sec!(-sec!(1) - ms!(500)), -1);
123//!
124//! // Calendar construction: builds a Dt, converting civil time to TAI.
125//! assert_eq!(from_ymd!(2000, 1, 1; 12, on=Scale::TAI), Dt::ZERO);
126//! ```
127//!
128//! See each macro's own documentation for accepted forms, defaults, and
129//! links to the corresponding [`Dt`](../struct.Dt.html) methods.
130
131/// Converts whole femtoseconds (`i128`) to total attoseconds (`i128`).
132///
133/// Equivalent to [`Dt::fs_to_attos`](../struct.Dt.html#method.fs_to_attos).
134///
135/// ## Examples
136///
137/// ```rust
138/// use deep_time::Dt;
139/// use deep_time::macros::fs;
140///
141/// assert_eq!(fs!(1), Dt::fs_to_attos(1));
142/// ```
143#[macro_export]
144macro_rules! fs {
145    ($x:expr) => {
146        $crate::Dt::fs_to_attos($x)
147    };
148}
149
150/// Converts whole picoseconds (`i128`) to total attoseconds (`i128`).
151///
152/// Equivalent to [`Dt::ps_to_attos`](../struct.Dt.html#method.ps_to_attos).
153///
154/// ## Examples
155///
156/// ```rust
157/// use deep_time::Dt;
158/// use deep_time::macros::ps;
159///
160/// assert_eq!(ps!(1), Dt::ps_to_attos(1));
161/// ```
162#[macro_export]
163macro_rules! ps {
164    ($x:expr) => {
165        $crate::Dt::ps_to_attos($x)
166    };
167}
168
169/// Converts whole nanoseconds (`i128`) to total attoseconds (`i128`).
170///
171/// Equivalent to [`Dt::ns_to_attos`](../struct.Dt.html#method.ns_to_attos).
172///
173/// ## Examples
174///
175/// ```rust
176/// use deep_time::Dt;
177/// use deep_time::macros::ns;
178///
179/// assert_eq!(ns!(1), Dt::ns_to_attos(1));
180/// ```
181#[macro_export]
182macro_rules! ns {
183    ($x:expr) => {
184        $crate::Dt::ns_to_attos($x)
185    };
186}
187
188/// Converts whole microseconds (`i128`) to total attoseconds (`i128`).
189///
190/// Equivalent to [`Dt::us_to_attos`](../struct.Dt.html#method.us_to_attos).
191///
192/// ## Examples
193///
194/// ```rust
195/// use deep_time::Dt;
196/// use deep_time::macros::us;
197///
198/// assert_eq!(us!(1), Dt::us_to_attos(1));
199/// ```
200#[macro_export]
201macro_rules! us {
202    ($x:expr) => {
203        $crate::Dt::us_to_attos($x)
204    };
205}
206
207/// Converts whole milliseconds (`i128`) to total attoseconds (`i128`).
208///
209/// Equivalent to [`Dt::ms_to_attos`](../struct.Dt.html#method.ms_to_attos).
210///
211/// ## Examples
212///
213/// ```rust
214/// use deep_time::Dt;
215/// use deep_time::macros::ms;
216///
217/// assert_eq!(ms!(1), Dt::ms_to_attos(1));
218/// ```
219#[macro_export]
220macro_rules! ms {
221    ($x:expr) => {
222        $crate::Dt::ms_to_attos($x)
223    };
224}
225
226/// Converts whole seconds (`i128`) to total attoseconds (`i128`).
227///
228/// Equivalent to [`Dt::sec_to_attos`](../struct.Dt.html#method.sec_to_attos).
229///
230/// ## Examples
231///
232/// ```rust
233/// use deep_time::Dt;
234/// use deep_time::macros::sec;
235///
236/// assert_eq!(sec!(1), Dt::sec_to_attos(1));
237/// ```
238#[macro_export]
239macro_rules! sec {
240    ($x:expr) => {
241        $crate::Dt::sec_to_attos($x)
242    };
243}
244
245/// Converts a floating-point second count ([`Real`](crate::Real)) to
246/// total attoseconds (`i128`).
247///
248/// Equivalent to [`Dt::sec_f_to_attos`](../struct.Dt.html#method.sec_f_to_attos).
249///
250/// ## Examples
251///
252/// ```rust
253/// use deep_time::Dt;
254/// use deep_time::macros::sec_f;
255///
256/// assert_eq!(sec_f!(1.5), Dt::sec_f_to_attos(1.5));
257/// ```
258#[macro_export]
259macro_rules! sec_f {
260    ($x:expr) => {
261        $crate::Dt::sec_f_to_attos($x)
262    };
263}
264
265/// Converts whole minutes (`i128`) to total attoseconds (`i128`).
266///
267/// Equivalent to [`Dt::mins_to_attos`](../struct.Dt.html#method.mins_to_attos).
268///
269/// ## Examples
270///
271/// ```rust
272/// use deep_time::Dt;
273/// use deep_time::macros::mins;
274///
275/// assert_eq!(mins!(1), Dt::mins_to_attos(1));
276/// ```
277#[macro_export]
278macro_rules! mins {
279    ($x:expr) => {
280        $crate::Dt::mins_to_attos($x)
281    };
282}
283
284/// Converts whole hours (`i128`) to total attoseconds (`i128`).
285///
286/// Equivalent to [`Dt::hours_to_attos`](../struct.Dt.html#method.hours_to_attos).
287///
288/// ## Examples
289///
290/// ```rust
291/// use deep_time::Dt;
292/// use deep_time::macros::hours;
293///
294/// assert_eq!(hours!(1), Dt::hours_to_attos(1));
295/// ```
296#[macro_export]
297macro_rules! hours {
298    ($x:expr) => {
299        $crate::Dt::hours_to_attos($x)
300    };
301}
302
303/// Converts whole days (`i128`) to total attoseconds (`i128`).
304///
305/// Equivalent to [`Dt::days_to_attos`](../struct.Dt.html#method.days_to_attos).
306///
307/// ## Examples
308///
309/// ```rust
310/// use deep_time::Dt;
311/// use deep_time::macros::days;
312///
313/// assert_eq!(days!(1), Dt::days_to_attos(1));
314/// ```
315#[macro_export]
316macro_rules! days {
317    ($x:expr) => {
318        $crate::Dt::days_to_attos($x)
319    };
320}
321
322/// Converts a floating-point day count ([`Real`](crate::Real)) to total attoseconds (`i128`).
323///
324/// Equivalent to [`Dt::days_f_to_attos`](../struct.Dt.html#method.days_f_to_attos).
325///
326/// ## Examples
327///
328/// ```rust
329/// use deep_time::Dt;
330/// use deep_time::macros::days_f;
331///
332/// assert_eq!(days_f!(0.25), Dt::days_f_to_attos(0.25));
333/// ```
334#[macro_export]
335macro_rules! days_f {
336    ($x:expr) => {
337        $crate::Dt::days_f_to_attos($x)
338    };
339}
340
341/// Converts whole weeks (`i128`) to total attoseconds (`i128`).
342///
343/// Equivalent to [`Dt::weeks_to_attos`](../struct.Dt.html#method.weeks_to_attos).
344///
345/// ## Examples
346///
347/// ```rust
348/// use deep_time::Dt;
349/// use deep_time::macros::weeks;
350///
351/// assert_eq!(weeks!(1), Dt::weeks_to_attos(1));
352/// ```
353#[macro_export]
354macro_rules! weeks {
355    ($x:expr) => {
356        $crate::Dt::weeks_to_attos($x)
357    };
358}
359
360/// Converts a floating-point week count ([`Real`](crate::Real)) to total attoseconds (`i128`).
361///
362/// Equivalent to [`Dt::weeks_f_to_attos`](../struct.Dt.html#method.weeks_f_to_attos).
363///
364/// A week is 7 civil days (`604800` seconds).
365///
366/// ## Examples
367///
368/// ```rust
369/// use deep_time::Dt;
370/// use deep_time::macros::weeks_f;
371///
372/// assert_eq!(weeks_f!(0.5), Dt::weeks_f_to_attos(0.5));
373/// ```
374#[macro_export]
375macro_rules! weeks_f {
376    ($x:expr) => {
377        $crate::Dt::weeks_f_to_attos($x)
378    };
379}
380
381/// Converts total attoseconds (`i128`) → whole femtoseconds (`i128`).
382///
383/// Equivalent to [`Dt::attos_to_fs`](../struct.Dt.html#method.attos_to_fs).
384///
385/// Truncates toward zero.
386///
387/// Half a femtosecond is `500` attoseconds (no smaller named unit macro).
388///
389/// ## Examples
390///
391/// Example shows attos inputs being built with macros rather than counting
392/// attosecond zeros by hand.
393///
394/// ```rust
395/// use deep_time::macros::{as_fs, fs};
396///
397/// // an amount of attoseconds that is equal to
398/// // −1.5 fs becomes −1 femtoseconds
399/// assert_eq!(as_fs!(-fs!(1) - 500), -1);
400/// ```
401#[macro_export]
402macro_rules! as_fs {
403    ($x:expr) => {
404        $crate::Dt::attos_to_fs($x)
405    };
406}
407
408/// Converts total attoseconds (`i128`) → whole picoseconds (`i128`).
409///
410/// Equivalent to [`Dt::attos_to_ps`](../struct.Dt.html#method.attos_to_ps).
411///
412/// Truncates toward zero.
413///
414/// ## Examples
415///
416/// Example shows attos inputs being built with macros rather than counting
417/// attosecond zeros by hand.
418///
419/// ```rust
420/// use deep_time::macros::{as_ps, fs, ps};
421///
422/// // an amount of attoseconds that is equal to
423/// // −1.5 ps becomes −1 picoseconds
424/// assert_eq!(as_ps!(-ps!(1) - fs!(500)), -1);
425/// ```
426#[macro_export]
427macro_rules! as_ps {
428    ($x:expr) => {
429        $crate::Dt::attos_to_ps($x)
430    };
431}
432
433/// Converts total attoseconds (`i128`) → whole nanoseconds (`i128`).
434///
435/// Equivalent to [`Dt::attos_to_ns`](../struct.Dt.html#method.attos_to_ns).
436///
437/// Truncates toward zero.
438///
439/// ## Examples
440///
441/// Example shows attos inputs being built with macros rather than counting
442/// attosecond zeros by hand.
443///
444/// ```rust
445/// use deep_time::macros::{as_ns, ns, ps};
446///
447/// // an amount of attoseconds that is equal to
448/// // −1.5 ns becomes −1 nanoseconds
449/// assert_eq!(as_ns!(-ns!(1) - ps!(500)), -1);
450/// ```
451#[macro_export]
452macro_rules! as_ns {
453    ($x:expr) => {
454        $crate::Dt::attos_to_ns($x)
455    };
456}
457
458/// Converts total attoseconds (`i128`) → whole microseconds (`i128`).
459///
460/// Equivalent to [`Dt::attos_to_us`](../struct.Dt.html#method.attos_to_us).
461///
462/// Truncates toward zero.
463///
464/// ## Examples
465///
466/// Example shows attos inputs being built with macros rather than counting
467/// attosecond zeros by hand.
468///
469/// ```rust
470/// use deep_time::macros::{as_us, ns, us};
471///
472/// // an amount of attoseconds that is equal to
473/// // −1.5 µs becomes −1 microseconds
474/// assert_eq!(as_us!(-us!(1) - ns!(500)), -1);
475/// ```
476#[macro_export]
477macro_rules! as_us {
478    ($x:expr) => {
479        $crate::Dt::attos_to_us($x)
480    };
481}
482
483/// Converts total attoseconds (`i128`) → whole milliseconds (`i128`).
484///
485/// Equivalent to [`Dt::attos_to_ms`](../struct.Dt.html#method.attos_to_ms).
486///
487/// Truncates toward zero.
488///
489/// ## Examples
490///
491/// Example shows attos inputs being built with macros rather than counting
492/// attosecond zeros by hand.
493///
494/// ```rust
495/// use deep_time::macros::{as_ms, ms, us};
496///
497/// // an amount of attoseconds that is equal to
498/// // −1.5 ms becomes −1 milliseconds
499/// assert_eq!(as_ms!(-ms!(1) - us!(500)), -1);
500/// ```
501#[macro_export]
502macro_rules! as_ms {
503    ($x:expr) => {
504        $crate::Dt::attos_to_ms($x)
505    };
506}
507
508/// Converts total attoseconds (`i128`) → whole seconds (`i128`).
509///
510/// Equivalent to [`Dt::attos_to_sec`](../struct.Dt.html#method.attos_to_sec).
511///
512/// Truncates toward zero.
513///
514/// ## Examples
515///
516/// Example shows attos inputs being built with macros rather than counting
517/// attosecond zeros by hand.
518///
519/// ```rust
520/// use deep_time::macros::{as_sec, ms, sec};
521///
522/// // an amount of attoseconds that is equal to
523/// // −1.5 s becomes −1 seconds
524/// assert_eq!(as_sec!(-sec!(1) - ms!(500)), -1);
525/// ```
526#[macro_export]
527macro_rules! as_sec {
528    ($x:expr) => {
529        $crate::Dt::attos_to_sec($x)
530    };
531}
532
533/// Converts total attoseconds (`i128`) → lossy float seconds ([`Real`](crate::Real)).
534///
535/// Equivalent to [`Dt::attos_to_sec_f`](../struct.Dt.html#method.attos_to_sec_f).
536///
537/// ## Examples
538///
539/// Example shows attos inputs being built with macros rather than counting
540/// attosecond zeros by hand.
541///
542/// ```rust
543/// use deep_time::macros::{as_sec_f, ms, sec};
544///
545/// // an amount of attoseconds that is equal to
546/// // −1.5 s becomes -1.5 seconds
547/// assert_eq!(as_sec_f!(-sec!(1) - ms!(500)), -1.5);
548/// ```
549#[macro_export]
550macro_rules! as_sec_f {
551    ($x:expr) => {
552        $crate::Dt::attos_to_sec_f($x)
553    };
554}
555
556/// Converts total attoseconds (`i128`) → whole minutes (`i128`).
557///
558/// Equivalent to [`Dt::attos_to_mins`](../struct.Dt.html#method.attos_to_mins).
559///
560/// Truncates toward zero.
561///
562/// ## Examples
563///
564/// Example shows attos inputs being built with macros rather than counting
565/// attosecond zeros by hand.
566///
567/// ```rust
568/// use deep_time::macros::{as_mins, mins, sec};
569///
570/// // an amount of attoseconds that is equal to
571/// // −1.5 min becomes −1 minutes
572/// assert_eq!(as_mins!(-mins!(1) - sec!(30)), -1);
573/// ```
574#[macro_export]
575macro_rules! as_mins {
576    ($x:expr) => {
577        $crate::Dt::attos_to_mins($x)
578    };
579}
580
581/// Converts total attoseconds (`i128`) → whole hours (`i128`).
582///
583/// Equivalent to [`Dt::attos_to_hours`](../struct.Dt.html#method.attos_to_hours).
584///
585/// Truncates toward zero.
586///
587/// ## Examples
588///
589/// Example shows attos inputs being built with macros rather than counting
590/// attosecond zeros by hand.
591///
592/// ```rust
593/// use deep_time::macros::{as_hours, hours, mins};
594///
595/// // an amount of attoseconds that is equal to
596/// // −1.5 h becomes −1 hours
597/// assert_eq!(as_hours!(-hours!(1) - mins!(30)), -1);
598/// ```
599#[macro_export]
600macro_rules! as_hours {
601    ($x:expr) => {
602        $crate::Dt::attos_to_hours($x)
603    };
604}
605
606/// Converts total attoseconds (`i128`) → whole days (`i128`).
607///
608/// Equivalent to [`Dt::attos_to_days`](../struct.Dt.html#method.attos_to_days).
609///
610/// Truncates toward zero.
611///
612/// ## Examples
613///
614/// Example shows attos inputs being built with macros rather than counting
615/// attosecond zeros by hand.
616///
617/// ```rust
618/// use deep_time::macros::{as_days, days, hours};
619///
620/// // an amount of attoseconds that is equal to
621/// // −1.5 d becomes −1 days
622/// assert_eq!(as_days!(-days!(1) - hours!(12)), -1);
623/// ```
624#[macro_export]
625macro_rules! as_days {
626    ($x:expr) => {
627        $crate::Dt::attos_to_days($x)
628    };
629}
630
631/// Converts total attoseconds (`i128`) → lossy float days ([`Real`](crate::Real)).
632///
633/// Equivalent to [`Dt::attos_to_days_f`](../struct.Dt.html#method.attos_to_days_f).
634///
635/// ## Examples
636///
637/// Example shows attos inputs being built with macros rather than counting
638/// attosecond zeros by hand.
639///
640/// ```rust
641/// use deep_time::macros::{as_days_f, days, hours};
642///
643/// // an amount of attoseconds that is equal to
644/// // −1.5 d becomes -1.5 days
645/// assert_eq!(as_days_f!(-days!(1) - hours!(12)), -1.5);
646/// ```
647#[macro_export]
648macro_rules! as_days_f {
649    ($x:expr) => {
650        $crate::Dt::attos_to_days_f($x)
651    };
652}
653
654/// Converts total attoseconds (`i128`) → whole weeks (`i128`).
655///
656/// Equivalent to [`Dt::attos_to_weeks`](../struct.Dt.html#method.attos_to_weeks).
657///
658/// Truncates toward zero.
659///
660/// ## Examples
661///
662/// Example shows attos inputs being built with macros rather than counting
663/// attosecond zeros by hand.
664///
665/// ```rust
666/// use deep_time::macros::{as_weeks, days, hours, weeks};
667///
668/// // an amount of attoseconds that is equal to
669/// // −1.5 wk becomes −1 weeks
670/// assert_eq!(as_weeks!(-weeks!(1) - days!(3) - hours!(12)), -1);
671/// ```
672#[macro_export]
673macro_rules! as_weeks {
674    ($x:expr) => {
675        $crate::Dt::attos_to_weeks($x)
676    };
677}
678
679/// Converts total attoseconds (`i128`) → lossy float weeks ([`Real`](crate::Real)).
680///
681/// Equivalent to [`Dt::attos_to_weeks_f`](../struct.Dt.html#method.attos_to_weeks_f).
682///
683/// A week is 7 civil days (`604800` seconds).
684///
685/// ## Examples
686///
687/// Example shows attos inputs being built with macros rather than counting
688/// attosecond zeros by hand.
689///
690/// ```rust
691/// use deep_time::macros::{as_weeks_f, days, hours, weeks};
692///
693/// // an amount of attoseconds that is equal to
694/// // −1.5 wk becomes -1.5 weeks
695/// assert_eq!(as_weeks_f!(-weeks!(1) - days!(3) - hours!(12)), -1.5);
696/// ```
697#[macro_export]
698macro_rules! as_weeks_f {
699    ($x:expr) => {
700        $crate::Dt::attos_to_weeks_f($x)
701    };
702}
703
704/// Builds a [`Dt`](../struct.Dt.html) from total attoseconds with optional scale labels.
705///
706/// - Equivalent to [`Dt::new`](../struct.Dt.html#method.new).
707/// - Does **not** perform time-scale conversion.
708///
709/// ## Defaults
710///
711/// | Omitted | Default |
712/// |---------|---------|
713/// | `on` and `target` | both [`Scale::TAI`](crate::Scale::TAI) |
714/// | only `on=s` | `target=s` |
715/// | only `target=t` | `on=TAI` |
716///
717/// ## Forms
718///
719/// | Form | Meaning |
720/// |------|---------|
721/// | `dt!(attos)` | both scales TAI |
722/// | `dt!(attos, on=s)` | scale and target `s` |
723/// | `dt!(attos, target=t)` | scale TAI, target `t` |
724/// | `dt!(attos, on=s, target=t)` | either order |
725/// | `dt!(attos, target=t, on=s)` | either order |
726///
727/// ## Examples
728///
729/// ```rust
730/// use deep_time::{Dt, Scale};
731/// use deep_time::macros::dt;
732///
733/// let a = dt!(Dt::sec_to_attos(1));
734/// let b = dt!(0, on=Scale::UTC);
735/// let c = dt!(0, on=Scale::TAI, target=Scale::UTC);
736/// let d = dt!(0, target=Scale::UTC, on=Scale::TAI);
737/// let e = dt!(0, target=Scale::UTC);
738///
739/// assert_eq!(a, Dt::new(Dt::sec_to_attos(1), Scale::TAI, Scale::TAI));
740/// assert_eq!(b, Dt::new(0, Scale::UTC, Scale::UTC));
741/// assert_eq!(c, Dt::new(0, Scale::TAI, Scale::UTC));
742/// assert_eq!(d, c);
743/// assert_eq!(e, Dt::new(0, Scale::TAI, Scale::UTC));
744/// ```
745#[macro_export]
746macro_rules! dt {
747    ($attos:expr, on=$scale:expr, target=$target:expr) => {
748        $crate::Dt::new($attos, $scale, $target)
749    };
750    ($attos:expr, target=$target:expr, on=$scale:expr) => {
751        $crate::Dt::new($attos, $scale, $target)
752    };
753    ($attos:expr, on=$scale:expr) => {
754        $crate::Dt::new($attos, $scale, $scale)
755    };
756    ($attos:expr, target=$target:expr) => {
757        $crate::Dt::new($attos, $crate::Scale::TAI, $target)
758    };
759    ($attos:expr) => {
760        $crate::Dt::new($attos, $crate::Scale::TAI, $crate::Scale::TAI)
761    };
762}
763
764/// Builds a [`Dt`](../struct.Dt.html) from whole seconds and an optional signed
765/// sub-second remainder (attoseconds).
766///
767/// - Equivalent to [`Dt::from_sec_and_frac`](../struct.Dt.html#method.from_sec_and_frac).
768/// - Does **not** perform time-scale conversion.
769///
770/// The fractional remainder is in **attoseconds** — use
771/// [`ms!`](../macro.ms.html) (or another `*_to_attos`
772/// helper) instead of writing the attosecond literal by hand.
773///
774/// ## Defaults
775///
776/// | Omitted | Default |
777/// |---------|---------|
778/// | fraction | `0` |
779/// | `on` and `target` | both [`Scale::TAI`](crate::Scale::TAI) |
780/// | only `on=s` | `target=s` |
781/// | only `target=t` | `on=TAI` |
782///
783/// ## Forms
784///
785/// ```text
786/// from_sec!(sec)
787/// from_sec!(sec, frac)
788/// from_sec!(sec, on=s)
789/// from_sec!(sec, target=t)
790/// from_sec!(sec, on=s, target=t)
791/// from_sec!(sec, target=t, on=s)
792/// from_sec!(sec, frac, on=s)
793/// from_sec!(sec, frac, target=t)
794/// from_sec!(sec, frac, on=s, target=t)
795/// from_sec!(sec, frac, target=t, on=s)
796/// ```
797///
798/// ## Examples
799///
800/// ```
801/// use deep_time::{Dt, Scale};
802/// use deep_time::macros::{from_sec, ms};
803///
804/// // 1.3 s → whole seconds + 300 ms remainder
805/// let a = from_sec!(1);
806/// let b = from_sec!(1, ms!(300));
807/// let c = from_sec!(-1, ms!(-300), on=Scale::TAI);
808/// let d = from_sec!(0, on=Scale::UTC);
809/// let e = from_sec!(1, on=Scale::TAI, target=Scale::UTC);
810/// let f = from_sec!(1, target=Scale::UTC, on=Scale::TAI);
811///
812/// assert_eq!(a, Dt::from_sec_and_frac(1, 0, Scale::TAI, Scale::TAI));
813/// assert_eq!(b, Dt::from_sec_and_frac(1, ms!(300), Scale::TAI, Scale::TAI));
814/// assert_eq!(c, Dt::from_sec_and_frac(-1, ms!(-300), Scale::TAI, Scale::TAI));
815/// assert_eq!(d, Dt::from_sec_and_frac(0, 0, Scale::UTC, Scale::UTC));
816/// assert_eq!(e, Dt::from_sec_and_frac(1, 0, Scale::TAI, Scale::UTC));
817/// assert_eq!(f, e);
818///
819/// let signed = from_sec!(-1, ms!(-300));
820/// let floored = from_sec!(-2, ms!(700));
821/// assert_eq!(signed, floored);
822/// assert_eq!(signed, Dt::from_sec_and_frac(-1, ms!(-300), Scale::TAI, Scale::TAI));
823/// ```
824#[macro_export]
825macro_rules! from_sec {
826    ($sec:expr, $frac:expr, on=$scale:expr, target=$target:expr) => {
827        $crate::Dt::from_sec_and_frac($sec, $frac, $scale, $target)
828    };
829    ($sec:expr, $frac:expr, target=$target:expr, on=$scale:expr) => {
830        $crate::Dt::from_sec_and_frac($sec, $frac, $scale, $target)
831    };
832    ($sec:expr, on=$scale:expr, target=$target:expr) => {
833        $crate::Dt::from_sec_and_frac($sec, 0, $scale, $target)
834    };
835    ($sec:expr, target=$target:expr, on=$scale:expr) => {
836        $crate::Dt::from_sec_and_frac($sec, 0, $scale, $target)
837    };
838    ($sec:expr, $frac:expr, on=$scale:expr) => {
839        $crate::Dt::from_sec_and_frac($sec, $frac, $scale, $scale)
840    };
841    ($sec:expr, $frac:expr, target=$target:expr) => {
842        $crate::Dt::from_sec_and_frac($sec, $frac, $crate::Scale::TAI, $target)
843    };
844    ($sec:expr, on=$scale:expr) => {
845        $crate::Dt::from_sec_and_frac($sec, 0, $scale, $scale)
846    };
847    ($sec:expr, target=$target:expr) => {
848        $crate::Dt::from_sec_and_frac($sec, 0, $crate::Scale::TAI, $target)
849    };
850    ($sec:expr, $frac:expr) => {
851        $crate::Dt::from_sec_and_frac($sec, $frac, $crate::Scale::TAI, $crate::Scale::TAI)
852    };
853    ($sec:expr) => {
854        $crate::Dt::from_sec_and_frac($sec, 0, $crate::Scale::TAI, $crate::Scale::TAI)
855    };
856}
857
858/// Builds a [`Dt`](../struct.Dt.html) from a floating-point seconds count with optional
859/// scale labels.
860///
861/// - Equivalent to [`Dt::from_sec_f`](../struct.Dt.html#method.from_sec_f).
862/// - Does **not** perform time-scale conversion.
863///
864/// ## Defaults
865///
866/// | Omitted | Default |
867/// |---------|---------|
868/// | `on` and `target` | both [`Scale::TAI`](crate::Scale::TAI) |
869/// | only `on=s` | `target=s` |
870/// | only `target=t` | `on=TAI` |
871///
872/// ## Forms
873///
874/// | Form | Meaning |
875/// |------|---------|
876/// | `from_sec_f!(sec)` | both scales TAI |
877/// | `from_sec_f!(sec, on=s)` | scale and target `s` |
878/// | `from_sec_f!(sec, target=t)` | scale TAI, target `t` |
879/// | `from_sec_f!(sec, on=s, target=t)` | either order |
880/// | `from_sec_f!(sec, target=t, on=s)` | either order |
881///
882/// ## Examples
883///
884/// ```
885/// use deep_time::Scale;
886/// use deep_time::macros::from_sec_f;
887///
888/// let a = from_sec_f!(5.5);
889/// let b = from_sec_f!(0.0, on=Scale::UTC);
890/// let c = from_sec_f!(1.0, on=Scale::TAI, target=Scale::UTC);
891/// let d = from_sec_f!(1.0, target=Scale::UTC, on=Scale::TAI);
892///
893/// assert_eq!(a, deep_time::Dt::from_sec_f(5.5, Scale::TAI, Scale::TAI));
894/// assert_eq!(b, deep_time::Dt::from_sec_f(0.0, Scale::UTC, Scale::UTC));
895/// assert_eq!(c, deep_time::Dt::from_sec_f(1.0, Scale::TAI, Scale::UTC));
896/// assert_eq!(d, c);
897/// ```
898#[macro_export]
899macro_rules! from_sec_f {
900    ($sec:expr, on=$scale:expr, target=$target:expr) => {
901        $crate::Dt::from_sec_f($sec, $scale, $target)
902    };
903    ($sec:expr, target=$target:expr, on=$scale:expr) => {
904        $crate::Dt::from_sec_f($sec, $scale, $target)
905    };
906    ($sec:expr, on=$scale:expr) => {
907        $crate::Dt::from_sec_f($sec, $scale, $scale)
908    };
909    ($sec:expr, target=$target:expr) => {
910        $crate::Dt::from_sec_f($sec, $crate::Scale::TAI, $target)
911    };
912    ($sec:expr) => {
913        $crate::Dt::from_sec_f($sec, $crate::Scale::TAI, $crate::Scale::TAI)
914    };
915}
916
917/// Builds a [`Dt`](../struct.Dt.html) from a floating-point day count with optional
918/// scale labels.
919///
920/// - Equivalent to [`Dt::from_days_f`](../struct.Dt.html#method.from_days_f).
921/// - Does **not** perform time-scale conversion.
922///
923/// ## Defaults
924///
925/// | Omitted | Default |
926/// |---------|---------|
927/// | `on` and `target` | both [`Scale::TAI`](crate::Scale::TAI) |
928/// | only `on=s` | `target=s` |
929/// | only `target=t` | `on=TAI` |
930///
931/// ## Forms
932///
933/// | Form | Meaning |
934/// |------|---------|
935/// | `from_days_f!(days)` | both scales TAI |
936/// | `from_days_f!(days, on=s)` | scale and target `s` |
937/// | `from_days_f!(days, target=t)` | scale TAI, target `t` |
938/// | `from_days_f!(days, on=s, target=t)` | either order |
939/// | `from_days_f!(days, target=t, on=s)` | either order |
940///
941/// ## Examples
942///
943/// ```
944/// use deep_time::Scale;
945/// use deep_time::macros::from_days_f;
946///
947/// let a = from_days_f!(1.25);
948/// let b = from_days_f!(0.0, on=Scale::UTC);
949/// let c = from_days_f!(1.0, on=Scale::TAI, target=Scale::UTC);
950///
951/// assert_eq!(a, deep_time::Dt::from_days_f(1.25, Scale::TAI, Scale::TAI));
952/// assert_eq!(b, deep_time::Dt::from_days_f(0.0, Scale::UTC, Scale::UTC));
953/// assert_eq!(c, deep_time::Dt::from_days_f(1.0, Scale::TAI, Scale::UTC));
954/// ```
955#[macro_export]
956macro_rules! from_days_f {
957    ($days:expr, on=$scale:expr, target=$target:expr) => {
958        $crate::Dt::from_days_f($days, $scale, $target)
959    };
960    ($days:expr, target=$target:expr, on=$scale:expr) => {
961        $crate::Dt::from_days_f($days, $scale, $target)
962    };
963    ($days:expr, on=$scale:expr) => {
964        $crate::Dt::from_days_f($days, $scale, $scale)
965    };
966    ($days:expr, target=$target:expr) => {
967        $crate::Dt::from_days_f($days, $crate::Scale::TAI, $target)
968    };
969    ($days:expr) => {
970        $crate::Dt::from_days_f($days, $crate::Scale::TAI, $crate::Scale::TAI)
971    };
972}
973
974/// Builds a [`Dt`](../struct.Dt.html) from whole nanoseconds and an optional signed
975/// fractional remainder in attoseconds.
976///
977/// - Equivalent to [`Dt::from_ns`](../struct.Dt.html#method.from_ns).
978/// - Does **not** perform time-scale conversion.
979///
980/// The fractional remainder is in **attoseconds** — use
981/// [`ps!`](macro.ps.html) (or another `*_to_attos`
982/// helper) instead of writing the attosecond literal by hand.
983///
984/// ## Defaults
985///
986/// | Omitted | Default |
987/// |---------|---------|
988/// | fraction | `0` |
989/// | `on` and `target` | both [`Scale::TAI`](crate::Scale::TAI) |
990/// | only `on=s` | `target=s` |
991/// | only `target=t` | `on=TAI` |
992///
993/// ## Forms
994///
995/// ```text
996/// from_ns!(ns)
997/// from_ns!(ns, frac)
998/// from_ns!(ns, on=s)
999/// from_ns!(ns, target=t)
1000/// from_ns!(ns, on=s, target=t)
1001/// from_ns!(ns, target=t, on=s)
1002/// from_ns!(ns, frac, on=s)
1003/// from_ns!(ns, frac, target=t)
1004/// from_ns!(ns, frac, on=s, target=t)
1005/// from_ns!(ns, frac, target=t, on=s)
1006/// ```
1007///
1008/// ## Examples
1009///
1010/// ```
1011/// use deep_time::{Dt, Scale};
1012/// use deep_time::macros::{from_ns, ps};
1013///
1014/// // 1.3 ns → whole nanoseconds + 300 ps remainder
1015/// let a = from_ns!(1);
1016/// let b = from_ns!(1, ps!(300));
1017/// let c = from_ns!(-1, ps!(-300), on=Scale::TAI);
1018/// let d = from_ns!(0, on=Scale::UTC);
1019/// let e = from_ns!(1, on=Scale::TAI, target=Scale::UTC);
1020/// let f = from_ns!(1, target=Scale::UTC);
1021///
1022/// assert_eq!(a, Dt::from_ns(1, 0, Scale::TAI, Scale::TAI));
1023/// assert_eq!(b, Dt::from_ns(1, ps!(300), Scale::TAI, Scale::TAI));
1024/// assert_eq!(c, Dt::from_ns(-1, ps!(-300), Scale::TAI, Scale::TAI));
1025/// assert_eq!(d, Dt::from_ns(0, 0, Scale::UTC, Scale::UTC));
1026/// assert_eq!(e, Dt::from_ns(1, 0, Scale::TAI, Scale::UTC));
1027/// assert_eq!(f, e);
1028///
1029/// let signed = from_ns!(-1, ps!(-300));
1030/// let floor = from_ns!(-2, ps!(700));
1031/// assert_eq!(signed, floor);
1032/// assert_eq!(signed, Dt::from_ns(-1, ps!(-300), Scale::TAI, Scale::TAI));
1033/// ```
1034#[macro_export]
1035macro_rules! from_ns {
1036    ($ns:expr, $frac:expr, on=$scale:expr, target=$target:expr) => {
1037        $crate::Dt::from_ns($ns, $frac, $scale, $target)
1038    };
1039    ($ns:expr, $frac:expr, target=$target:expr, on=$scale:expr) => {
1040        $crate::Dt::from_ns($ns, $frac, $scale, $target)
1041    };
1042    ($ns:expr, on=$scale:expr, target=$target:expr) => {
1043        $crate::Dt::from_ns($ns, 0, $scale, $target)
1044    };
1045    ($ns:expr, target=$target:expr, on=$scale:expr) => {
1046        $crate::Dt::from_ns($ns, 0, $scale, $target)
1047    };
1048    ($ns:expr, $frac:expr, on=$scale:expr) => {
1049        $crate::Dt::from_ns($ns, $frac, $scale, $scale)
1050    };
1051    ($ns:expr, $frac:expr, target=$target:expr) => {
1052        $crate::Dt::from_ns($ns, $frac, $crate::Scale::TAI, $target)
1053    };
1054    ($ns:expr, on=$scale:expr) => {
1055        $crate::Dt::from_ns($ns, 0, $scale, $scale)
1056    };
1057    ($ns:expr, target=$target:expr) => {
1058        $crate::Dt::from_ns($ns, 0, $crate::Scale::TAI, $target)
1059    };
1060    ($ns:expr, $frac:expr) => {
1061        $crate::Dt::from_ns($ns, $frac, $crate::Scale::TAI, $crate::Scale::TAI)
1062    };
1063    ($ns:expr) => {
1064        $crate::Dt::from_ns($ns, 0, $crate::Scale::TAI, $crate::Scale::TAI)
1065    };
1066}
1067
1068/// Builds a [`Dt`](../struct.Dt.html) from whole milliseconds and an optional signed
1069/// fractional remainder in attoseconds.
1070///
1071/// - Equivalent to [`Dt::from_ms`](../struct.Dt.html#method.from_ms).
1072/// - Does **not** perform time-scale conversion.
1073///
1074/// The fractional remainder is in **attoseconds** — use
1075/// [`us!`](macro.us.html) (or another `*_to_attos`
1076/// helper) instead of writing the attosecond literal by hand.
1077///
1078/// ## Defaults
1079///
1080/// | Omitted | Default |
1081/// |---------|---------|
1082/// | fraction | `0` |
1083/// | `on` and `target` | both [`Scale::TAI`](crate::Scale::TAI) |
1084/// | only `on=s` | `target=s` |
1085/// | only `target=t` | `on=TAI` |
1086///
1087/// ## Forms
1088///
1089/// ```text
1090/// from_ms!(ms)
1091/// from_ms!(ms, frac)
1092/// from_ms!(ms, on=s)
1093/// from_ms!(ms, target=t)
1094/// from_ms!(ms, on=s, target=t)
1095/// from_ms!(ms, target=t, on=s)
1096/// from_ms!(ms, frac, on=s)
1097/// from_ms!(ms, frac, target=t)
1098/// from_ms!(ms, frac, on=s, target=t)
1099/// from_ms!(ms, frac, target=t, on=s)
1100/// ```
1101///
1102/// ## Examples
1103///
1104/// ```
1105/// use deep_time::{Dt, Scale};
1106/// use deep_time::macros::{from_ms, us};
1107///
1108/// // 1.3 ms → whole milliseconds + 300 µs remainder
1109/// let a = from_ms!(1);
1110/// let b = from_ms!(1, us!(300));
1111/// let c = from_ms!(-1, us!(-300), on=Scale::TAI);
1112/// let d = from_ms!(0, on=Scale::UTC);
1113/// let e = from_ms!(1, on=Scale::TAI, target=Scale::UTC);
1114/// let f = from_ms!(1, target=Scale::UTC);
1115///
1116/// assert_eq!(a, Dt::from_ms(1, 0, Scale::TAI, Scale::TAI));
1117/// assert_eq!(b, Dt::from_ms(1, us!(300), Scale::TAI, Scale::TAI));
1118/// assert_eq!(c, Dt::from_ms(-1, us!(-300), Scale::TAI, Scale::TAI));
1119/// assert_eq!(d, Dt::from_ms(0, 0, Scale::UTC, Scale::UTC));
1120/// assert_eq!(e, Dt::from_ms(1, 0, Scale::TAI, Scale::UTC));
1121/// assert_eq!(f, e);
1122///
1123/// let signed = from_ms!(-1, us!(-300));
1124/// let floor = from_ms!(-2, us!(700));
1125/// assert_eq!(signed, floor);
1126/// assert_eq!(signed, Dt::from_ms(-1, us!(-300), Scale::TAI, Scale::TAI));
1127/// ```
1128#[macro_export]
1129macro_rules! from_ms {
1130    ($ms:expr, $frac:expr, on=$scale:expr, target=$target:expr) => {
1131        $crate::Dt::from_ms($ms, $frac, $scale, $target)
1132    };
1133    ($ms:expr, $frac:expr, target=$target:expr, on=$scale:expr) => {
1134        $crate::Dt::from_ms($ms, $frac, $scale, $target)
1135    };
1136    ($ms:expr, on=$scale:expr, target=$target:expr) => {
1137        $crate::Dt::from_ms($ms, 0, $scale, $target)
1138    };
1139    ($ms:expr, target=$target:expr, on=$scale:expr) => {
1140        $crate::Dt::from_ms($ms, 0, $scale, $target)
1141    };
1142    ($ms:expr, $frac:expr, on=$scale:expr) => {
1143        $crate::Dt::from_ms($ms, $frac, $scale, $scale)
1144    };
1145    ($ms:expr, $frac:expr, target=$target:expr) => {
1146        $crate::Dt::from_ms($ms, $frac, $crate::Scale::TAI, $target)
1147    };
1148    ($ms:expr, on=$scale:expr) => {
1149        $crate::Dt::from_ms($ms, 0, $scale, $scale)
1150    };
1151    ($ms:expr, target=$target:expr) => {
1152        $crate::Dt::from_ms($ms, 0, $crate::Scale::TAI, $target)
1153    };
1154    ($ms:expr, $frac:expr) => {
1155        $crate::Dt::from_ms($ms, $frac, $crate::Scale::TAI, $crate::Scale::TAI)
1156    };
1157    ($ms:expr) => {
1158        $crate::Dt::from_ms($ms, 0, $crate::Scale::TAI, $crate::Scale::TAI)
1159    };
1160}
1161
1162/// Builds a [`Dt`](../struct.Dt.html) from whole microseconds and an optional signed
1163/// fractional remainder in attoseconds.
1164///
1165/// - Equivalent to [`Dt::from_us`](../struct.Dt.html#method.from_us).
1166/// - Does **not** perform time-scale conversion.
1167///
1168/// The fractional remainder is in **attoseconds** — use
1169/// [`ns!`](../macro.ns.html) (or another `*_to_attos`
1170/// helper) instead of writing the attosecond literal by hand.
1171///
1172/// ## Defaults
1173///
1174/// | Omitted | Default |
1175/// |---------|---------|
1176/// | fraction | `0` |
1177/// | `on` and `target` | both [`Scale::TAI`](crate::Scale::TAI) |
1178/// | only `on=s` | `target=s` |
1179/// | only `target=t` | `on=TAI` |
1180///
1181/// ## Forms
1182///
1183/// ```text
1184/// from_us!(us)
1185/// from_us!(us, frac)
1186/// from_us!(us, on=s)
1187/// from_us!(us, target=t)
1188/// from_us!(us, on=s, target=t)
1189/// from_us!(us, target=t, on=s)
1190/// from_us!(us, frac, on=s)
1191/// from_us!(us, frac, target=t)
1192/// from_us!(us, frac, on=s, target=t)
1193/// from_us!(us, frac, target=t, on=s)
1194/// ```
1195///
1196/// ## Examples
1197///
1198/// ```
1199/// use deep_time::{Dt, Scale};
1200/// use deep_time::macros::{from_us, ns};
1201///
1202/// // 1.3 µs → whole microseconds + 300 ns remainder
1203/// let a = from_us!(1);
1204/// let b = from_us!(1, ns!(300));
1205/// let c = from_us!(-1, ns!(-300), on=Scale::TAI);
1206/// let d = from_us!(0, on=Scale::UTC);
1207/// let e = from_us!(1, on=Scale::TAI, target=Scale::UTC);
1208/// let f = from_us!(1, target=Scale::UTC);
1209///
1210/// assert_eq!(a, Dt::from_us(1, 0, Scale::TAI, Scale::TAI));
1211/// assert_eq!(b, Dt::from_us(1, ns!(300), Scale::TAI, Scale::TAI));
1212/// assert_eq!(c, Dt::from_us(-1, ns!(-300), Scale::TAI, Scale::TAI));
1213/// assert_eq!(d, Dt::from_us(0, 0, Scale::UTC, Scale::UTC));
1214/// assert_eq!(e, Dt::from_us(1, 0, Scale::TAI, Scale::UTC));
1215/// assert_eq!(f, e);
1216///
1217/// let signed = from_us!(-1, ns!(-300));
1218/// let floor = from_us!(-2, ns!(700));
1219/// assert_eq!(signed, floor);
1220/// assert_eq!(signed, Dt::from_us(-1, ns!(-300), Scale::TAI, Scale::TAI));
1221/// ```
1222#[macro_export]
1223macro_rules! from_us {
1224    ($us:expr, $frac:expr, on=$scale:expr, target=$target:expr) => {
1225        $crate::Dt::from_us($us, $frac, $scale, $target)
1226    };
1227    ($us:expr, $frac:expr, target=$target:expr, on=$scale:expr) => {
1228        $crate::Dt::from_us($us, $frac, $scale, $target)
1229    };
1230    ($us:expr, on=$scale:expr, target=$target:expr) => {
1231        $crate::Dt::from_us($us, 0, $scale, $target)
1232    };
1233    ($us:expr, target=$target:expr, on=$scale:expr) => {
1234        $crate::Dt::from_us($us, 0, $scale, $target)
1235    };
1236    ($us:expr, $frac:expr, on=$scale:expr) => {
1237        $crate::Dt::from_us($us, $frac, $scale, $scale)
1238    };
1239    ($us:expr, $frac:expr, target=$target:expr) => {
1240        $crate::Dt::from_us($us, $frac, $crate::Scale::TAI, $target)
1241    };
1242    ($us:expr, on=$scale:expr) => {
1243        $crate::Dt::from_us($us, 0, $scale, $scale)
1244    };
1245    ($us:expr, target=$target:expr) => {
1246        $crate::Dt::from_us($us, 0, $crate::Scale::TAI, $target)
1247    };
1248    ($us:expr, $frac:expr) => {
1249        $crate::Dt::from_us($us, $frac, $crate::Scale::TAI, $crate::Scale::TAI)
1250    };
1251    ($us:expr) => {
1252        $crate::Dt::from_us($us, 0, $crate::Scale::TAI, $crate::Scale::TAI)
1253    };
1254}
1255
1256/// Builds a [`Dt`](../struct.Dt.html) from whole picoseconds and an optional signed
1257/// fractional remainder in attoseconds.
1258///
1259/// - Equivalent to [`Dt::from_ps`](../struct.Dt.html#method.from_ps).
1260/// - Does **not** perform time-scale conversion.
1261///
1262/// The fractional remainder is in **attoseconds** — use
1263/// [`fs!`](macro.fs.html) (or another `*_to_attos`
1264/// helper) instead of writing the attosecond literal by hand.
1265///
1266/// ## Defaults
1267///
1268/// | Omitted | Default |
1269/// |---------|---------|
1270/// | fraction | `0` |
1271/// | `on` and `target` | both [`Scale::TAI`](crate::Scale::TAI) |
1272/// | only `on=s` | `target=s` |
1273/// | only `target=t` | `on=TAI` |
1274///
1275/// ## Forms
1276///
1277/// ```text
1278/// from_ps!(ps)
1279/// from_ps!(ps, frac)
1280/// from_ps!(ps, on=s)
1281/// from_ps!(ps, target=t)
1282/// from_ps!(ps, on=s, target=t)
1283/// from_ps!(ps, target=t, on=s)
1284/// from_ps!(ps, frac, on=s)
1285/// from_ps!(ps, frac, target=t)
1286/// from_ps!(ps, frac, on=s, target=t)
1287/// from_ps!(ps, frac, target=t, on=s)
1288/// ```
1289///
1290/// ## Examples
1291///
1292/// ```
1293/// use deep_time::{Dt, Scale};
1294/// use deep_time::macros::{from_ps, fs};
1295///
1296/// // 1.3 ps → whole picoseconds + 300 fs remainder
1297/// let a = from_ps!(1);
1298/// let b = from_ps!(1, fs!(300));
1299/// let c = from_ps!(-1, fs!(-300), on=Scale::TAI);
1300/// let d = from_ps!(0, on=Scale::UTC);
1301/// let e = from_ps!(1, on=Scale::TAI, target=Scale::UTC);
1302/// let f = from_ps!(1, target=Scale::UTC);
1303///
1304/// assert_eq!(a, Dt::from_ps(1, 0, Scale::TAI, Scale::TAI));
1305/// assert_eq!(b, Dt::from_ps(1, fs!(300), Scale::TAI, Scale::TAI));
1306/// assert_eq!(c, Dt::from_ps(-1, fs!(-300), Scale::TAI, Scale::TAI));
1307/// assert_eq!(d, Dt::from_ps(0, 0, Scale::UTC, Scale::UTC));
1308/// assert_eq!(e, Dt::from_ps(1, 0, Scale::TAI, Scale::UTC));
1309/// assert_eq!(f, e);
1310///
1311/// let signed = from_ps!(-1, fs!(-300));
1312/// let floor = from_ps!(-2, fs!(700));
1313/// assert_eq!(signed, floor);
1314/// assert_eq!(signed, Dt::from_ps(-1, fs!(-300), Scale::TAI, Scale::TAI));
1315/// ```
1316#[macro_export]
1317macro_rules! from_ps {
1318    ($ps:expr, $frac:expr, on=$scale:expr, target=$target:expr) => {
1319        $crate::Dt::from_ps($ps, $frac, $scale, $target)
1320    };
1321    ($ps:expr, $frac:expr, target=$target:expr, on=$scale:expr) => {
1322        $crate::Dt::from_ps($ps, $frac, $scale, $target)
1323    };
1324    ($ps:expr, on=$scale:expr, target=$target:expr) => {
1325        $crate::Dt::from_ps($ps, 0, $scale, $target)
1326    };
1327    ($ps:expr, target=$target:expr, on=$scale:expr) => {
1328        $crate::Dt::from_ps($ps, 0, $scale, $target)
1329    };
1330    ($ps:expr, $frac:expr, on=$scale:expr) => {
1331        $crate::Dt::from_ps($ps, $frac, $scale, $scale)
1332    };
1333    ($ps:expr, $frac:expr, target=$target:expr) => {
1334        $crate::Dt::from_ps($ps, $frac, $crate::Scale::TAI, $target)
1335    };
1336    ($ps:expr, on=$scale:expr) => {
1337        $crate::Dt::from_ps($ps, 0, $scale, $scale)
1338    };
1339    ($ps:expr, target=$target:expr) => {
1340        $crate::Dt::from_ps($ps, 0, $crate::Scale::TAI, $target)
1341    };
1342    ($ps:expr, $frac:expr) => {
1343        $crate::Dt::from_ps($ps, $frac, $crate::Scale::TAI, $crate::Scale::TAI)
1344    };
1345    ($ps:expr) => {
1346        $crate::Dt::from_ps($ps, 0, $crate::Scale::TAI, $crate::Scale::TAI)
1347    };
1348}
1349
1350/// Builds a [`Dt`](../struct.Dt.html) from whole femtoseconds and an optional signed
1351/// fractional remainder in attoseconds.
1352///
1353/// - Equivalent to [`Dt::from_fs`](../struct.Dt.html#method.from_fs).
1354/// - Does **not** perform time-scale conversion.
1355///
1356/// The fractional remainder is already in **attoseconds** (one femtosecond is
1357/// 1000 attoseconds) — there is no smaller named unit macro.
1358///
1359/// ## Defaults
1360///
1361/// | Omitted | Default |
1362/// |---------|---------|
1363/// | fraction | `0` |
1364/// | `on` and `target` | both [`Scale::TAI`](crate::Scale::TAI) |
1365/// | only `on=s` | `target=s` |
1366/// | only `target=t` | `on=TAI` |
1367///
1368/// ## Forms
1369///
1370/// ```text
1371/// from_fs!(fs)
1372/// from_fs!(fs, frac)
1373/// from_fs!(fs, on=s)
1374/// from_fs!(fs, target=t)
1375/// from_fs!(fs, on=s, target=t)
1376/// from_fs!(fs, target=t, on=s)
1377/// from_fs!(fs, frac, on=s)
1378/// from_fs!(fs, frac, target=t)
1379/// from_fs!(fs, frac, on=s, target=t)
1380/// from_fs!(fs, frac, target=t, on=s)
1381/// ```
1382///
1383/// ## Examples
1384///
1385/// ```
1386/// use deep_time::{Dt, Scale};
1387/// use deep_time::macros::from_fs;
1388///
1389/// // 1.3 fs → whole femtoseconds + 300 attoseconds remainder
1390/// let a = from_fs!(1);
1391/// let b = from_fs!(1, 300);
1392/// let c = from_fs!(-1, -300, on=Scale::TAI);
1393/// let d = from_fs!(0, on=Scale::UTC);
1394/// let e = from_fs!(1, on=Scale::TAI, target=Scale::UTC);
1395/// let f = from_fs!(1, target=Scale::UTC);
1396///
1397/// assert_eq!(a, Dt::from_fs(1, 0, Scale::TAI, Scale::TAI));
1398/// assert_eq!(b, Dt::from_fs(1, 300, Scale::TAI, Scale::TAI));
1399/// assert_eq!(c, Dt::from_fs(-1, -300, Scale::TAI, Scale::TAI));
1400/// assert_eq!(d, Dt::from_fs(0, 0, Scale::UTC, Scale::UTC));
1401/// assert_eq!(e, Dt::from_fs(1, 0, Scale::TAI, Scale::UTC));
1402/// assert_eq!(f, e);
1403///
1404/// let signed = from_fs!(-1, -300);
1405/// let floor = from_fs!(-2, 700);
1406/// assert_eq!(signed, floor);
1407/// assert_eq!(signed, Dt::from_fs(-1, -300, Scale::TAI, Scale::TAI));
1408/// ```
1409#[macro_export]
1410macro_rules! from_fs {
1411    ($fs:expr, $frac:expr, on=$scale:expr, target=$target:expr) => {
1412        $crate::Dt::from_fs($fs, $frac, $scale, $target)
1413    };
1414    ($fs:expr, $frac:expr, target=$target:expr, on=$scale:expr) => {
1415        $crate::Dt::from_fs($fs, $frac, $scale, $target)
1416    };
1417    ($fs:expr, on=$scale:expr, target=$target:expr) => {
1418        $crate::Dt::from_fs($fs, 0, $scale, $target)
1419    };
1420    ($fs:expr, target=$target:expr, on=$scale:expr) => {
1421        $crate::Dt::from_fs($fs, 0, $scale, $target)
1422    };
1423    ($fs:expr, $frac:expr, on=$scale:expr) => {
1424        $crate::Dt::from_fs($fs, $frac, $scale, $scale)
1425    };
1426    ($fs:expr, $frac:expr, target=$target:expr) => {
1427        $crate::Dt::from_fs($fs, $frac, $crate::Scale::TAI, $target)
1428    };
1429    ($fs:expr, on=$scale:expr) => {
1430        $crate::Dt::from_fs($fs, 0, $scale, $scale)
1431    };
1432    ($fs:expr, target=$target:expr) => {
1433        $crate::Dt::from_fs($fs, 0, $crate::Scale::TAI, $target)
1434    };
1435    ($fs:expr, $frac:expr) => {
1436        $crate::Dt::from_fs($fs, $frac, $crate::Scale::TAI, $crate::Scale::TAI)
1437    };
1438    ($fs:expr) => {
1439        $crate::Dt::from_fs($fs, 0, $crate::Scale::TAI, $crate::Scale::TAI)
1440    };
1441}
1442
1443/// Builds a **TAI** [`Dt`](../struct.Dt.html) from a Julian Date (whole days plus
1444/// optional attosecond remainder).
1445///
1446/// Equivalent to [`Dt::from_jd`](../struct.Dt.html#method.from_jd).
1447///
1448/// When an `on` arg is provided and it's not [`Scale::TAI`](crate::Scale::TAI)
1449/// then a time scale conversion is performed equivalent to `on` ->
1450/// [`Scale::TAI`](crate::Scale::TAI).
1451///
1452/// There is no `target=` on this macro — the returned [`Dt`](../struct.Dt.html)'s
1453/// `target` is set from `on` (or TAI when omitted), as
1454/// [`from_jd`](../struct.Dt.html#method.from_jd) does. Chain
1455/// [`.target(…)`](../struct.Dt.html#method.target) if needed.
1456///
1457/// The fractional remainder is in **attoseconds** — use a `*_to_attos` helper
1458/// (e.g. a day fraction built from [`days_f!`](../macro.days_f.html))
1459/// instead of hand-counting zeros when convenient.
1460///
1461/// ## Defaults
1462///
1463/// | Omitted | Default |
1464/// |---------|---------|
1465/// | fraction | `0` |
1466/// | `on` | [`Scale::TAI`](crate::Scale::TAI) |
1467///
1468/// ## Forms
1469///
1470/// ```text
1471/// from_jd!(jd_days)
1472/// from_jd!(jd_days, frac)
1473/// from_jd!(jd_days, on=s)
1474/// from_jd!(jd_days, frac, on=s)
1475/// ```
1476///
1477/// ## Examples
1478///
1479/// ```
1480/// use deep_time::{Dt, Scale};
1481/// use deep_time::macros::{days_f, from_jd};
1482///
1483/// // 2_460_782.25
1484/// let a = from_jd!(2_460_782);
1485/// let b = from_jd!(2_460_782, days_f!(0.25));
1486/// let c = from_jd!(2_460_782, days_f!(0.25), on=Scale::TAI);
1487/// let d = from_jd!(2_460_782, on=Scale::UTC);
1488///
1489/// assert_eq!(a, Dt::from_jd(2_460_782, 0, Scale::TAI));
1490/// assert_eq!(b, Dt::from_jd(2_460_782, days_f!(0.25), Scale::TAI));
1491/// assert_eq!(c, b);
1492/// assert_eq!(d, Dt::from_jd(2_460_782, 0, Scale::UTC));
1493///
1494/// // -1_000.25 (signed remainder)
1495/// let neg = from_jd!(-1_000, -days_f!(0.25));
1496/// assert_eq!(neg, Dt::from_jd(-1_000, -days_f!(0.25), Scale::TAI));
1497/// assert_eq!(neg.to_jd(), (-1_000, -days_f!(0.25)));
1498///
1499/// // or with floor style
1500/// assert_eq!(neg, from_jd!(-1_001, days_f!(0.75)));
1501/// ```
1502#[macro_export]
1503macro_rules! from_jd {
1504    // `on=` arms before bare `$frac:expr` so `on=…` is not taken as an expr.
1505    ($jd_days:expr, $frac:expr, on=$scale:expr) => {
1506        $crate::Dt::from_jd($jd_days, $frac, $scale)
1507    };
1508    ($jd_days:expr, on=$scale:expr) => {
1509        $crate::Dt::from_jd($jd_days, 0, $scale)
1510    };
1511    ($jd_days:expr, $frac:expr) => {
1512        $crate::Dt::from_jd($jd_days, $frac, $crate::Scale::TAI)
1513    };
1514    ($jd_days:expr) => {
1515        $crate::Dt::from_jd($jd_days, 0, $crate::Scale::TAI)
1516    };
1517}
1518
1519/// Builds a **TAI** [`Dt`](../struct.Dt.html) from a floating-point Julian Date.
1520///
1521/// Equivalent to [`Dt::from_jd_f`](../struct.Dt.html#method.from_jd_f).
1522///
1523/// When an `on` arg is provided and it's not [`Scale::TAI`](crate::Scale::TAI)
1524/// then a time scale conversion is performed equivalent to `on` ->
1525/// [`Scale::TAI`](crate::Scale::TAI).
1526///
1527/// There is no `target=` on this macro — the returned [`Dt`](../struct.Dt.html)'s
1528/// `target` is set from `on` (or TAI when omitted), as
1529/// [`from_jd_f`](../struct.Dt.html#method.from_jd_f) does. Chain
1530/// [`.target(…)`](../struct.Dt.html#method.target) if needed.
1531///
1532/// ## Defaults
1533///
1534/// | Omitted | Default |
1535/// |---------|---------|
1536/// | `on` | [`Scale::TAI`](crate::Scale::TAI) |
1537///
1538/// ## Forms
1539///
1540/// ```text
1541/// from_jd_f!(jd)
1542/// from_jd_f!(jd, on=s)
1543/// ```
1544///
1545/// ## Examples
1546///
1547/// ```
1548/// use deep_time::{Dt, Scale};
1549/// use deep_time::macros::{days_f, from_jd_f};
1550///
1551/// // 2_460_782.25
1552/// let a = from_jd_f!(2_460_782.25);
1553/// let b = from_jd_f!(2_460_782.25, on=Scale::TAI);
1554/// let c = from_jd_f!(2_460_782.0, on=Scale::UTC);
1555///
1556/// assert_eq!(a, Dt::from_jd_f(2_460_782.25, Scale::TAI));
1557/// assert_eq!(b, a);
1558/// assert_eq!(c, Dt::from_jd_f(2_460_782.0, Scale::UTC));
1559/// assert_eq!(a.to_jd(), (2_460_782, days_f!(0.25)));
1560///
1561/// // -1_000.25 (signed remainder)
1562/// let neg = from_jd_f!(-1_000.25);
1563/// assert_eq!(neg, Dt::from_jd_f(-1_000.25, Scale::TAI));
1564/// assert_eq!(neg.to_jd(), (-1_000, -days_f!(0.25)));
1565/// ```
1566#[macro_export]
1567macro_rules! from_jd_f {
1568    ($jd:expr, on=$scale:expr) => {
1569        $crate::Dt::from_jd_f($jd, $scale)
1570    };
1571    ($jd:expr) => {
1572        $crate::Dt::from_jd_f($jd, $crate::Scale::TAI)
1573    };
1574}
1575
1576/// Builds a **TAI** [`Dt`](../struct.Dt.html) from a Modified Julian Date (whole days
1577/// plus optional attosecond remainder).
1578///
1579/// Equivalent to [`Dt::from_mjd`](../struct.Dt.html#method.from_mjd).
1580///
1581/// When an `on` arg is provided and it's not [`Scale::TAI`](crate::Scale::TAI)
1582/// then a time scale conversion is performed equivalent to `on` ->
1583/// [`Scale::TAI`](crate::Scale::TAI).
1584///
1585/// There is no `target=` on this macro — the returned [`Dt`](../struct.Dt.html)'s
1586/// `target` is set from `on` (or TAI when omitted), as
1587/// [`from_mjd`](../struct.Dt.html#method.from_mjd) does. Chain
1588/// [`.target(…)`](../struct.Dt.html#method.target) if needed.
1589///
1590/// MJD and JD relate by `JD = MJD + 2_400_000.5`.
1591///
1592/// The fractional remainder is in **attoseconds** — use a `*_to_attos` helper
1593/// (e.g. a day fraction built from [`days_f!`](../macro.days_f.html))
1594/// instead of hand-counting zeros when convenient.
1595///
1596/// ## Defaults
1597///
1598/// | Omitted | Default |
1599/// |---------|---------|
1600/// | fraction | `0` |
1601/// | `on` | [`Scale::TAI`](crate::Scale::TAI) |
1602///
1603/// ## Forms
1604///
1605/// ```text
1606/// from_mjd!(mjd_days)
1607/// from_mjd!(mjd_days, frac)
1608/// from_mjd!(mjd_days, on=s)
1609/// from_mjd!(mjd_days, frac, on=s)
1610/// ```
1611///
1612/// ## Examples
1613///
1614/// ```
1615/// use deep_time::{Dt, Scale};
1616/// use deep_time::macros::{days_f, from_mjd};
1617///
1618/// // J2000.0 → MJD 51_544.5
1619/// let a = from_mjd!(51_544);
1620/// let b = from_mjd!(51_544, days_f!(0.5));
1621/// let c = from_mjd!(51_544, days_f!(0.5), on=Scale::TAI);
1622/// let d = from_mjd!(51_544, on=Scale::UTC);
1623///
1624/// assert_eq!(a, Dt::from_mjd(51_544, 0, Scale::TAI));
1625/// assert_eq!(b, Dt::from_mjd(51_544, days_f!(0.5), Scale::TAI));
1626/// assert_eq!(c, b);
1627/// assert_eq!(d, Dt::from_mjd(51_544, 0, Scale::UTC));
1628/// assert_eq!(b.to_mjd(), (51_544, days_f!(0.5)));
1629///
1630/// // -1_000.25 (signed truncating remainder)
1631/// let neg = from_mjd!(-1_000, -days_f!(0.25));
1632/// assert_eq!(neg, Dt::from_mjd(-1_000, -days_f!(0.25), Scale::TAI));
1633/// assert_eq!(neg.to_mjd(), (-1_000, -days_f!(0.25)));
1634///
1635/// // or with floor style
1636/// assert_eq!(neg, from_mjd!(-1_001, days_f!(0.75)));
1637/// ```
1638#[macro_export]
1639macro_rules! from_mjd {
1640    // `on=` arms before bare `$frac:expr` so `on=…` is not taken as an expr.
1641    ($mjd_days:expr, $frac:expr, on=$scale:expr) => {
1642        $crate::Dt::from_mjd($mjd_days, $frac, $scale)
1643    };
1644    ($mjd_days:expr, on=$scale:expr) => {
1645        $crate::Dt::from_mjd($mjd_days, 0, $scale)
1646    };
1647    ($mjd_days:expr, $frac:expr) => {
1648        $crate::Dt::from_mjd($mjd_days, $frac, $crate::Scale::TAI)
1649    };
1650    ($mjd_days:expr) => {
1651        $crate::Dt::from_mjd($mjd_days, 0, $crate::Scale::TAI)
1652    };
1653}
1654
1655/// Builds a **TAI** [`Dt`](../struct.Dt.html) from a floating-point Modified Julian Date.
1656///
1657/// Equivalent to [`Dt::from_mjd_f`](../struct.Dt.html#method.from_mjd_f).
1658///
1659/// When an `on` arg is provided and it's not [`Scale::TAI`](crate::Scale::TAI)
1660/// then a time scale conversion is performed equivalent to `on` ->
1661/// [`Scale::TAI`](crate::Scale::TAI).
1662///
1663/// There is no `target=` on this macro — the returned [`Dt`](../struct.Dt.html)'s
1664/// `target` is set from `on` (or TAI when omitted), as
1665/// [`from_mjd_f`](../struct.Dt.html#method.from_mjd_f) does. Chain
1666/// [`.target(…)`](../struct.Dt.html#method.target) if needed.
1667///
1668/// MJD and JD relate by `JD = MJD + 2_400_000.5`.
1669///
1670/// ## Defaults
1671///
1672/// | Omitted | Default |
1673/// |---------|---------|
1674/// | `on` | [`Scale::TAI`](crate::Scale::TAI) |
1675///
1676/// ## Forms
1677///
1678/// ```text
1679/// from_mjd_f!(mjd)
1680/// from_mjd_f!(mjd, on=s)
1681/// ```
1682///
1683/// ## Examples
1684///
1685/// ```
1686/// use deep_time::{Dt, Scale};
1687/// use deep_time::macros::{days_f, from_mjd_f};
1688///
1689/// // 60_961.25
1690/// let a = from_mjd_f!(60_961.25);
1691/// let b = from_mjd_f!(60_961.25, on=Scale::TAI);
1692/// let c = from_mjd_f!(60_961.0, on=Scale::UTC);
1693///
1694/// assert_eq!(a, Dt::from_mjd_f(60_961.25, Scale::TAI));
1695/// assert_eq!(b, a);
1696/// assert_eq!(c, Dt::from_mjd_f(60_961.0, Scale::UTC));
1697/// assert_eq!(a.to_mjd_floor(), (60_961, days_f!(0.25)));
1698///
1699/// // -1_000.25 as -1_001 + 0.75 day
1700/// let neg = from_mjd_f!(-1_000.25);
1701/// assert_eq!(neg, Dt::from_mjd_f(-1_000.25, Scale::TAI));
1702/// assert_eq!(neg.to_mjd_floor(), (-1_001, days_f!(0.75)));
1703/// ```
1704#[macro_export]
1705macro_rules! from_mjd_f {
1706    ($mjd:expr, on=$scale:expr) => {
1707        $crate::Dt::from_mjd_f($mjd, $scale)
1708    };
1709    ($mjd:expr) => {
1710        $crate::Dt::from_mjd_f($mjd, $crate::Scale::TAI)
1711    };
1712}
1713
1714/// Builds a [`Dt`](../struct.Dt.html) from a Gregorian calendar date and optional time.
1715///
1716/// Equivalent to [`Dt::from_ymd`](../struct.Dt.html#method.from_ymd).
1717///
1718/// Date fields are positional (`y`, `m`, `d`). Time is optional: put a
1719/// **semicolon after the day**, then hour (required if `;` is present), then
1720/// optional minute, second, and attoseconds. An optional `on=` civil scale may
1721/// follow.
1722///
1723/// A time scale conversion is performed from the `on` arg ->
1724/// [`Scale::TAI`](crate::Scale::TAI).
1725///
1726/// When no `on` arg is used the time scale is assumed to be
1727/// [`Scale::UTC`](crate::Scale::UTC).
1728///
1729/// | Omitted field | Default |
1730/// |---------------|---------|
1731/// | month | `1` |
1732/// | day | `1` |
1733/// | time (no `;`) | `0, 0, 0, 0` |
1734/// | minute / second / attos after `; h` | `0` |
1735/// | `on` | [`Scale::UTC`](crate::Scale::UTC) |
1736///
1737/// The resulting [`Dt`](../struct.Dt.html)'s `target` field is set from that civil
1738/// scale (the `on=` value, or UTC when omitted), as
1739/// [`Dt::from_ymd`](../struct.Dt.html#method.from_ymd) does. There is no `target=` on
1740/// this macro — chain [`.target(…)`](../struct.Dt.html#method.target) if needed.
1741///
1742/// ## Forms
1743///
1744/// ```text
1745/// from_ymd!(y)
1746/// from_ymd!(y, m)
1747/// from_ymd!(y, m, d)
1748/// from_ymd!(y, m, d, on=Scale::TAI)
1749/// from_ymd!(y, m, d; h)
1750/// from_ymd!(y, m, d; h, min)
1751/// from_ymd!(y, m, d; h, min, sec)
1752/// from_ymd!(y, m, d; h, min, sec, attos)
1753/// from_ymd!(y, m, d; h, min, sec, attos, on=Scale::UTC)
1754/// ```
1755///
1756/// ## Examples
1757///
1758/// ```
1759/// use deep_time::Scale;
1760/// use deep_time::macros::from_ymd;
1761///
1762/// assert_eq!(
1763///     from_ymd!(1970),
1764///     deep_time::Dt::UNIX_EPOCH,
1765/// );
1766/// assert_eq!(
1767///     from_ymd!(2026, 6, 16),
1768///     deep_time::Dt::from_ymd(2026, 6, 16, Scale::UTC, 0, 0, 0, 0),
1769/// );
1770/// assert_eq!(
1771///     from_ymd!(2000, 1, 1; 12, on=Scale::TAI),
1772///     deep_time::Dt::ZERO,
1773/// );
1774/// assert_eq!(
1775///     from_ymd!(2000, 1, 1; 12, 0, 0, 123_456_789, on=Scale::UTC),
1776///     deep_time::Dt::from_ymd(2000, 1, 1, Scale::UTC, 12, 0, 0, 123_456_789),
1777/// );
1778///
1779/// // different target field after construction
1780/// let _ = from_ymd!(2020, 1, 1, on=Scale::UTC).target(Scale::TAI);
1781/// ```
1782#[macro_export]
1783macro_rules! from_ymd {
1784    // Time section: `d; h …` — `on=` arms before bare so `on=…` is not an `:expr`.
1785    ($y:expr, $m:expr, $d:expr; $h:expr, $min:expr, $s:expr, $attos:expr, on=$scale:expr) => {
1786        $crate::Dt::from_ymd($y, $m, $d, $scale, $h, $min, $s, $attos)
1787    };
1788    ($y:expr, $m:expr, $d:expr; $h:expr, $min:expr, $s:expr, on=$scale:expr) => {
1789        $crate::Dt::from_ymd($y, $m, $d, $scale, $h, $min, $s, 0)
1790    };
1791    ($y:expr, $m:expr, $d:expr; $h:expr, $min:expr, on=$scale:expr) => {
1792        $crate::Dt::from_ymd($y, $m, $d, $scale, $h, $min, 0, 0)
1793    };
1794    ($y:expr, $m:expr, $d:expr; $h:expr, on=$scale:expr) => {
1795        $crate::Dt::from_ymd($y, $m, $d, $scale, $h, 0, 0, 0)
1796    };
1797    ($y:expr, $m:expr, $d:expr; $h:expr, $min:expr, $s:expr, $attos:expr) => {
1798        $crate::Dt::from_ymd($y, $m, $d, $crate::Scale::UTC, $h, $min, $s, $attos)
1799    };
1800    ($y:expr, $m:expr, $d:expr; $h:expr, $min:expr, $s:expr) => {
1801        $crate::Dt::from_ymd($y, $m, $d, $crate::Scale::UTC, $h, $min, $s, 0)
1802    };
1803    ($y:expr, $m:expr, $d:expr; $h:expr, $min:expr) => {
1804        $crate::Dt::from_ymd($y, $m, $d, $crate::Scale::UTC, $h, $min, 0, 0)
1805    };
1806    ($y:expr, $m:expr, $d:expr; $h:expr) => {
1807        $crate::Dt::from_ymd($y, $m, $d, $crate::Scale::UTC, $h, 0, 0, 0)
1808    };
1809
1810    // Date only (+ optional on=)
1811    ($y:expr, $m:expr, $d:expr, on=$scale:expr) => {
1812        $crate::Dt::from_ymd($y, $m, $d, $scale, 0, 0, 0, 0)
1813    };
1814    ($y:expr, $m:expr, on=$scale:expr) => {
1815        $crate::Dt::from_ymd($y, $m, 1, $scale, 0, 0, 0, 0)
1816    };
1817    ($y:expr, on=$scale:expr) => {
1818        $crate::Dt::from_ymd($y, 1, 1, $scale, 0, 0, 0, 0)
1819    };
1820    ($y:expr, $m:expr, $d:expr) => {
1821        $crate::Dt::from_ymd($y, $m, $d, $crate::Scale::UTC, 0, 0, 0, 0)
1822    };
1823    ($y:expr, $m:expr) => {
1824        $crate::Dt::from_ymd($y, $m, 1, $crate::Scale::UTC, 0, 0, 0, 0)
1825    };
1826    ($y:expr) => {
1827        $crate::Dt::from_ymd($y, 1, 1, $crate::Scale::UTC, 0, 0, 0, 0)
1828    };
1829}
1830
1831#[doc(inline)]
1832pub use crate::{
1833    // attoseconds → unit
1834    as_days,
1835    as_days_f,
1836    as_fs,
1837    as_hours,
1838    as_mins,
1839    as_ms,
1840    as_ns,
1841    as_ps,
1842    as_sec,
1843    as_sec_f,
1844    as_us,
1845    as_weeks,
1846    as_weeks_f,
1847    // unit → attoseconds
1848    days,
1849    days_f,
1850    // constructors
1851    dt,
1852    from_days_f,
1853    from_fs,
1854    from_jd,
1855    from_jd_f,
1856    from_mjd,
1857    from_mjd_f,
1858    from_ms,
1859    from_ns,
1860    from_ps,
1861    from_sec,
1862    from_sec_f,
1863    from_us,
1864    from_ymd,
1865    fs,
1866    hours,
1867    mins,
1868    ms,
1869    ns,
1870    ps,
1871    sec,
1872    sec_f,
1873    us,
1874    weeks,
1875    weeks_f,
1876};