deep_time/dt/mod.rs
1mod arithmetic;
2mod constructors;
3mod conveniences;
4mod conversions;
5mod decimal_year;
6mod from_ccsds;
7mod from_str;
8mod gregorian;
9mod julian_date;
10mod ops;
11mod tdb;
12mod to_ccsds_bin;
13mod to_str;
14
15pub mod lunar;
16pub mod numbers_traits;
17pub mod trajectory;
18
19#[cfg(feature = "alloc")]
20mod formatting;
21#[cfg(feature = "alloc")]
22mod to_ccsds_str;
23
24#[cfg(feature = "mars")]
25pub mod mars;
26
27#[cfg(feature = "hifitime")]
28mod hifitime;
29
30#[cfg(feature = "chrono")]
31mod chrono;
32
33#[cfg(feature = "jiff")]
34mod jiff;
35
36use crate::ATTOS_PER_SEC;
37use core::fmt;
38
39/// ## [`Dt`] A high-precision instant or duration with attosecond resolution.
40///
41/// This is the core time type of the library. It represents both absolute
42/// instants and durations using the same compact representation, making it
43/// convenient anywhere precise time measurement or arithmetic is needed.
44///
45/// ## Representation
46///
47/// It stores:
48///
49/// - `sec: i64` — whole seconds (signed).
50/// - `attos: u64` — fractional seconds in attoseconds (`0 ≤ attos < 10¹⁸`).
51/// - These always push the `Dt` towards the positive.
52///
53/// This gives a resolution of one attosecond while supporting a range of
54/// roughly ±292 billion years. An [`i128`] was considered but decided against
55/// due to the difficulty of math without overflow.
56///
57/// There are many different ways to go to and from a `Dt` see the [`documentation`](../struct.Dt.html)
58/// for the full list of methods.
59///
60/// It implements `Copy` and `Clone`. Optional derives for `serde` and
61/// `tsify` are available behind the corresponding features.
62///
63/// ## Reference epoch and scales
64///
65/// When using the conversion functions [`Dt::to`] and [`Dt::from`] the
66/// epoch for **all** time scales is [`Dt::ZERO`] 2000-01-01 noon.
67///
68/// Many convenience constructors and accessors exist for common epochs
69/// (UNIX, GPS, Galileo, BeiDou, CXC, 1977 TT/TCG/TCB, etc.).
70///
71/// See the [`Scale`] documentation for the complete list of supported scales,
72/// leap-second handling, historical UTC models, relativistic coordinate times
73/// (TCG, TCB), and the lunar scales LTC / TCL (based on the LTE440 model).
74///
75/// ## Arithmetic and manipulation
76///
77/// `Dt` provides rich const-friendly arithmetic:
78///
79/// - Addition and subtraction of durations
80/// - Multiplication and division by integers or `Real` (f64)
81/// - `floor`, `ceil`, `round` to an arbitrary unit
82/// - Many convenience increment/decrement methods (`add_1ns`, `sub_ms`, …)
83/// - Signed difference via [`to_diff_raw`](Self::to_diff_raw)
84///
85/// Relativistic proper-time corrections and clock-drift models are supported
86/// via [`convert_using_drift`](Self::convert_using_drift) and related methods.
87///
88/// ## Notes
89///
90/// - `Dt` does **not** store a time scale internally. The scale is always
91/// an explicit parameter of conversion and construction methods.
92/// - Leap-second handling follows the chosen `Scale` (UTC, UTCSpice, UTCSofa).
93#[derive(Clone, Copy)]
94#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
95#[cfg_attr(feature = "js", derive(tsify::Tsify))]
96pub struct Dt {
97 pub sec: i64,
98 pub attos: u64,
99}
100
101impl Dt {
102 /// Normalizes the representation so that the attosecond part lies in the range `[0, ATTOS_PER_SEC)`.
103 #[inline]
104 pub const fn carry_attos_mut(&mut self) -> &mut Self {
105 if self.attos >= ATTOS_PER_SEC {
106 self.sec = self.sec.saturating_add((self.attos / ATTOS_PER_SEC) as i64);
107 self.attos %= ATTOS_PER_SEC;
108 }
109 self
110 }
111
112 /// Normalizes the representation so that the attosecond part lies in the range `[0, ATTOS_PER_SEC)`.
113 #[inline]
114 pub const fn carry_attos(&self) -> Self {
115 if self.attos < ATTOS_PER_SEC {
116 return *self;
117 }
118 Self {
119 sec: self.sec.saturating_add((self.attos / ATTOS_PER_SEC) as i64),
120 attos: self.attos % ATTOS_PER_SEC,
121 }
122 }
123}
124
125impl Default for Dt {
126 fn default() -> Self {
127 Self::ZERO
128 }
129}
130
131impl fmt::Display for Dt {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 let sec = self.sec;
134 let attos = self.attos;
135
136 // Default to nanosecond precision (9 digits) — most useful for everyday use
137 let precision = f.precision().unwrap_or(9);
138
139 // Respect the `+` sign when the user writes {:+}
140 if f.sign_plus() && sec >= 0 {
141 write!(f, "+")?;
142 }
143
144 write!(f, "{}", sec)?;
145
146 if precision > 0 {
147 let prec = precision.min(18);
148 let scale = 10u64.pow(18 - prec as u32);
149 let value = attos / scale;
150 write!(f, ".{:0>width$}", value, width = prec)?;
151 }
152
153 Ok(())
154 }
155}
156
157impl fmt::Debug for Dt {
158 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159 f.debug_struct("Dt")
160 .field("sec", &self.sec)
161 .field("attos", &self.attos)
162 .finish()
163 }
164}