deep_time/dt/to_unit.rs
1use crate::{
2 ATTOS_PER_DAY, ATTOS_PER_FS_I128, ATTOS_PER_HOUR, ATTOS_PER_MIN, ATTOS_PER_MS_I128,
3 ATTOS_PER_NS_I128, ATTOS_PER_PS_I128, ATTOS_PER_SEC_I128, ATTOS_PER_SECF, ATTOS_PER_US_I128,
4 Dt, Real, dt,
5};
6
7impl Dt {
8 /// Returns this duration as a whole number of seconds, dropping any
9 /// fraction (always toward zero: 1.7 → 1, −1.7 → −1).
10 ///
11 /// Same as `self.attos / ATTOS_PER_SEC_I128`. Does not round.
12 ///
13 /// On positive values this matches
14 /// [`to_sec_floor`](../struct.Dt.html#method.to_sec_floor).
15 /// On negatives they diverge: here −1.3 → −1, while `to_sec_floor` gives
16 /// −2 (always rounds down). Prefer `to_sec_floor` when you also need a
17 /// non-negative fractional part via
18 /// [`to_sec_ufrac`](../struct.Dt.html#method.to_sec_ufrac).
19 ///
20 /// ## Examples
21 ///
22 /// ```rust
23 /// use deep_time::Dt;
24 /// use deep_time::macros::{from_sec, ms};
25 ///
26 /// // Fraction only — less than one second either way → 0
27 /// assert_eq!(from_sec!(0, -ms!(300)).to_sec(), 0);
28 /// assert_eq!(from_sec!(0, ms!(300)).to_sec(), 0);
29 ///
30 /// // -1.3 s → -1 here, but -2 with `to_sec_floor`
31 /// let dt = from_sec!(-1, -ms!(300));
32 /// assert_eq!(dt.to_sec(), -1);
33 /// assert_eq!(dt.to_sec_floor(), -2);
34 ///
35 /// // Positive values match `to_sec_floor`
36 /// let dt = from_sec!(1, ms!(300));
37 /// assert_eq!(dt.to_sec(), 1);
38 /// assert_eq!(dt.to_sec_floor(), 1);
39 /// ```
40 #[inline(always)]
41 pub const fn to_sec(&self) -> i128 {
42 self.attos / ATTOS_PER_SEC_I128
43 }
44
45 /// Same as [`to_sec`](../struct.Dt.html#method.to_sec), but returns an
46 /// [`i64`].
47 ///
48 /// Values outside the `i64` range clamp to [`i64::MAX`] or [`i64::MIN`].
49 ///
50 /// ## Examples
51 ///
52 /// ```rust
53 /// use deep_time::Dt;
54 /// use deep_time::macros::{from_sec, ms};
55 ///
56 /// // Same toward-zero rule as `to_sec`
57 /// assert_eq!(from_sec!(1, ms!(300)).to_sec64(), 1);
58 /// assert_eq!(from_sec!(-1, -ms!(300)).to_sec64(), -1);
59 ///
60 /// // Fits in i64 here; huge values would clamp instead of panicking
61 /// assert_eq!(from_sec!(i64::MAX as i128).to_sec64(), i64::MAX);
62 /// ```
63 #[inline(always)]
64 pub const fn to_sec64(&self) -> i64 {
65 Self::to_i64(self.attos / ATTOS_PER_SEC_I128)
66 }
67
68 /// Returns whole seconds, always rounding down (toward −∞).
69 ///
70 /// So 1.3 → 1 and −1.3 → −2. The leftover attoseconds are then always
71 /// ≥ 0; get them with
72 /// [`to_sec_ufrac`](../struct.Dt.html#method.to_sec_ufrac).
73 ///
74 /// For truncation toward zero (1.3 → 1, −1.3 → −1), use
75 /// [`to_sec`](../struct.Dt.html#method.to_sec).
76 /// For nearest-second rounding, use
77 /// [`to_sec_round`](../struct.Dt.html#method.to_sec_round).
78 ///
79 /// ## Examples
80 ///
81 /// ```rust
82 /// use deep_time::Dt;
83 /// use deep_time::macros::{from_sec, ms};
84 ///
85 /// // -1.3 s → whole -2, leftover +0.7 s
86 /// let dt = from_sec!(-1, -ms!(300));
87 /// assert_eq!(dt.to_sec_floor(), -2);
88 /// assert_eq!(dt.to_sec_ufrac(), ms!(700) as u64);
89 ///
90 /// // +1.3 s → whole 1, leftover +0.3 s
91 /// let dt = from_sec!(1, ms!(300));
92 /// assert_eq!(dt.to_sec_floor(), 1);
93 /// assert_eq!(dt.to_sec_ufrac(), ms!(300) as u64);
94 /// ```
95 #[inline(always)]
96 pub const fn to_sec_floor(&self) -> i128 {
97 self.attos.div_euclid(ATTOS_PER_SEC_I128)
98 }
99
100 /// Same as [`to_sec_floor`](../struct.Dt.html#method.to_sec_floor),
101 /// but returns an [`i64`].
102 ///
103 /// Values outside the `i64` range clamp to [`i64::MAX`] or [`i64::MIN`].
104 /// Pair with [`to_sec_ufrac`](../struct.Dt.html#method.to_sec_ufrac)
105 /// for the leftover.
106 ///
107 /// ## Examples
108 ///
109 /// ```rust
110 /// use deep_time::Dt;
111 /// use deep_time::macros::{from_sec, ms};
112 ///
113 /// let dt = from_sec!(-1, -ms!(300));
114 /// assert_eq!(dt.to_sec64_floor(), -2);
115 /// assert_eq!(dt.to_sec_ufrac(), ms!(700) as u64);
116 ///
117 /// let dt = from_sec!(1, ms!(300));
118 /// assert_eq!(dt.to_sec64_floor(), 1);
119 /// assert_eq!(dt.to_sec_ufrac(), ms!(300) as u64);
120 /// ```
121 #[inline(always)]
122 pub const fn to_sec64_floor(&self) -> i64 {
123 Self::to_i64(self.attos.div_euclid(ATTOS_PER_SEC_I128))
124 }
125
126 /// Rounds to the nearest whole second and returns that count as [`i128`].
127 ///
128 /// Halfway cases go away from zero: 0.5 → 1 and −0.5 → −1
129 /// (same rule as [`Dt::round`]).
130 ///
131 /// ## Examples
132 ///
133 /// ```rust
134 /// use deep_time::Dt;
135 /// use deep_time::macros::{from_sec, ms};
136 ///
137 /// assert_eq!(from_sec!(1, ms!(300)).to_sec_round(), 1);
138 /// assert_eq!(from_sec!(1, ms!(600)).to_sec_round(), 2);
139 /// assert_eq!(from_sec!(-1, -ms!(300)).to_sec_round(), -1);
140 ///
141 /// // Halfway: away from zero
142 /// assert_eq!(from_sec!(0, ms!(500)).to_sec_round(), 1);
143 /// assert_eq!(from_sec!(0, -ms!(500)).to_sec_round(), -1);
144 /// ```
145 #[inline(always)]
146 pub const fn to_sec_round(&self) -> i128 {
147 self.round_to_sec().to_sec()
148 }
149
150 /// Same as [`to_sec_round`](../struct.Dt.html#method.to_sec_round),
151 /// but returns an [`i64`].
152 ///
153 /// Values outside the `i64` range clamp to [`i64::MAX`] or [`i64::MIN`].
154 ///
155 /// ## Examples
156 ///
157 /// ```rust
158 /// use deep_time::Dt;
159 /// use deep_time::macros::{from_sec, ms};
160 ///
161 /// assert_eq!(from_sec!(1, ms!(300)).to_sec64_round(), 1);
162 /// assert_eq!(from_sec!(1, ms!(600)).to_sec64_round(), 2);
163 /// assert_eq!(from_sec!(-1, -ms!(300)).to_sec64_round(), -1);
164 /// assert_eq!(from_sec!(0, ms!(500)).to_sec64_round(), 1);
165 /// ```
166 #[inline(always)]
167 pub const fn to_sec64_round(&self) -> i64 {
168 Self::to_i64(self.round_to_sec().to_sec())
169 }
170
171 /// Converts this duration to seconds as an [`f64`].
172 ///
173 /// Alias of [`to_sec_f`](../struct.Dt.html#method.to_sec_f).
174 #[inline(always)]
175 pub const fn to_f64(&self) -> f64 {
176 self.to_sec_f()
177 }
178
179 /// Converts this duration to seconds as a floating-point number.
180 ///
181 /// ## Examples
182 ///
183 /// ```rust
184 /// use deep_time::Dt;
185 /// use deep_time::macros::{from_sec, ms};
186 ///
187 /// assert_eq!(Dt::ZERO.to_sec_f(), 0.0);
188 /// assert!((from_sec!(1, ms!(500)).to_sec_f() - 1.5).abs() < 1e-12);
189 /// assert!((from_sec!(-1, -ms!(500)).to_sec_f() + 1.5).abs() < 1e-12);
190 /// ```
191 pub const fn to_sec_f(&self) -> Real {
192 if self.attos == 0 {
193 return 0.0;
194 }
195 let sec = self.attos.div_euclid(ATTOS_PER_SEC_I128);
196 let rem = self.attos.rem_euclid(ATTOS_PER_SEC_I128); // always in [0, aps)
197
198 if sec < 0 && rem > ATTOS_PER_SEC_I128 / 2 {
199 // original cancellation-avoidance path
200 let small = ATTOS_PER_SEC_I128 - rem;
201 let small_f = f!(small as u64) / ATTOS_PER_SECF;
202 f!(sec) + 1.0 - small_f
203 } else {
204 f!(sec) + f!(rem as u64) / ATTOS_PER_SECF
205 }
206 }
207
208 /// Returns the signed leftover attoseconds after removing whole seconds
209 /// toward zero.
210 ///
211 /// Same sign as the original value when non-zero: 1.3 s → `+0.3 s` in
212 /// attoseconds, −1.3 s → `−0.3 s` in attoseconds. Pairs with
213 /// [`from_sec_and_frac`](../struct.Dt.html#method.from_sec_and_frac)
214 /// and [`to_sec`](../struct.Dt.html#method.to_sec).
215 ///
216 /// For a leftover that is always ≥ 0 (paired with floor), use
217 /// [`to_sec_ufrac`](../struct.Dt.html#method.to_sec_ufrac).
218 ///
219 /// ## Examples
220 ///
221 /// ```rust
222 /// use deep_time::Dt;
223 /// use deep_time::macros::{from_sec, ms};
224 ///
225 /// let dt = from_sec!(1, ms!(300));
226 /// assert_eq!(dt.to_sec(), 1);
227 /// assert_eq!(dt.to_sec_frac(), ms!(300) as i64);
228 ///
229 /// let dt = from_sec!(-1, -ms!(300));
230 /// assert_eq!(dt.to_sec(), -1);
231 /// assert_eq!(dt.to_sec_frac(), -ms!(300) as i64);
232 /// ```
233 #[inline(always)]
234 pub const fn to_sec_frac(&self) -> i64 {
235 (self.attos % ATTOS_PER_SEC_I128) as i64
236 }
237
238 /// Returns the leftover attoseconds after
239 /// [`to_sec_floor`](../struct.Dt.html#method.to_sec_floor) /
240 /// [`to_sec64_floor`](../struct.Dt.html#method.to_sec64_floor).
241 ///
242 /// Always in `0 .. ATTOS_PER_SEC`. On negatives this is **not** “the
243 /// decimal part with a sign”: −1.3 s floors to −2 whole seconds with a
244 /// **+0.7 s** leftover.
245 ///
246 /// ## Examples
247 ///
248 /// ```rust
249 /// use deep_time::Dt;
250 /// use deep_time::macros::{from_sec, ms};
251 ///
252 /// // -1.3 s → floor -2 s + 0.7 s leftover
253 /// let dt = from_sec!(-1, -ms!(300));
254 /// assert_eq!(dt.to_sec64_floor(), -2);
255 /// assert_eq!(dt.to_sec_ufrac(), ms!(700) as u64);
256 ///
257 /// // +1.3 s → floor 1 s + 0.3 s leftover
258 /// let dt = from_sec!(1, ms!(300));
259 /// assert_eq!(dt.to_sec64_floor(), 1);
260 /// assert_eq!(dt.to_sec_ufrac(), ms!(300) as u64);
261 /// ```
262 #[inline(always)]
263 pub const fn to_sec_ufrac(&self) -> u64 {
264 self.attos.rem_euclid(ATTOS_PER_SEC_I128) as u64
265 }
266
267 /// Returns a new [`Dt`] rounded to the nearest whole second.
268 ///
269 /// Halfway cases go away from zero (same rule as [`Dt::round`]). For the
270 /// rounded value as an integer count, see
271 /// [`to_sec_round`](../struct.Dt.html#method.to_sec_round).
272 ///
273 /// ## Examples
274 ///
275 /// ```rust
276 /// use deep_time::Dt;
277 /// use deep_time::macros::{from_sec, ms, sec};
278 ///
279 /// assert_eq!(from_sec!(1, ms!(300)).round_to_sec(), from_sec!(1));
280 /// assert_eq!(from_sec!(1, ms!(600)).round_to_sec(), from_sec!(2));
281 /// assert_eq!(from_sec!(0, ms!(500)).round_to_sec(), from_sec!(1));
282 /// assert_eq!(from_sec!(0, -ms!(500)).round_to_sec(), from_sec!(-1));
283 /// ```
284 #[inline(always)]
285 pub const fn round_to_sec(&self) -> Dt {
286 self.round(dt!(ATTOS_PER_SEC_I128))
287 }
288
289 /// Returns the total time in attoseconds.
290 #[inline(always)]
291 pub const fn to_attos(&self) -> i128 {
292 self.attos
293 }
294
295 /// Splits into whole femtoseconds and leftover attoseconds, rounding the
296 /// whole part down so the leftover is always ≥ 0.
297 ///
298 /// Returns `(whole, frac_attos)`. Same floor rule as
299 /// [`to_sec_floor`](../struct.Dt.html#method.to_sec_floor).
300 /// For truncation toward zero, use [`to_fs`](../struct.Dt.html#method.to_fs).
301 #[inline(always)]
302 pub const fn to_fs_floor(&self) -> (i128, i128) {
303 (
304 self.attos.div_euclid(ATTOS_PER_FS_I128),
305 self.attos.rem_euclid(ATTOS_PER_FS_I128),
306 )
307 }
308
309 /// Splits into whole picoseconds and leftover attoseconds, rounding the
310 /// whole part down so the leftover is always ≥ 0.
311 ///
312 /// Returns `(whole, frac_attos)`. Same floor rule as
313 /// [`to_sec_floor`](../struct.Dt.html#method.to_sec_floor).
314 /// For truncation toward zero, use [`to_ps`](../struct.Dt.html#method.to_ps).
315 #[inline(always)]
316 pub const fn to_ps_floor(&self) -> (i128, i128) {
317 (
318 self.attos.div_euclid(ATTOS_PER_PS_I128),
319 self.attos.rem_euclid(ATTOS_PER_PS_I128),
320 )
321 }
322
323 /// Splits into whole nanoseconds and leftover attoseconds, rounding the
324 /// whole part down so the leftover is always ≥ 0.
325 ///
326 /// Returns `(whole, frac_attos)`. Same floor rule as
327 /// [`to_sec_floor`](../struct.Dt.html#method.to_sec_floor).
328 /// For truncation toward zero, use [`to_ns`](../struct.Dt.html#method.to_ns).
329 #[inline(always)]
330 pub const fn to_ns_floor(&self) -> (i128, i128) {
331 (
332 self.attos.div_euclid(ATTOS_PER_NS_I128),
333 self.attos.rem_euclid(ATTOS_PER_NS_I128),
334 )
335 }
336
337 /// Splits into whole microseconds and leftover attoseconds, rounding the
338 /// whole part down so the leftover is always ≥ 0.
339 ///
340 /// Returns `(whole, frac_attos)`. Same floor rule as
341 /// [`to_sec_floor`](../struct.Dt.html#method.to_sec_floor).
342 /// For truncation toward zero, use [`to_us`](../struct.Dt.html#method.to_us).
343 #[inline(always)]
344 pub const fn to_us_floor(&self) -> (i128, i128) {
345 (
346 self.attos.div_euclid(ATTOS_PER_US_I128),
347 self.attos.rem_euclid(ATTOS_PER_US_I128),
348 )
349 }
350
351 /// Splits into whole milliseconds and leftover attoseconds, rounding the
352 /// whole part down so the leftover is always ≥ 0.
353 ///
354 /// Returns `(whole, frac_attos)`. Same floor rule as
355 /// [`to_sec_floor`](../struct.Dt.html#method.to_sec_floor)
356 /// (e.g. −1.3 ms → `(-2, 0.7 ms)`). For truncation toward zero, use
357 /// [`to_ms`](../struct.Dt.html#method.to_ms).
358 ///
359 /// ## Examples
360 ///
361 /// ```rust
362 /// use deep_time::Dt;
363 /// use deep_time::macros::{from_ms, us};
364 ///
365 /// // +1.3 ms → 1 ms + 0.3 ms leftover
366 /// assert_eq!(from_ms!(1, us!(300)).to_ms_floor(), (1, us!(300)));
367 ///
368 /// // -1.3 ms → -2 ms + 0.7 ms leftover
369 /// assert_eq!(from_ms!(-1, -us!(300)).to_ms_floor(), (-2, us!(700)));
370 /// ```
371 #[inline(always)]
372 pub const fn to_ms_floor(&self) -> (i128, i128) {
373 (
374 self.attos.div_euclid(ATTOS_PER_MS_I128),
375 self.attos.rem_euclid(ATTOS_PER_MS_I128),
376 )
377 }
378
379 /// Splits into whole minutes and leftover attoseconds, rounding the whole
380 /// part down so the leftover is always ≥ 0.
381 ///
382 /// Returns `(whole, frac_attos)`. For truncation toward zero (signed
383 /// leftover), use [`to_mins`](../struct.Dt.html#method.to_mins).
384 ///
385 /// ## Examples
386 ///
387 /// ```rust
388 /// use deep_time::Dt;
389 /// use deep_time::macros::{from_sec, sec};
390 ///
391 /// assert_eq!(from_sec!(90).to_mins_floor(), (1, sec!(30)));
392 /// // -90 s → -2 min + 30 s leftover
393 /// assert_eq!(from_sec!(-90).to_mins_floor(), (-2, sec!(30)));
394 /// ```
395 #[inline(always)]
396 pub const fn to_mins_floor(&self) -> (i128, i128) {
397 (
398 self.attos.div_euclid(ATTOS_PER_MIN),
399 self.attos.rem_euclid(ATTOS_PER_MIN),
400 )
401 }
402
403 /// Splits into whole minutes and leftover attoseconds, dropping any
404 /// fraction toward zero (1.5 → 1, −1.5 → −1).
405 ///
406 /// Returns `(whole, frac_attos)`. When negative with a fraction,
407 /// `frac_attos` is negative too. For a leftover that is always ≥ 0, use
408 /// [`to_mins_floor`](../struct.Dt.html#method.to_mins_floor).
409 ///
410 /// ## Examples
411 ///
412 /// ```rust
413 /// use deep_time::Dt;
414 /// use deep_time::macros::{from_sec, sec};
415 ///
416 /// assert_eq!(from_sec!(90).to_mins(), (1, sec!(30)));
417 /// assert_eq!(from_sec!(-90).to_mins(), (-1, sec!(-30)));
418 /// ```
419 #[inline(always)]
420 pub const fn to_mins(&self) -> (i128, i128) {
421 (self.attos / ATTOS_PER_MIN, self.attos % ATTOS_PER_MIN)
422 }
423
424 /// Converts this duration to minutes as a floating-point number.
425 ///
426 /// ## Examples
427 ///
428 /// ```rust
429 /// use deep_time::Dt;
430 /// use deep_time::macros::from_sec;
431 ///
432 /// assert_eq!(Dt::ZERO.to_mins_f(), 0.0);
433 /// assert_eq!(from_sec!(90).to_mins_f(), 1.5);
434 /// assert_eq!(from_sec!(-90).to_mins_f(), -1.5);
435 /// ```
436 pub const fn to_mins_f(&self) -> Real {
437 if self.attos == 0 {
438 return 0.0;
439 }
440 let mins = self.attos.div_euclid(ATTOS_PER_MIN);
441 let rem = self.attos.rem_euclid(ATTOS_PER_MIN);
442
443 if mins < 0 && rem > ATTOS_PER_MIN / 2 {
444 let small = ATTOS_PER_MIN - rem;
445 let small_f = f!(small as u128) / f!(ATTOS_PER_MIN);
446 f!(mins) + 1.0 - small_f
447 } else {
448 f!(mins) + f!(rem as u128) / f!(ATTOS_PER_MIN)
449 }
450 }
451
452 /// Splits into whole hours and leftover attoseconds, rounding the whole
453 /// part down so the leftover is always ≥ 0.
454 ///
455 /// Returns `(whole, frac_attos)`. For truncation toward zero (signed
456 /// leftover), use [`to_hours`](../struct.Dt.html#method.to_hours).
457 ///
458 /// ## Examples
459 ///
460 /// ```rust
461 /// use deep_time::Dt;
462 /// use deep_time::macros::{from_sec, sec};
463 ///
464 /// // 90 minutes → 1 hour + 30 minutes
465 /// assert_eq!(from_sec!(90 * 60).to_hours_floor(), (1, sec!(30 * 60)));
466 /// // -90 minutes → -2 hours + 30 minutes leftover
467 /// assert_eq!(from_sec!(-90 * 60).to_hours_floor(), (-2, sec!(30 * 60)));
468 /// ```
469 #[inline(always)]
470 pub const fn to_hours_floor(&self) -> (i128, i128) {
471 (
472 self.attos.div_euclid(ATTOS_PER_HOUR),
473 self.attos.rem_euclid(ATTOS_PER_HOUR),
474 )
475 }
476
477 /// Splits into whole hours and leftover attoseconds, dropping any
478 /// fraction toward zero (1.5 → 1, −1.5 → −1).
479 ///
480 /// Returns `(whole, frac_attos)`. When negative with a fraction,
481 /// `frac_attos` is negative too. For a leftover that is always ≥ 0, use
482 /// [`to_hours_floor`](../struct.Dt.html#method.to_hours_floor).
483 ///
484 /// ## Examples
485 ///
486 /// ```rust
487 /// use deep_time::Dt;
488 /// use deep_time::macros::{from_sec, sec};
489 ///
490 /// assert_eq!(from_sec!(90 * 60).to_hours(), (1, sec!(30 * 60)));
491 /// assert_eq!(from_sec!(-90 * 60).to_hours(), (-1, sec!(-30 * 60)));
492 /// ```
493 #[inline(always)]
494 pub const fn to_hours(&self) -> (i128, i128) {
495 (self.attos / ATTOS_PER_HOUR, self.attos % ATTOS_PER_HOUR)
496 }
497
498 /// Converts this duration to hours as a floating-point number.
499 ///
500 /// ## Examples
501 ///
502 /// ```rust
503 /// use deep_time::Dt;
504 /// use deep_time::macros::from_sec;
505 ///
506 /// assert_eq!(Dt::ZERO.to_hours_f(), 0.0);
507 /// assert_eq!(from_sec!(90 * 60).to_hours_f(), 1.5);
508 /// assert_eq!(from_sec!(-90 * 60).to_hours_f(), -1.5);
509 /// ```
510 pub const fn to_hours_f(&self) -> Real {
511 if self.attos == 0 {
512 return 0.0;
513 }
514 let hours = self.attos.div_euclid(ATTOS_PER_HOUR);
515 let rem = self.attos.rem_euclid(ATTOS_PER_HOUR);
516
517 if hours < 0 && rem > ATTOS_PER_HOUR / 2 {
518 let small = ATTOS_PER_HOUR - rem;
519 let small_f = f!(small as u128) / f!(ATTOS_PER_HOUR);
520 f!(hours) + 1.0 - small_f
521 } else {
522 f!(hours) + f!(rem as u128) / f!(ATTOS_PER_HOUR)
523 }
524 }
525
526 /// Splits into whole days and leftover attoseconds, dropping any fraction
527 /// toward zero (1.25 → 1, −1.25 → −1).
528 ///
529 /// Returns `(whole, frac_attos)`. When negative with a fraction,
530 /// `frac_attos` is negative too — e.g. −1.25 days is
531 /// `(-1, -ATTOS_PER_DAY / 4)`.
532 ///
533 /// For a leftover that is always ≥ 0, use
534 /// [`to_days_floor`](../struct.Dt.html#method.to_days_floor).
535 ///
536 /// ## Examples
537 ///
538 /// ```rust
539 /// use deep_time::{Dt, Scale, consts::ATTOS_PER_DAY};
540 ///
541 /// let dt = Dt::from_days_f(1.25, Scale::TAI, Scale::TAI);
542 /// assert_eq!(dt.to_days(), (1, ATTOS_PER_DAY / 4));
543 ///
544 /// let dt = Dt::from_days_f(-1.25, Scale::TAI, Scale::TAI);
545 /// assert_eq!(dt.to_days(), (-1, -ATTOS_PER_DAY / 4));
546 /// ```
547 ///
548 /// ## See also
549 ///
550 /// - [`Dt::to_days_floor`](../struct.Dt.html#method.to_days_floor)
551 /// - [`Dt::from_days`](../struct.Dt.html#method.from_days)
552 #[inline(always)]
553 pub const fn to_days(&self) -> (i128, i128) {
554 (self.attos / ATTOS_PER_DAY, self.attos % ATTOS_PER_DAY)
555 }
556
557 /// Splits into whole days and leftover attoseconds, rounding the whole
558 /// part down so the leftover is always ≥ 0.
559 ///
560 /// Returns `(whole, frac_attos)`. Same floor rule as
561 /// [`to_sec_floor`](../struct.Dt.html#method.to_sec_floor) (e.g. −1.5 days →
562 /// `(-2, half a day)`). For truncation toward zero, use
563 /// [`to_days`](../struct.Dt.html#method.to_days).
564 ///
565 /// ## Examples
566 ///
567 /// ```rust
568 /// use deep_time::{Dt, Scale, consts::ATTOS_PER_DAY};
569 ///
570 /// let dt = Dt::from_days_f(1.25, Scale::TAI, Scale::TAI);
571 /// assert_eq!(dt.to_days_floor(), (1, ATTOS_PER_DAY / 4));
572 ///
573 /// // -1.25 days → -2 whole days + 0.75 day leftover
574 /// let dt = Dt::from_days_f(-1.25, Scale::TAI, Scale::TAI);
575 /// assert_eq!(dt.to_days_floor(), (-2, (ATTOS_PER_DAY * 3) / 4));
576 /// ```
577 ///
578 /// ## See also
579 ///
580 /// - [`Dt::to_days`](../struct.Dt.html#method.to_days)
581 #[inline(always)]
582 pub const fn to_days_floor(&self) -> (i128, i128) {
583 (
584 self.attos.div_euclid(ATTOS_PER_DAY),
585 self.attos.rem_euclid(ATTOS_PER_DAY),
586 )
587 }
588
589 /// Converts this duration to days as a floating-point number.
590 ///
591 /// ## Examples
592 ///
593 /// ```rust
594 /// use deep_time::{Dt, Scale};
595 ///
596 /// assert_eq!(Dt::ZERO.to_days_f(), 0.0);
597 /// assert_eq!(Dt::from_days_f(1.5, Scale::TAI, Scale::TAI).to_days_f(), 1.5);
598 /// assert_eq!(Dt::from_days_f(-1.5, Scale::TAI, Scale::TAI).to_days_f(), -1.5);
599 /// ```
600 ///
601 /// ## See also
602 ///
603 /// - [`Dt::from_days_f`](../struct.Dt.html#method.from_days_f)
604 pub const fn to_days_f(&self) -> Real {
605 if self.attos == 0 {
606 return 0.0;
607 }
608 let days = self.attos.div_euclid(ATTOS_PER_DAY);
609 let rem = self.attos.rem_euclid(ATTOS_PER_DAY);
610
611 if days < 0 && rem > ATTOS_PER_DAY / 2 {
612 let small = ATTOS_PER_DAY - rem;
613 let small_f = f!(small as u128) / f!(ATTOS_PER_DAY);
614 f!(days) + 1.0 - small_f
615 } else {
616 f!(days) + f!(rem as u128) / f!(ATTOS_PER_DAY)
617 }
618 }
619
620 /// Splits into whole milliseconds and leftover attoseconds, dropping any
621 /// fraction toward zero (1.7 → 1, −1.7 → −1).
622 ///
623 /// Returns `(whole, frac_attos)`. When negative with a fraction,
624 /// `frac_attos` is negative too. For a leftover that is always ≥ 0, use
625 /// [`to_ms_floor`](../struct.Dt.html#method.to_ms_floor).
626 ///
627 /// ## Examples
628 ///
629 /// ```rust
630 /// use deep_time::Dt;
631 /// use deep_time::macros::{from_ms, us};
632 ///
633 /// assert_eq!(from_ms!(1, us!(300)).to_ms(), (1, us!(300)));
634 /// assert_eq!(from_ms!(-1, -us!(300)).to_ms(), (-1, -us!(300)));
635 /// ```
636 #[inline(always)]
637 pub const fn to_ms(&self) -> (i128, i128) {
638 (
639 self.attos / ATTOS_PER_MS_I128,
640 self.attos % ATTOS_PER_MS_I128,
641 )
642 }
643
644 /// Splits into whole microseconds and leftover attoseconds, dropping any
645 /// fraction toward zero (1.7 → 1, −1.7 → −1).
646 ///
647 /// Returns `(whole, frac_attos)`. When negative with a fraction,
648 /// `frac_attos` is negative too. For a leftover that is always ≥ 0, use
649 /// [`to_us_floor`](../struct.Dt.html#method.to_us_floor).
650 #[inline(always)]
651 pub const fn to_us(&self) -> (i128, i128) {
652 (
653 self.attos / ATTOS_PER_US_I128,
654 self.attos % ATTOS_PER_US_I128,
655 )
656 }
657
658 /// Splits into whole nanoseconds and leftover attoseconds, dropping any
659 /// fraction toward zero (1.7 → 1, −1.7 → −1).
660 ///
661 /// Returns `(whole, frac_attos)`. When negative with a fraction,
662 /// `frac_attos` is negative too. For a leftover that is always ≥ 0, use
663 /// [`to_ns_floor`](../struct.Dt.html#method.to_ns_floor).
664 #[inline(always)]
665 pub const fn to_ns(&self) -> (i128, i128) {
666 (
667 self.attos / ATTOS_PER_NS_I128,
668 self.attos % ATTOS_PER_NS_I128,
669 )
670 }
671
672 /// Splits into whole picoseconds and leftover attoseconds, dropping any
673 /// fraction toward zero (1.7 → 1, −1.7 → −1).
674 ///
675 /// Returns `(whole, frac_attos)`. When negative with a fraction,
676 /// `frac_attos` is negative too. For a leftover that is always ≥ 0, use
677 /// [`to_ps_floor`](../struct.Dt.html#method.to_ps_floor).
678 #[inline(always)]
679 pub const fn to_ps(&self) -> (i128, i128) {
680 (
681 self.attos / ATTOS_PER_PS_I128,
682 self.attos % ATTOS_PER_PS_I128,
683 )
684 }
685
686 /// Splits into whole femtoseconds and leftover attoseconds, dropping any
687 /// fraction toward zero (1.7 → 1, −1.7 → −1).
688 ///
689 /// Returns `(whole, frac_attos)`. When negative with a fraction,
690 /// `frac_attos` is negative too. For a leftover that is always ≥ 0, use
691 /// [`to_fs_floor`](../struct.Dt.html#method.to_fs_floor).
692 #[inline(always)]
693 pub const fn to_fs(&self) -> (i128, i128) {
694 (
695 self.attos / ATTOS_PER_FS_I128,
696 self.attos % ATTOS_PER_FS_I128,
697 )
698 }
699}