Skip to main content

qtty_core/units/
time.rs

1//! Time units.
2//!
3//! The canonical scaling unit for this dimension is [`Second`] (`Second::RATIO == 1.0`). All other time unit ratios are
4//! expressed in *seconds*.
5//!
6//! ## Precision and conventions
7//!
8//! - The **SI second** is the canonical unit.
9//! - Civil units such as [`Day`] are expressed using the conventional mapping
10//!   `1 day = 86_400 s` (mean solar day; leap seconds ignored).
11//! - “Mean” astronomical units (e.g., [`SiderealDay`], [`SynodicMonth`], [`SiderealYear`]) are **approximations**
12//!   that vary slightly with epoch/definition. Each unit documents the convention used.
13//!
14//! ```rust
15//! use qtty_core::time::{Hours, Second, Hour};
16//!
17//! let half_hour = Hours::new(0.5);
18//! let seconds = half_hour.to::<Second>();
19//! assert!((seconds.value() - 1800.0).abs() < 1e-12);
20//!
21//! let two_hours = seconds.to::<Hour>();
22//! assert!((two_hours.value() - 0.5).abs() < 1e-12);
23//! ```
24//!
25//! ## All time units
26//!
27//! ```rust
28//! use qtty_core::time::*;
29//!
30//! macro_rules! touch {
31//!     ($T:ty, $v:expr) => {{
32//!         let q = <$T>::new($v);
33//!         let _cloned = q;
34//!         assert!(q == q);
35//!     }};
36//! }
37//!
38//! touch!(Attoseconds,  1.0); touch!(Femtoseconds, 1.0);
39//! touch!(Picoseconds,  1.0); touch!(Nanoseconds,  1.0);
40//! touch!(Microseconds, 1.0); touch!(Milliseconds, 1.0);
41//! touch!(Centiseconds, 1.0); touch!(Deciseconds,  1.0);
42//! touch!(Seconds,      1.0); touch!(Decaseconds,  1.0);
43//! touch!(Hectoseconds, 1.0); touch!(Kiloseconds,  1.0);
44//! touch!(Megaseconds,  1.0); touch!(Gigaseconds,  1.0);
45//! touch!(Teraseconds,  1.0); touch!(Minutes,      1.0);
46//! touch!(Hours,        1.0); touch!(Days,         1.0);
47//! touch!(Weeks,        1.0); touch!(Fortnights,   1.0);
48//! touch!(Years,        1.0); touch!(Decades,      1.0);
49//! touch!(Centuries,    1.0); touch!(Millennia,    1.0);
50//! touch!(JulianYears,  1.0); touch!(JulianCenturies, 1.0);
51//! touch!(SiderealDays, 1.0); touch!(SynodicMonths, 1.0);
52//! touch!(SiderealYears, 1.0);
53//! ```
54
55use crate::{Quantity, Unit};
56use qtty_derive::Unit;
57
58/// Re-export from the dimension module.
59pub use crate::dimension::Time;
60
61/// Marker trait for any [`Unit`] whose dimension is [`Time`].
62pub trait TimeUnit: Unit<Dim = Time> {}
63impl<T: Unit<Dim = Time>> TimeUnit for T {}
64
65/// Conventional civil mapping used by this module: seconds per mean solar day.
66pub const SECONDS_PER_DAY: f64 = 86_400.0;
67
68// --- SI submultiples of the second ---
69
70/// Attoseconds (`1 as = 10^-18 s`).
71#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
72#[unit(symbol = "as", dimension = Time, ratio = 1e-18)]
73pub struct Attosecond;
74/// A quantity measured in attoseconds.
75pub type Attoseconds = Quantity<Attosecond>;
76/// A constant representing one attosecond.
77pub const ATTOSEC: Attoseconds = Attoseconds::new(1.0);
78
79/// Femtoseconds (`1 fs = 10^-15 s`).
80#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
81#[unit(symbol = "fs", dimension = Time, ratio = 1e-15)]
82pub struct Femtosecond;
83/// A quantity measured in femtoseconds.
84pub type Femtoseconds = Quantity<Femtosecond>;
85/// A constant representing one femtosecond.
86pub const FEMTOSEC: Femtoseconds = Femtoseconds::new(1.0);
87
88/// Picoseconds (`1 ps = 10^-12 s`).
89#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
90#[unit(symbol = "ps", dimension = Time, ratio = 1e-12)]
91pub struct Picosecond;
92/// A quantity measured in picoseconds.
93pub type Picoseconds = Quantity<Picosecond>;
94/// A constant representing one picosecond.
95pub const PICOSEC: Picoseconds = Picoseconds::new(1.0);
96
97/// Nanoseconds (`1 ns = 10^-9 s`).
98#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
99#[unit(symbol = "ns", dimension = Time, ratio = 1e-9)]
100pub struct Nanosecond;
101/// A quantity measured in nanoseconds.
102pub type Nanoseconds = Quantity<Nanosecond>;
103/// A constant representing one nanosecond.
104pub const NANOSEC: Nanoseconds = Nanoseconds::new(1.0);
105
106/// Microseconds (`1 µs = 10^-6 s`).
107#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
108#[unit(symbol = "µs", dimension = Time, ratio = 1e-6)]
109pub struct Microsecond;
110/// A quantity measured in microseconds.
111pub type Microseconds = Quantity<Microsecond>;
112/// A constant representing one microsecond.
113pub const MICROSEC: Microseconds = Microseconds::new(1.0);
114
115/// Milliseconds (`1 ms = 10^-3 s`).
116#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
117#[unit(symbol = "ms", dimension = Time, ratio = 1e-3)]
118pub struct Millisecond;
119/// A quantity measured in milliseconds.
120pub type Milliseconds = Quantity<Millisecond>;
121/// A constant representing one millisecond.
122pub const MILLISEC: Milliseconds = Milliseconds::new(1.0);
123
124/// Centiseconds (`1 cs = 10^-2 s`).
125#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
126#[unit(symbol = "cs", dimension = Time, ratio = 1e-2)]
127pub struct Centisecond;
128/// A quantity measured in centiseconds.
129pub type Centiseconds = Quantity<Centisecond>;
130/// A constant representing one centisecond.
131pub const CENTISEC: Centiseconds = Centiseconds::new(1.0);
132
133/// Deciseconds (`1 ds = 10^-1 s`).
134#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
135#[unit(symbol = "ds", dimension = Time, ratio = 1e-1)]
136pub struct Decisecond;
137/// A quantity measured in deciseconds.
138pub type Deciseconds = Quantity<Decisecond>;
139/// A constant representing one decisecond.
140pub const DECISEC: Deciseconds = Deciseconds::new(1.0);
141
142/// Seconds (SI base unit).
143#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
144#[unit(symbol = "s", dimension = Time, ratio = 1.0)]
145pub struct Second;
146/// A quantity measured in seconds.
147pub type Seconds = Quantity<Second>;
148/// A constant representing one second.
149pub const SEC: Seconds = Seconds::new(1.0);
150
151// --- SI multiples of the second ---
152
153/// Decaseconds (`1 das = 10 s`).
154#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
155#[unit(symbol = "das", dimension = Time, ratio = 10.0)]
156pub struct Decasecond;
157/// A quantity measured in decaseconds.
158pub type Decaseconds = Quantity<Decasecond>;
159/// A constant representing one decasecond.
160pub const DECASEC: Decaseconds = Decaseconds::new(1.0);
161
162/// Hectoseconds (`1 hs = 100 s`).
163#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
164#[unit(symbol = "hs", dimension = Time, ratio = 100.0)]
165pub struct Hectosecond;
166/// A quantity measured in hectoseconds.
167pub type Hectoseconds = Quantity<Hectosecond>;
168/// A constant representing one hectosecond.
169pub const HECTOSEC: Hectoseconds = Hectoseconds::new(1.0);
170
171/// Kiloseconds (`1 ks = 1_000 s`).
172#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
173#[unit(symbol = "ks", dimension = Time, ratio = 1_000.0)]
174pub struct Kilosecond;
175/// A quantity measured in kiloseconds.
176pub type Kiloseconds = Quantity<Kilosecond>;
177/// A constant representing one kilosecond.
178pub const KILOSEC: Kiloseconds = Kiloseconds::new(1.0);
179
180/// Megaseconds (`1 Ms = 10^6 s`).
181#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
182#[unit(symbol = "Ms", dimension = Time, ratio = 1e6)]
183pub struct Megasecond;
184/// A quantity measured in megaseconds.
185pub type Megaseconds = Quantity<Megasecond>;
186/// A constant representing one megasecond.
187pub const MEGASEC: Megaseconds = Megaseconds::new(1.0);
188
189/// Gigaseconds (`1 Gs = 10^9 s`).
190#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
191#[unit(symbol = "Gs", dimension = Time, ratio = 1e9)]
192pub struct Gigasecond;
193/// A quantity measured in gigaseconds.
194pub type Gigaseconds = Quantity<Gigasecond>;
195/// A constant representing one gigasecond.
196pub const GIGASEC: Gigaseconds = Gigaseconds::new(1.0);
197
198/// Teraseconds (`1 Ts = 10^12 s`).
199#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
200#[unit(symbol = "Ts", dimension = Time, ratio = 1e12)]
201pub struct Terasecond;
202/// A quantity measured in teraseconds.
203pub type Teraseconds = Quantity<Terasecond>;
204/// A constant representing one terasecond.
205pub const TERASEC: Teraseconds = Teraseconds::new(1.0);
206
207// --- Common civil units ---
208
209/// Minutes (`60 s`).
210#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
211#[unit(symbol = "min", dimension = Time, ratio = 60.0)]
212pub struct Minute;
213/// A quantity measured in minutes.
214pub type Minutes = Quantity<Minute>;
215/// A constant representing one minute.
216pub const MIN: Minutes = Minutes::new(1.0);
217
218/// Hours (`3_600 s`).
219#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
220#[unit(symbol = "h", dimension = Time, ratio = 3_600.0)]
221pub struct Hour;
222/// A quantity measured in hours.
223pub type Hours = Quantity<Hour>;
224/// A constant representing one hour.
225pub const HOUR: Hours = Hours::new(1.0);
226
227/// Mean solar day (`86_400 s` by convention; leap seconds ignored).
228#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
229#[unit(symbol = "d", dimension = Time, ratio = SECONDS_PER_DAY)]
230pub struct Day;
231/// A quantity measured in days.
232pub type Days = Quantity<Day>;
233/// A constant representing one day.
234pub const DAY: Days = Days::new(1.0);
235
236/// Week (`7 d = 604_800 s`).
237#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
238#[unit(symbol = "wk", dimension = Time, ratio = 7.0 * SECONDS_PER_DAY)]
239pub struct Week;
240/// A quantity measured in weeks.
241pub type Weeks = Quantity<Week>;
242/// A constant representing one week.
243pub const WEEK: Weeks = Weeks::new(1.0);
244
245/// Fortnight (`14 d = 1_209_600 s`).
246#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
247#[unit(symbol = "fn", dimension = Time, ratio = 14.0 * SECONDS_PER_DAY)]
248pub struct Fortnight;
249/// A quantity measured in fortnights.
250pub type Fortnights = Quantity<Fortnight>;
251/// A constant representing one fortnight.
252pub const FORTNIGHT: Fortnights = Fortnights::new(1.0);
253
254/// Mean tropical year, as a conventional mean length.
255///
256/// Convention used: `365.2425 d`.
257#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
258#[unit(symbol = "yr", dimension = Time, ratio = 365.242_5 * SECONDS_PER_DAY)]
259pub struct Year;
260/// A quantity measured in years.
261pub type Years = Quantity<Year>;
262/// A constant representing one year.
263pub const YEAR: Years = Years::new(1.0);
264
265/// Decade (`10` mean tropical years).
266#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
267#[unit(symbol = "dec", dimension = Time, ratio = 10.0 * 365.242_5 * SECONDS_PER_DAY)]
268pub struct Decade;
269/// A quantity measured in decades.
270pub type Decades = Quantity<Decade>;
271/// A constant representing one decade.
272pub const DECADE: Decades = Decades::new(1.0);
273
274/// Century (`100` mean tropical years).
275#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
276#[unit(symbol = "cent", dimension = Time, ratio = 100.0 * 365.242_5 * SECONDS_PER_DAY)]
277pub struct Century;
278/// A quantity measured in centuries.
279pub type Centuries = Quantity<Century>;
280/// A constant representing one century.
281pub const CENTURY: Centuries = Centuries::new(1.0);
282
283/// Millennium (`1000` mean tropical years).
284#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
285#[unit(symbol = "mill", dimension = Time, ratio = 1000.0 * 365.242_5 * SECONDS_PER_DAY)]
286pub struct Millennium;
287/// A quantity measured in millennia.
288pub type Millennia = Quantity<Millennium>;
289/// A constant representing one millennium.
290pub const MILLENNIUM: Millennia = Millennia::new(1.0);
291
292// --- Julian conventions (useful in astronomy/ephemerides) ---
293
294/// Julian year (`365.25 d`), expressed in seconds.
295#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
296#[unit(symbol = "a", dimension = Time, ratio = 365.25 * SECONDS_PER_DAY)]
297pub struct JulianYear;
298/// A quantity measured in Julian years.
299pub type JulianYears = Quantity<JulianYear>;
300/// A constant representing one Julian year.
301pub const JULIAN_YEAR: JulianYears = JulianYears::new(1.0);
302
303/// Julian century (`36_525 d`), expressed in seconds.
304#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
305#[unit(symbol = "JC", dimension = Time, ratio = 36_525.0 * SECONDS_PER_DAY)]
306pub struct JulianCentury;
307/// A quantity measured in Julian centuries.
308pub type JulianCenturies = Quantity<JulianCentury>;
309/// A constant representing one Julian century.
310pub const JULIAN_CENTURY: JulianCenturies = JulianCenturies::new(1.0);
311
312// --- Astronomical mean units (explicitly approximate) ---
313
314/// Mean sidereal day (Earth), expressed in SI seconds.
315///
316/// Convention used: `1 sidereal day ≈ 86_164.0905 s`.
317#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
318#[unit(symbol = "sd", dimension = Time, ratio = 86_164.090_5)]
319pub struct SiderealDay;
320/// A quantity measured in sidereal days.
321pub type SiderealDays = Quantity<SiderealDay>;
322/// A constant representing one sidereal day.
323pub const SIDEREAL_DAY: SiderealDays = SiderealDays::new(1.0);
324
325/// Mean synodic month (lunar phase cycle), expressed in seconds.
326///
327/// Convention used: `1 synodic month ≈ 29.530588 d`.
328#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
329#[unit(symbol = "synmo", dimension = Time, ratio = 29.530_588 * SECONDS_PER_DAY)]
330pub struct SynodicMonth;
331/// A quantity measured in synodic months.
332pub type SynodicMonths = Quantity<SynodicMonth>;
333/// A constant representing one synodic month.
334pub const SYNODIC_MONTH: SynodicMonths = SynodicMonths::new(1.0);
335
336/// Mean sidereal year (Earth), expressed in seconds.
337///
338/// Common convention: `1 sidereal year ≈ 365.256363004 d`.
339#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
340#[unit(symbol = "syr", dimension = Time, ratio = 365.256_363_004 * SECONDS_PER_DAY)]
341pub struct SiderealYear;
342/// A quantity measured in sidereal years.
343pub type SiderealYears = Quantity<SiderealYear>;
344/// A constant representing one sidereal year.
345pub const SIDEREAL_YEAR: SiderealYears = SiderealYears::new(1.0);
346
347#[cfg(test)]
348mod tests {
349    use super::*;
350    use approx::assert_abs_diff_eq;
351    use proptest::prelude::*;
352
353    // ─────────────────────────────────────────────────────────────────────────────
354    // Basic conversions
355    // ─────────────────────────────────────────────────────────────────────────────
356
357    #[test]
358    fn seconds_to_minutes() {
359        let sec = Seconds::new(60.0);
360        let min = sec.to::<Minute>();
361        assert_abs_diff_eq!(min.value(), 1.0, epsilon = 1e-12);
362    }
363
364    #[test]
365    fn minutes_to_hours() {
366        let min = Minutes::new(60.0);
367        let hr = min.to::<Hour>();
368        assert_abs_diff_eq!(hr.value(), 1.0, epsilon = 1e-12);
369    }
370
371    #[test]
372    fn hours_to_days() {
373        let hr = Hours::new(24.0);
374        let day = hr.to::<Day>();
375        assert_abs_diff_eq!(day.value(), 1.0, epsilon = 1e-12);
376    }
377
378    #[test]
379    fn seconds_86400_equals_one_day() {
380        let sec = Seconds::new(86400.0);
381        let day = sec.to::<Day>();
382        assert_abs_diff_eq!(day.value(), 1.0, epsilon = 1e-12);
383    }
384
385    #[test]
386    fn day_to_seconds() {
387        let day = Days::new(1.0);
388        let sec = day.to::<Second>();
389        assert_abs_diff_eq!(sec.value(), 86400.0, epsilon = 1e-9);
390    }
391
392    #[test]
393    fn days_to_weeks() {
394        let day = Days::new(7.0);
395        let week = day.to::<Week>();
396        assert_abs_diff_eq!(week.value(), 1.0, epsilon = 1e-12);
397    }
398
399    #[test]
400    fn julian_year_to_days() {
401        let jy = JulianYears::new(1.0);
402        let day = jy.to::<Day>();
403        assert_abs_diff_eq!(day.value(), 365.25, epsilon = 1e-9);
404    }
405
406    #[test]
407    fn julian_century_to_days() {
408        let jc = JulianCenturies::new(1.0);
409        let day = jc.to::<Day>();
410        assert_abs_diff_eq!(day.value(), 36525.0, epsilon = 1e-9);
411    }
412
413    #[test]
414    fn julian_century_to_julian_years() {
415        let jc = JulianCenturies::new(1.0);
416        let jy = jc.to::<JulianYear>();
417        assert_abs_diff_eq!(jy.value(), 100.0, epsilon = 1e-9);
418    }
419
420    #[test]
421    fn tropical_year_to_days() {
422        let y = Years::new(1.0);
423        let day = y.to::<Day>();
424        assert_abs_diff_eq!(day.value(), 365.2425, epsilon = 1e-9);
425    }
426
427    #[test]
428    fn century_to_days() {
429        let c = Centuries::new(1.0);
430        let day = c.to::<Day>();
431        assert_abs_diff_eq!(day.value(), 36524.25, epsilon = 1e-9);
432    }
433
434    #[test]
435    fn milliseconds_to_seconds() {
436        let ms = Milliseconds::new(1000.0);
437        let sec = ms.to::<Second>();
438        assert_abs_diff_eq!(sec.value(), 1.0, epsilon = 1e-9);
439    }
440
441    // ─────────────────────────────────────────────────────────────────────────────
442    // Roundtrip conversions
443    // ─────────────────────────────────────────────────────────────────────────────
444
445    #[test]
446    fn roundtrip_day_second() {
447        let original = Days::new(1.5);
448        let converted = original.to::<Second>();
449        let back = converted.to::<Day>();
450        assert_abs_diff_eq!(back.value(), original.value(), epsilon = 1e-12);
451    }
452
453    #[test]
454    fn roundtrip_julian_year_day() {
455        let original = JulianYears::new(2.5);
456        let converted = original.to::<Day>();
457        let back = converted.to::<JulianYear>();
458        assert_abs_diff_eq!(back.value(), original.value(), epsilon = 1e-12);
459    }
460
461    // ─────────────────────────────────────────────────────────────────────────────
462    // Ratio sanity checks
463    // ─────────────────────────────────────────────────────────────────────────────
464
465    #[test]
466    fn second_ratio_sanity() {
467        // Second::RATIO = 1.0 (canonical unit)
468        assert_abs_diff_eq!(Second::RATIO, 1.0, epsilon = 1e-15);
469    }
470
471    #[test]
472    fn minute_ratio_sanity() {
473        // 1 minute = 60 seconds
474        assert_abs_diff_eq!(Minute::RATIO, 60.0, epsilon = 1e-15);
475    }
476
477    #[test]
478    fn hour_ratio_sanity() {
479        // 1 hour = 3600 seconds
480        assert_abs_diff_eq!(Hour::RATIO, 3_600.0, epsilon = 1e-15);
481    }
482
483    // ─────────────────────────────────────────────────────────────────────────────
484    // Property-based tests
485    // ─────────────────────────────────────────────────────────────────────────────
486
487    proptest! {
488        #[test]
489        fn prop_roundtrip_day_second(d in -1e6..1e6f64) {
490            let original = Days::new(d);
491            let converted = original.to::<Second>();
492            let back = converted.to::<Day>();
493            prop_assert!((back.value() - original.value()).abs() < 1e-9);
494        }
495
496        #[test]
497        fn prop_day_second_ratio(d in 1e-6..1e6f64) {
498            let day = Days::new(d);
499            let sec = day.to::<Second>();
500            // 1 day = 86400 seconds
501            prop_assert!((sec.value() / day.value() - 86400.0).abs() < 1e-9);
502        }
503
504        #[test]
505        fn prop_julian_year_day_ratio(y in 1e-6..1e6f64) {
506            let jy = JulianYears::new(y);
507            let day = jy.to::<Day>();
508            // 1 Julian year = 365.25 days
509            prop_assert!((day.value() / jy.value() - 365.25).abs() < 1e-9);
510        }
511    }
512
513    // ─── SI sub-second units ────────────────────────────────────────────────
514
515    #[test]
516    fn attosecond_to_second() {
517        let q = Attoseconds::new(1e18);
518        let s = q.to::<Second>();
519        assert_abs_diff_eq!(s.value(), 1.0, epsilon = 1e-9);
520    }
521
522    #[test]
523    fn femtosecond_to_second() {
524        let q = Femtoseconds::new(1e15);
525        let s = q.to::<Second>();
526        assert_abs_diff_eq!(s.value(), 1.0, epsilon = 1e-9);
527    }
528
529    #[test]
530    fn picosecond_to_second() {
531        let q = Picoseconds::new(1e12);
532        let s = q.to::<Second>();
533        assert_abs_diff_eq!(s.value(), 1.0, epsilon = 1e-9);
534    }
535
536    #[test]
537    fn nanosecond_to_second() {
538        let q = Nanoseconds::new(1e9);
539        let s = q.to::<Second>();
540        assert_abs_diff_eq!(s.value(), 1.0, epsilon = 1e-9);
541    }
542
543    #[test]
544    fn microsecond_to_second() {
545        let q = Microseconds::new(1e6);
546        let s = q.to::<Second>();
547        assert_abs_diff_eq!(s.value(), 1.0, epsilon = 1e-9);
548    }
549
550    #[test]
551    fn centisecond_to_second() {
552        let q = Centiseconds::new(100.0);
553        let s = q.to::<Second>();
554        assert_abs_diff_eq!(s.value(), 1.0, epsilon = 1e-12);
555    }
556
557    #[test]
558    fn decisecond_to_second() {
559        let q = Deciseconds::new(10.0);
560        let s = q.to::<Second>();
561        assert_abs_diff_eq!(s.value(), 1.0, epsilon = 1e-12);
562    }
563
564    // ─── SI multi-second units ──────────────────────────────────────────────
565
566    #[test]
567    fn decasecond_to_second() {
568        let q = Decaseconds::new(1.0);
569        let s = q.to::<Second>();
570        assert_abs_diff_eq!(s.value(), 10.0, epsilon = 1e-12);
571    }
572
573    #[test]
574    fn hectosecond_to_second() {
575        let q = Hectoseconds::new(1.0);
576        let s = q.to::<Second>();
577        assert_abs_diff_eq!(s.value(), 100.0, epsilon = 1e-12);
578    }
579
580    #[test]
581    fn kilosecond_to_second() {
582        let q = Kiloseconds::new(1.0);
583        let s = q.to::<Second>();
584        assert_abs_diff_eq!(s.value(), 1_000.0, epsilon = 1e-12);
585    }
586
587    #[test]
588    fn megasecond_to_second() {
589        let q = Megaseconds::new(1.0);
590        let s = q.to::<Second>();
591        assert_abs_diff_eq!(s.value(), 1e6, epsilon = 1.0);
592    }
593
594    #[test]
595    fn gigasecond_to_second() {
596        let q = Gigaseconds::new(1.0);
597        let s = q.to::<Second>();
598        assert_abs_diff_eq!(s.value(), 1e9, epsilon = 1e3);
599    }
600
601    #[test]
602    fn terasecond_to_second() {
603        let q = Teraseconds::new(1.0);
604        let s = q.to::<Second>();
605        assert_abs_diff_eq!(s.value(), 1e12, epsilon = 1e6);
606    }
607
608    // ─── Civil units ────────────────────────────────────────────────────────
609
610    #[test]
611    fn fortnight_to_days() {
612        let q = Fortnights::new(1.0);
613        let d = q.to::<Day>();
614        assert_abs_diff_eq!(d.value(), 14.0, epsilon = 1e-12);
615    }
616
617    #[test]
618    fn decade_to_years() {
619        let q = Decades::new(1.0);
620        let y = q.to::<Year>();
621        assert_abs_diff_eq!(y.value(), 10.0, epsilon = 1e-9);
622    }
623
624    #[test]
625    fn millennium_to_years() {
626        let q = Millennia::new(1.0);
627        let y = q.to::<Year>();
628        assert_abs_diff_eq!(y.value(), 1000.0, epsilon = 1e-9);
629    }
630
631    // ─── Astronomical mean units ────────────────────────────────────────────
632
633    #[test]
634    fn sidereal_day_to_seconds() {
635        let q = SiderealDays::new(1.0);
636        let s = q.to::<Second>();
637        // 1 sidereal day ≈ 86164.0905 s
638        assert_abs_diff_eq!(s.value(), 86_164.090_5, epsilon = 1e-3);
639    }
640
641    #[test]
642    fn synodic_month_to_days() {
643        let q = SynodicMonths::new(1.0);
644        let d = q.to::<Day>();
645        // 1 synodic month ≈ 29.530588 d
646        assert_abs_diff_eq!(d.value(), 29.530_588, epsilon = 1e-6);
647    }
648
649    #[test]
650    fn sidereal_year_to_days() {
651        let q = SiderealYears::new(1.0);
652        let d = q.to::<Day>();
653        // 1 sidereal year ≈ 365.256363004 d
654        assert_abs_diff_eq!(d.value(), 365.256_363_004, epsilon = 1e-6);
655    }
656
657    // ─── Symbol checks ──────────────────────────────────────────────────────
658
659    #[test]
660    fn symbols_are_correct() {
661        assert_eq!(format!("{}", Attoseconds::new(1.0)), "1 as");
662        assert_eq!(format!("{}", Nanoseconds::new(1.0)), "1 ns");
663        assert_eq!(format!("{}", Kiloseconds::new(1.0)), "1 ks");
664        assert_eq!(format!("{}", Fortnights::new(1.0)), "1 fn");
665        assert_eq!(format!("{}", SiderealDays::new(1.0)), "1 sd");
666    }
667}