1#[cfg(any(feature = "js", feature = "std"))]
2use crate::DtErr;
3use crate::{
4 ATTOS_PER_FS, ATTOS_PER_MS, ATTOS_PER_NS, ATTOS_PER_PS, ATTOS_PER_SEC, ATTOS_PER_SEC_I128,
5 ATTOS_PER_US, Dt, Real, SEC_PER_DAYI64, SEC_PER_WEEK, Scale,
6 TAI_SECS_1970_MIDNIGHT_TO_2000_NOON,
7};
8
9impl Dt {
10 pub const ZERO: Self = Self::new(0, 0);
14
15 pub const NTP_EPOCH: Self = Self::new(-3155716800, 0);
22
23 pub const UNIX_EPOCH: Self = Self::new(-TAI_SECS_1970_MIDNIGHT_TO_2000_NOON, 0);
31
32 pub const TAI_1977_EPOCH: Self = Self::new(-725_803_200, 0);
39
40 pub const TCL_1977_EPOCH: Self = { Self::TAI_1977_EPOCH.to(Scale::TAI, Scale::TCL) };
45
46 pub const CXC_EPOCH: Self = Self::new(-63_115_233, 816000000000000000);
53
54 pub const GPS_EPOCH: Self = Self::new(-630_763_200 + 19, 0);
61
62 pub const GALILEO_EPOCH: Self = Self::new(-11_448_000 + 19, 0);
69
70 pub const BDT_EPOCH: Self = Self::new(189_345_600 + 33, 0);
77
78 pub const MAX: Self = Self {
80 sec: i64::MAX,
81 attos: ATTOS_PER_SEC - 1,
82 };
83
84 pub const MIN: Self = Self {
86 sec: i64::MIN,
87 attos: 0,
88 };
89
90 pub const SEC_19: Self = Self::new(19, 0);
91 pub const SEC_33: Self = Self::new(33, 0);
92 pub const SEC_37: Self = Self::new(37, 0);
93 pub const ONE_DAY: Self = Self::new(SEC_PER_DAYI64, 0);
94
95 #[inline]
98 pub const fn new(sec: i64, attos: u64) -> Self {
99 let mut tp = Self { sec, attos };
100 tp.carry_attos_mut();
101 tp
102 }
103
104 #[inline]
105 pub const fn from_attos(attos: i128, scale: Scale) -> Self {
106 Self::from_dt(Dt::attos_to_dt(attos), scale)
107 }
108
109 #[inline]
110 pub const fn from_sec(sec: i64, scale: Scale) -> Self {
111 Self::from(sec, 0, scale)
112 }
113
114 #[inline]
115 pub const fn from_ms(ms: i128, scale: Scale) -> Self {
116 let attos = ms.saturating_mul(ATTOS_PER_MS as i128);
117 Self::from_dt(Dt::attos_to_dt(attos), scale)
118 }
119
120 #[inline]
121 pub const fn from_us(us: i128, scale: Scale) -> Self {
122 let attos = us.saturating_mul(ATTOS_PER_US as i128);
123 Self::from_dt(Dt::attos_to_dt(attos), scale)
124 }
125
126 #[inline]
127 pub const fn from_ns(ns: i128, scale: Scale) -> Self {
128 let attos = ns.saturating_mul(ATTOS_PER_NS as i128);
129 Self::from_dt(Dt::attos_to_dt(attos), scale)
130 }
131
132 #[inline]
133 pub const fn from_ps(ps: i128, scale: Scale) -> Self {
134 let attos = ps.saturating_mul(ATTOS_PER_PS as i128);
135 Self::from_dt(Dt::attos_to_dt(attos), scale)
136 }
137
138 #[inline]
139 pub const fn from_fs(fs: i128, scale: Scale) -> Self {
140 let attos = fs.saturating_mul(ATTOS_PER_FS as i128);
141 Self::from_dt(Dt::attos_to_dt(attos), scale)
142 }
143
144 #[inline]
145 pub const fn from_min(m: i64, scale: Scale) -> Self {
146 Self::from(m * 60, 0, scale)
147 }
148
149 #[inline]
150 pub const fn from_hr(h: i64, scale: Scale) -> Self {
151 Self::from(h * 3600, 0, scale)
152 }
153
154 pub const fn from_hms(
157 hr: i64,
158 min: i64,
159 sec: i64,
160 ms: i128,
161 us: i128,
162 ns: i128,
163 scale: Scale,
164 ) -> Self {
165 let total_sec = hr * 3600i64 + min * 60i64 + sec;
166
167 let sub_ns = ms * 1_000_000i128 + us * 1_000i128 + ns;
168
169 if sub_ns == 0 {
170 return Self::from(total_sec, 0, scale);
171 }
172
173 let abs_ns = sub_ns.unsigned_abs();
174 let extra_sec = (abs_ns / 1_000_000_000u128) as i64;
175 let rem_ns = abs_ns % 1_000_000_000u128;
176 let frac = (rem_ns as u64) * ATTOS_PER_NS;
177
178 let (final_sec, final_frac) = if sub_ns >= 0 {
179 (total_sec + extra_sec, frac)
180 } else if frac == 0 {
181 (total_sec - extra_sec, 0)
182 } else {
183 (total_sec - extra_sec - 1, ATTOS_PER_SEC - frac)
184 };
185
186 Self::from(final_sec, final_frac, scale)
187 }
188
189 pub(crate) const fn attos_to_dt(attos: i128) -> Self {
190 let q = safe_div_euc!(attos, ATTOS_PER_SEC_I128, 0i128);
191
192 if q > (i64::MAX as i128) {
193 Self::MAX
194 } else if q < (i64::MIN as i128) {
195 Self::MIN
196 } else {
197 let r = safe_rem_euc!(attos, ATTOS_PER_SEC_I128, 0i128);
198 Self {
199 sec: q as i64,
200 attos: r as u64,
201 }
202 }
203 }
204
205 #[inline]
206 pub const fn from_days(d: i64, scale: Scale) -> Dt {
207 Self::from_sec(d.saturating_mul(SEC_PER_DAYI64), scale)
208 }
209
210 #[inline]
211 pub const fn wk(wk: i64, scale: Scale) -> Dt {
212 Dt::from_sec(wk.saturating_mul(SEC_PER_WEEK), scale)
213 }
214
215 #[inline]
216 pub const fn yr(yr: i64, scale: Scale) -> Dt {
217 Dt::from_sec(yr.saturating_mul(31_557_600), scale)
218 }
219
220 #[inline]
222 pub const fn ago(self, scale: Scale) -> Dt {
223 Dt::from(0, 0, scale).sub(self)
224 }
225
226 #[inline]
228 pub const fn from_now(self, scale: Scale) -> Dt {
229 Dt::from(0, 0, scale).add(self)
230 }
231
232 #[inline]
234 pub const fn neg(self) -> Self {
235 if self.attos == 0 {
236 Self {
237 sec: -self.sec,
238 attos: 0,
239 }
240 } else {
241 Self {
242 sec: -self.sec - 1,
243 attos: ATTOS_PER_SEC - self.attos,
244 }
245 }
246 }
247
248 #[inline]
250 pub const fn abs(self) -> Self {
251 Self::from_attos(self.to_attos().saturating_abs(), Scale::TAI)
252 }
253
254 #[inline]
256 pub const fn from_sec_f(sec_f: Real) -> Self {
257 Self::from_sec_f_on(sec_f, Scale::TAI)
258 }
259
260 pub const fn sec_f_to_total_attos(sec_f: f64) -> i128 {
263 if sec_f == 0.0 {
264 return 0;
265 }
266
267 let bits = sec_f.to_bits();
268 let is_negative = (bits >> 63) != 0;
269 let biased_exp = ((bits >> 52) & 0x7ff) as i32;
270 let mantissa = bits & 0x000f_ffff_ffff_ffff;
271
272 let (sig, exp) = if biased_exp == 0 {
274 if mantissa == 0 {
276 return 0;
277 }
278 let lz = mantissa.leading_zeros() as i32;
279 let shift = lz - 11;
280 let sig = (mantissa as u128) << shift;
281 (sig, -1022i32 - 52 + shift)
282 } else {
283 let sig = ((1u64 << 52) | mantissa) as u128;
284 (sig, biased_exp - 1023 - 52)
285 };
286
287 const FIVE_POW_18: u64 = 3_814_697_265_625; let product = sig * (FIVE_POW_18 as u128);
289 let total_exp = exp + 18;
290
291 if total_exp > 120 {
293 return if is_negative { i128::MIN } else { i128::MAX };
294 }
295 if total_exp < -200 {
296 return 0;
297 }
298
299 let abs_total = if total_exp >= 0 {
300 if total_exp >= 128 {
301 return if is_negative { i128::MIN } else { i128::MAX };
302 }
303 (product << total_exp) as i128
304 } else {
305 let shift = (-total_exp) as u32;
306 let int_part = (product >> shift) as i128;
307
308 let mask = (1u128 << shift) - 1;
313 let rem = product & mask;
314 if rem > (mask >> 1) {
315 int_part + 1
316 } else {
317 int_part
318 }
319 };
320
321 if is_negative { -abs_total } else { abs_total }
322 }
323
324 pub const fn from_sec_f_on(sec_f: Real, s: Scale) -> Dt {
329 if sec_f.is_nan() {
330 return Self::ZERO;
331 } else if sec_f.is_infinite() {
332 return if sec_f.is_sign_positive() {
333 Self::MAX
334 } else {
335 Self::MIN
336 };
337 }
338
339 let total_attos = Self::sec_f_to_total_attos(sec_f);
340
341 let floor_sec = total_attos.div_euclid(ATTOS_PER_SEC_I128);
345 let mut attos = total_attos.rem_euclid(ATTOS_PER_SEC_I128);
346
347 if attos.abs() < 1 {
351 attos = 0;
352 }
353
354 let total = floor_sec * ATTOS_PER_SEC_I128 + attos;
355 Self::from_attos(total, s)
356 }
357
358 #[cfg(all(feature = "std", not(all(target_arch = "wasm32", feature = "js"))))]
363 #[inline]
364 pub fn now() -> Result<Self, DtErr> {
365 let now = std::time::SystemTime::now();
366 let (secs, nanos) = match now.duration_since(std::time::UNIX_EPOCH) {
367 Ok(dur) => (dur.as_secs() as i64, dur.subsec_nanos() as i64),
368 Err(_) => {
369 use crate::{DtErrKind, an_err};
371 let dur = std::time::SystemTime::UNIX_EPOCH
372 .duration_since(now)
373 .map_err(|e| an_err!(DtErrKind::IOErr, "{}", e))?;
374 (-(dur.as_secs() as i64), -(dur.subsec_nanos() as i64))
375 }
376 };
377 Ok(
378 Dt::from_diff_and_scale(Dt::new(secs, 0), Dt::UNIX_EPOCH, Scale::UTC)
379 .add(Dt::from_ns(nanos as i128, Scale::TAI)),
380 )
381 }
382
383 #[cfg(all(target_arch = "wasm32", feature = "js"))]
386 #[inline]
387 pub fn now() -> Result<Self, DtErr> {
388 let ms: f64 = js_sys::Date::now();
389 let secs = (ms / 1000.0).floor() as i64;
390 let nanos = ((ms % 1000.0) * 1_000_000.0) as i128;
391 Ok(
392 Dt::from_diff_and_scale(Dt::new(secs, 0), Dt::UNIX_EPOCH, Scale::UTC)
393 .add(Dt::from_ns(nanos as i128, Scale::TAI)),
394 )
395 }
396}