1use crate::{
2 ATTOS_PER_FS, ATTOS_PER_MS, ATTOS_PER_NS, ATTOS_PER_PS, ATTOS_PER_SEC, ATTOS_PER_SEC_I128,
3 ATTOS_PER_US, Dt, Real, SEC_PER_DAYI64, SEC_PER_WEEK, Scale,
4 TAI_SECS_1970_MIDNIGHT_TO_2000_NOON,
5};
6
7impl Dt {
8 pub const ZERO: Self = Self::new(0, 0);
12
13 pub const UNIX_EPOCH: Self = Self::new(-TAI_SECS_1970_MIDNIGHT_TO_2000_NOON, 0);
21
22 pub const TAI_1977_EPOCH: Self = Self::new(-725_803_200, 0);
30
31 pub const TCL_1977_EPOCH: Self = { Self::TAI_1977_EPOCH.to(Scale::TAI, Scale::TCL) };
37
38 pub const CXC_EPOCH: Self = Self::new(-63_115_233, 816000000000000000);
46
47 pub const GPS_EPOCH: Self = Self::new(-630_763_200 + 19, 0);
55
56 pub const GALILEO_EPOCH: Self = Self::new(-11_448_000 + 19, 0);
64
65 pub const BDT_EPOCH: Self = Self::new(189_345_600 + 33, 0);
73
74 pub const MAX: Self = Self {
76 sec: i64::MAX,
77 attos: ATTOS_PER_SEC - 1,
78 };
79
80 pub const MIN: Self = Self {
82 sec: i64::MIN,
83 attos: 0,
84 };
85
86 pub const SEC_19: Self = Self::new(19, 0);
87 pub const SEC_33: Self = Self::new(33, 0);
88 pub const SEC_37: Self = Self::new(37, 0);
89 pub const ONE_DAY: Self = Self::new(SEC_PER_DAYI64, 0);
90
91 #[inline]
94 pub const fn new(sec: i64, attos: u64) -> Self {
95 let mut tp = Self { sec, attos };
96 tp.carry_over_mut();
97 tp
98 }
99
100 #[inline]
101 pub const fn from_attos(attos: i128, scale: Scale) -> Self {
102 Self::from_dt(Dt::attos_to_dt(attos), scale)
103 }
104
105 #[inline]
106 pub const fn from_sec(sec: i64, scale: Scale) -> Self {
107 Self::from(sec, 0, scale)
108 }
109
110 #[inline]
111 pub const fn from_ms(ms: i128, scale: Scale) -> Self {
112 let attos = ms.saturating_mul(ATTOS_PER_MS as i128);
113 Self::from_dt(Dt::attos_to_dt(attos), scale)
114 }
115
116 #[inline]
117 pub const fn from_us(us: i128, scale: Scale) -> Self {
118 let attos = us.saturating_mul(ATTOS_PER_US as i128);
119 Self::from_dt(Dt::attos_to_dt(attos), scale)
120 }
121
122 #[inline]
123 pub const fn from_ns(ns: i128, scale: Scale) -> Self {
124 let attos = ns.saturating_mul(ATTOS_PER_NS as i128);
125 Self::from_dt(Dt::attos_to_dt(attos), scale)
126 }
127
128 #[inline]
129 pub const fn from_ps(ps: i128, scale: Scale) -> Self {
130 let attos = ps.saturating_mul(ATTOS_PER_PS as i128);
131 Self::from_dt(Dt::attos_to_dt(attos), scale)
132 }
133
134 #[inline]
135 pub const fn from_fs(fs: i128, scale: Scale) -> Self {
136 let attos = fs.saturating_mul(ATTOS_PER_FS as i128);
137 Self::from_dt(Dt::attos_to_dt(attos), scale)
138 }
139
140 #[inline]
141 pub const fn from_min(m: i64, scale: Scale) -> Self {
142 Self::from(m * 60, 0, scale)
143 }
144
145 #[inline]
146 pub const fn from_hr(h: i64, scale: Scale) -> Self {
147 Self::from(h * 3600, 0, scale)
148 }
149
150 pub const fn from_hms(
153 hr: i64,
154 min: i64,
155 sec: i64,
156 ms: i128,
157 us: i128,
158 ns: i128,
159 scale: Scale,
160 ) -> Self {
161 let total_sec = hr * 3600i64 + min * 60i64 + sec;
162
163 let sub_ns = ms * 1_000_000i128 + us * 1_000i128 + ns;
164
165 if sub_ns == 0 {
166 return Self::from(total_sec, 0, scale);
167 }
168
169 let abs_ns = sub_ns.unsigned_abs();
170 let extra_sec = (abs_ns / 1_000_000_000u128) as i64;
171 let rem_ns = abs_ns % 1_000_000_000u128;
172 let frac = (rem_ns as u64) * ATTOS_PER_NS;
173
174 let (final_sec, final_frac) = if sub_ns >= 0 {
175 (total_sec + extra_sec, frac)
176 } else if frac == 0 {
177 (total_sec - extra_sec, 0)
178 } else {
179 (total_sec - extra_sec - 1, ATTOS_PER_SEC - frac)
180 };
181
182 Self::from(final_sec, final_frac, scale)
183 }
184
185 pub(crate) const fn attos_to_dt(attos: i128) -> Self {
186 let q = safe_div_euc!(attos, ATTOS_PER_SEC_I128, 0i128);
187
188 if q > (i64::MAX as i128) {
189 Self::MAX
190 } else if q < (i64::MIN as i128) {
191 Self::MIN
192 } else {
193 let r = safe_rem_euc!(attos, ATTOS_PER_SEC_I128, 0i128);
194 Self {
195 sec: q as i64,
196 attos: r as u64,
197 }
198 }
199 }
200
201 #[inline]
202 pub const fn from_days(d: i64, scale: Scale) -> Dt {
203 Self::from_sec(d.saturating_mul(SEC_PER_DAYI64), scale)
204 }
205
206 #[inline]
207 pub const fn wk(wk: i64, scale: Scale) -> Dt {
208 Dt::from_sec(wk.saturating_mul(SEC_PER_WEEK), scale)
209 }
210
211 #[inline]
212 pub const fn yr(yr: i64, scale: Scale) -> Dt {
213 Dt::from_sec(yr.saturating_mul(31_557_600), scale)
214 }
215
216 #[inline]
218 pub const fn ago(self, scale: Scale) -> Dt {
219 Dt::from(0, 0, scale).sub(self)
220 }
221
222 #[inline]
224 pub const fn from_now(self, scale: Scale) -> Dt {
225 Dt::from(0, 0, scale).add(self)
226 }
227
228 #[inline]
230 pub const fn neg(self) -> Self {
231 if self.attos == 0 {
232 Self {
233 sec: -self.sec,
234 attos: 0,
235 }
236 } else {
237 Self {
238 sec: -self.sec - 1,
239 attos: ATTOS_PER_SEC - self.attos,
240 }
241 }
242 }
243
244 #[inline]
246 pub const fn abs(self) -> Self {
247 Self::from_attos(self.to_attos().saturating_abs(), Scale::TAI)
248 }
249
250 #[inline]
252 pub const fn from_sec_f(sec_f: Real) -> Self {
253 Self::from_sec_f_on(sec_f, Scale::TAI)
254 }
255
256 pub const fn sec_f_to_total_attos(sec_f: f64) -> i128 {
259 if sec_f == 0.0 {
260 return 0;
261 }
262
263 let bits = sec_f.to_bits();
264 let is_negative = (bits >> 63) != 0;
265 let biased_exp = ((bits >> 52) & 0x7ff) as i32;
266 let mantissa = bits & 0x000f_ffff_ffff_ffff;
267
268 let (sig, exp) = if biased_exp == 0 {
270 if mantissa == 0 {
272 return 0;
273 }
274 let lz = mantissa.leading_zeros() as i32;
275 let shift = lz - 11;
276 let sig = (mantissa as u128) << shift;
277 (sig, -1022i32 - 52 + shift)
278 } else {
279 let sig = ((1u64 << 52) | mantissa) as u128;
280 (sig, biased_exp - 1023 - 52)
281 };
282
283 const FIVE_POW_18: u64 = 3_814_697_265_625; let product = sig * (FIVE_POW_18 as u128);
285 let total_exp = exp + 18;
286
287 if total_exp > 120 {
289 return if is_negative { i128::MIN } else { i128::MAX };
290 }
291 if total_exp < -200 {
292 return 0;
293 }
294
295 let abs_total = if total_exp >= 0 {
296 if total_exp >= 128 {
297 return if is_negative { i128::MIN } else { i128::MAX };
298 }
299 (product << total_exp) as i128
300 } else {
301 let shift = (-total_exp) as u32;
302 let int_part = (product >> shift) as i128;
303
304 let mask = (1u128 << shift) - 1;
309 let rem = product & mask;
310 if rem > (mask >> 1) {
311 int_part + 1
312 } else {
313 int_part
314 }
315 };
316
317 if is_negative { -abs_total } else { abs_total }
318 }
319
320 pub const fn from_sec_f_on(sec_f: Real, s: Scale) -> Dt {
325 if sec_f.is_nan() {
326 return Self::ZERO;
327 } else if sec_f.is_infinite() {
328 return if sec_f.is_sign_positive() {
329 Self::MAX
330 } else {
331 Self::MIN
332 };
333 }
334
335 let total_attos = Self::sec_f_to_total_attos(sec_f);
336
337 let floor_sec = total_attos.div_euclid(ATTOS_PER_SEC_I128);
341 let mut attos = total_attos.rem_euclid(ATTOS_PER_SEC_I128);
342
343 if attos.abs() < 1 {
347 attos = 0;
348 }
349
350 let total = floor_sec * ATTOS_PER_SEC_I128 + attos;
351 Self::from_attos(total, s)
352 }
353
354 #[cfg(all(feature = "std", not(all(target_arch = "wasm32", feature = "js"))))]
359 #[inline]
360 pub fn now() -> Self {
361 let now = std::time::SystemTime::now();
362 let (secs, nanos) = match now.duration_since(std::time::UNIX_EPOCH) {
363 Ok(dur) => (dur.as_secs() as i64, dur.subsec_nanos() as i64),
364 Err(_) => {
365 let dur = std::time::SystemTime::UNIX_EPOCH
367 .duration_since(now)
368 .unwrap();
369 (-(dur.as_secs() as i64), -(dur.subsec_nanos() as i64))
370 }
371 };
372
373 Dt::from_diff_and_scale(Dt::new(secs, 0), Dt::UNIX_EPOCH, Scale::UTC)
374 .add(Dt::from_ns(nanos as i128, Scale::TAI))
375 }
376
377 #[cfg(all(target_arch = "wasm32", feature = "js"))]
380 #[inline]
381 pub fn now() -> Self {
382 let ms: f64 = js_sys::Date::now();
383 let secs = (ms / 1000.0).floor() as i64;
384 let nanos = ((ms % 1000.0) * 1_000_000.0) as i128;
385
386 Dt::from_diff_and_scale(Dt::new(secs, 0), Dt::UNIX_EPOCH, Scale::UTC)
387 .add(Dt::from_ns(nanos as i128, Scale::TAI))
388 }
389}