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