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_SECF, ATTOS_PER_US, Dt, Real, SEC_PER_DAYI64, SEC_PER_WEEK, Scale,
4 TAI_SECS_1970_MIDNIGHT_TO_2000_NOON, floor_f,
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();
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 = attos.div_euclid(ATTOS_PER_SEC_I128);
187
188 if q > (i64::MAX as i128) {
189 return Self::MAX;
190 } else if q < (i64::MIN as i128) {
191 return Self::MIN;
192 } else {
193 let r = attos.rem_euclid(ATTOS_PER_SEC_I128);
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 from_sec_f_on(sec_f: Real, s: Scale) -> Self {
257 if sec_f.is_nan() {
258 return Self::ZERO;
259 }
260 if sec_f.is_infinite() {
261 return if sec_f.is_sign_positive() {
262 Self::MAX
263 } else {
264 Self::MIN
265 };
266 }
267
268 let floor_val = floor_f(sec_f);
269 let frac = sec_f - floor_val;
270 let attos_frac = (frac * ATTOS_PER_SECF) as i128;
271
272 let total = (floor_val as i128) * ATTOS_PER_SEC_I128 + attos_frac;
273 Self::from_attos(total, s)
274 }
275
276 #[cfg(all(feature = "std", not(all(target_arch = "wasm32", feature = "js"))))]
281 #[inline]
282 pub fn now() -> Self {
283 let now = std::time::SystemTime::now();
284 let (secs, nanos) = match now.duration_since(std::time::UNIX_EPOCH) {
285 Ok(dur) => (dur.as_secs() as i64, dur.subsec_nanos() as i64),
286 Err(_) => {
287 let dur = std::time::SystemTime::UNIX_EPOCH
289 .duration_since(now)
290 .unwrap();
291 (-(dur.as_secs() as i64), -(dur.subsec_nanos() as i64))
292 }
293 };
294
295 Dt::from_diff_and_scale(Dt::new(secs, 0), Dt::UNIX_EPOCH, Scale::UTC)
296 .add(Dt::from_ns(nanos as i128, Scale::TAI))
297 }
298
299 #[cfg(all(target_arch = "wasm32", feature = "js"))]
302 #[inline]
303 pub fn now() -> Self {
304 let ms: f64 = js_sys::Date::now();
305 let secs = (ms / 1000.0).floor() as i64;
306 let nanos = ((ms % 1000.0) * 1_000_000.0) as i128;
307
308 Dt::from_diff_and_scale(Dt::new(secs, 0), Dt::UNIX_EPOCH, Scale::UTC)
309 .add(Dt::from_ns(nanos as i128, Scale::TAI))
310 }
311}