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 ATTOS_PER_WEEK, Dt, Real, dt,
5};
6
7impl Dt {
8 /// Converts total attoseconds to a floating-point count of
9 /// `unit_attos`-sized units.
10 ///
11 /// Euclidean split keeps the remainder in `[0, unit_attos)`. For large
12 /// negative whole parts with a large remainder, rewrites as
13 /// `whole + 1 − small_frac` to avoid cancellation in the float add.
14 #[inline(always)]
15 const fn attos_to_unit_f(attos: i128, unit_attos: i128) -> Real {
16 if attos == 0 {
17 return 0.0;
18 }
19 let whole = attos.div_euclid(unit_attos);
20 let rem = attos.rem_euclid(unit_attos);
21
22 if whole < 0 && rem > unit_attos / 2 {
23 let small = unit_attos - rem;
24 let small_f = f!(small as u128) / f!(unit_attos);
25 f!(whole) + 1.0 - small_f
26 } else {
27 f!(whole) + f!(rem as u128) / f!(unit_attos)
28 }
29 }
30
31 /// Returns this duration as a whole number of seconds, dropping any
32 /// fraction (always toward zero: 1.7 → 1, −1.7 → −1).
33 ///
34 /// Same as `self.attos / ATTOS_PER_SEC_I128`. Does not round.
35 ///
36 /// On positive values this matches
37 /// [`to_sec_floor`](../struct.Dt.html#method.to_sec_floor).
38 /// On negatives they diverge: here −1.3 → −1, while `to_sec_floor` gives
39 /// −2 (always rounds down). Prefer `to_sec_floor` when you also need a
40 /// non-negative fractional part via
41 /// [`to_sec_ufrac`](../struct.Dt.html#method.to_sec_ufrac).
42 ///
43 /// ## Examples
44 ///
45 /// ```rust
46 /// use deep_time::Dt;
47 /// use deep_time::macros::{from_sec, ms};
48 ///
49 /// // Fraction only — less than one second either way → 0
50 /// assert_eq!(from_sec!(0, -ms!(300)).to_sec(), 0);
51 /// assert_eq!(from_sec!(0, ms!(300)).to_sec(), 0);
52 ///
53 /// // -1.3 s → -1 here, but -2 with `to_sec_floor`
54 /// let dt = from_sec!(-1, -ms!(300));
55 /// assert_eq!(dt.to_sec(), -1);
56 /// assert_eq!(dt.to_sec_floor(), -2);
57 ///
58 /// // Positive values match `to_sec_floor`
59 /// let dt = from_sec!(1, ms!(300));
60 /// assert_eq!(dt.to_sec(), 1);
61 /// assert_eq!(dt.to_sec_floor(), 1);
62 /// ```
63 #[inline(always)]
64 pub const fn to_sec(&self) -> i128 {
65 self.attos / ATTOS_PER_SEC_I128
66 }
67
68 /// Same as [`to_sec`](../struct.Dt.html#method.to_sec), but returns an
69 /// [`i64`].
70 ///
71 /// Values outside the `i64` range clamp to [`i64::MAX`] or [`i64::MIN`].
72 ///
73 /// ## Examples
74 ///
75 /// ```rust
76 /// use deep_time::Dt;
77 /// use deep_time::macros::{from_sec, ms};
78 ///
79 /// // Same toward-zero rule as `to_sec`
80 /// assert_eq!(from_sec!(1, ms!(300)).to_sec64(), 1);
81 /// assert_eq!(from_sec!(-1, -ms!(300)).to_sec64(), -1);
82 ///
83 /// // Fits in i64 here; huge values would clamp instead of panicking
84 /// assert_eq!(from_sec!(i64::MAX as i128).to_sec64(), i64::MAX);
85 /// ```
86 #[inline(always)]
87 pub const fn to_sec64(&self) -> i64 {
88 Self::to_i64(self.attos / ATTOS_PER_SEC_I128)
89 }
90
91 /// Returns whole seconds, always rounding down (toward −∞).
92 ///
93 /// So 1.3 → 1 and −1.3 → −2. The leftover attoseconds are then always
94 /// ≥ 0; get them with
95 /// [`to_sec_ufrac`](../struct.Dt.html#method.to_sec_ufrac).
96 ///
97 /// For truncation toward zero (1.3 → 1, −1.3 → −1), use
98 /// [`to_sec`](../struct.Dt.html#method.to_sec).
99 /// For nearest-second rounding, use
100 /// [`to_sec_round`](../struct.Dt.html#method.to_sec_round).
101 ///
102 /// ## Examples
103 ///
104 /// ```rust
105 /// use deep_time::Dt;
106 /// use deep_time::macros::{from_sec, ms};
107 ///
108 /// // -1.3 s → whole -2, leftover +0.7 s
109 /// let dt = from_sec!(-1, -ms!(300));
110 /// assert_eq!(dt.to_sec_floor(), -2);
111 /// assert_eq!(dt.to_sec_ufrac(), ms!(700) as u64);
112 ///
113 /// // +1.3 s → whole 1, leftover +0.3 s
114 /// let dt = from_sec!(1, ms!(300));
115 /// assert_eq!(dt.to_sec_floor(), 1);
116 /// assert_eq!(dt.to_sec_ufrac(), ms!(300) as u64);
117 /// ```
118 #[inline(always)]
119 pub const fn to_sec_floor(&self) -> i128 {
120 self.attos.div_euclid(ATTOS_PER_SEC_I128)
121 }
122
123 /// Same as [`to_sec_floor`](../struct.Dt.html#method.to_sec_floor),
124 /// but returns an [`i64`].
125 ///
126 /// Values outside the `i64` range clamp to [`i64::MAX`] or [`i64::MIN`].
127 /// Pair with [`to_sec_ufrac`](../struct.Dt.html#method.to_sec_ufrac)
128 /// for the leftover.
129 ///
130 /// ## Examples
131 ///
132 /// ```rust
133 /// use deep_time::Dt;
134 /// use deep_time::macros::{from_sec, ms};
135 ///
136 /// let dt = from_sec!(-1, -ms!(300));
137 /// assert_eq!(dt.to_sec64_floor(), -2);
138 /// assert_eq!(dt.to_sec_ufrac(), ms!(700) as u64);
139 ///
140 /// let dt = from_sec!(1, ms!(300));
141 /// assert_eq!(dt.to_sec64_floor(), 1);
142 /// assert_eq!(dt.to_sec_ufrac(), ms!(300) as u64);
143 /// ```
144 #[inline(always)]
145 pub const fn to_sec64_floor(&self) -> i64 {
146 Self::to_i64(self.attos.div_euclid(ATTOS_PER_SEC_I128))
147 }
148
149 /// Rounds to the nearest whole second and returns that count as [`i128`].
150 ///
151 /// Halfway cases go away from zero: 0.5 → 1 and −0.5 → −1
152 /// (same rule as [`Dt::round`]).
153 ///
154 /// ## Examples
155 ///
156 /// ```rust
157 /// use deep_time::Dt;
158 /// use deep_time::macros::{from_sec, ms};
159 ///
160 /// assert_eq!(from_sec!(1, ms!(300)).to_sec_round(), 1);
161 /// assert_eq!(from_sec!(1, ms!(600)).to_sec_round(), 2);
162 /// assert_eq!(from_sec!(-1, -ms!(300)).to_sec_round(), -1);
163 ///
164 /// // Halfway: away from zero
165 /// assert_eq!(from_sec!(0, ms!(500)).to_sec_round(), 1);
166 /// assert_eq!(from_sec!(0, -ms!(500)).to_sec_round(), -1);
167 /// ```
168 #[inline(always)]
169 pub const fn to_sec_round(&self) -> i128 {
170 self.round_to_sec().to_sec()
171 }
172
173 /// Same as [`to_sec_round`](../struct.Dt.html#method.to_sec_round),
174 /// but returns an [`i64`].
175 ///
176 /// Values outside the `i64` range clamp to [`i64::MAX`] or [`i64::MIN`].
177 ///
178 /// ## Examples
179 ///
180 /// ```rust
181 /// use deep_time::Dt;
182 /// use deep_time::macros::{from_sec, ms};
183 ///
184 /// assert_eq!(from_sec!(1, ms!(300)).to_sec64_round(), 1);
185 /// assert_eq!(from_sec!(1, ms!(600)).to_sec64_round(), 2);
186 /// assert_eq!(from_sec!(-1, -ms!(300)).to_sec64_round(), -1);
187 /// assert_eq!(from_sec!(0, ms!(500)).to_sec64_round(), 1);
188 /// ```
189 #[inline(always)]
190 pub const fn to_sec64_round(&self) -> i64 {
191 Self::to_i64(self.round_to_sec().to_sec())
192 }
193
194 /// Converts this duration to seconds as an [`f64`].
195 ///
196 /// Alias of [`to_sec_f`](../struct.Dt.html#method.to_sec_f).
197 ///
198 /// ## Examples
199 ///
200 /// ```rust
201 /// use deep_time::Dt;
202 /// use deep_time::macros::{from_sec, ms};
203 ///
204 /// assert_eq!(Dt::ZERO.to_f64(), 0.0);
205 /// assert!((from_sec!(1, ms!(500)).to_f64() - 1.5).abs() < 1e-12);
206 /// ```
207 #[inline(always)]
208 pub const fn to_f64(&self) -> f64 {
209 self.to_sec_f()
210 }
211
212 /// Converts this duration to seconds as a floating-point number.
213 ///
214 /// ## Examples
215 ///
216 /// ```rust
217 /// use deep_time::Dt;
218 /// use deep_time::macros::{from_sec, ms};
219 ///
220 /// assert_eq!(Dt::ZERO.to_sec_f(), 0.0);
221 /// assert!((from_sec!(1, ms!(500)).to_sec_f() - 1.5).abs() < 1e-12);
222 /// assert!((from_sec!(-1, -ms!(500)).to_sec_f() + 1.5).abs() < 1e-12);
223 /// ```
224 pub const fn to_sec_f(&self) -> Real {
225 if self.attos == 0 {
226 return 0.0;
227 }
228 let sec = self.attos.div_euclid(ATTOS_PER_SEC_I128);
229 let rem = self.attos.rem_euclid(ATTOS_PER_SEC_I128); // always in [0, aps)
230
231 if sec < 0 && rem > ATTOS_PER_SEC_I128 / 2 {
232 // original cancellation-avoidance path
233 let small = ATTOS_PER_SEC_I128 - rem;
234 let small_f = f!(small as u64) / ATTOS_PER_SECF;
235 f!(sec) + 1.0 - small_f
236 } else {
237 f!(sec) + f!(rem as u64) / ATTOS_PER_SECF
238 }
239 }
240
241 /// Returns the signed leftover attoseconds after removing whole seconds
242 /// toward zero.
243 ///
244 /// Same sign as the original value when non-zero: 1.3 s → `+0.3 s` in
245 /// attoseconds, −1.3 s → `−0.3 s` in attoseconds. Pairs with
246 /// [`from_sec_and_frac`](../struct.Dt.html#method.from_sec_and_frac)
247 /// and [`to_sec`](../struct.Dt.html#method.to_sec).
248 ///
249 /// For a leftover that is always ≥ 0 (paired with floor), use
250 /// [`to_sec_ufrac`](../struct.Dt.html#method.to_sec_ufrac).
251 ///
252 /// ## Examples
253 ///
254 /// ```rust
255 /// use deep_time::Dt;
256 /// use deep_time::macros::{from_sec, ms};
257 ///
258 /// let dt = from_sec!(1, ms!(300));
259 /// assert_eq!(dt.to_sec(), 1);
260 /// assert_eq!(dt.to_sec_frac(), ms!(300) as i64);
261 ///
262 /// let dt = from_sec!(-1, -ms!(300));
263 /// assert_eq!(dt.to_sec(), -1);
264 /// assert_eq!(dt.to_sec_frac(), -ms!(300) as i64);
265 /// ```
266 #[inline(always)]
267 pub const fn to_sec_frac(&self) -> i64 {
268 (self.attos % ATTOS_PER_SEC_I128) as i64
269 }
270
271 /// Returns the leftover attoseconds after
272 /// [`to_sec_floor`](../struct.Dt.html#method.to_sec_floor) /
273 /// [`to_sec64_floor`](../struct.Dt.html#method.to_sec64_floor).
274 ///
275 /// Always in `0 .. ATTOS_PER_SEC`. On negatives this is **not** “the
276 /// decimal part with a sign”: −1.3 s floors to −2 whole seconds with a
277 /// **+0.7 s** leftover.
278 ///
279 /// ## Examples
280 ///
281 /// ```rust
282 /// use deep_time::Dt;
283 /// use deep_time::macros::{from_sec, ms};
284 ///
285 /// // -1.3 s → floor -2 s + 0.7 s leftover
286 /// let dt = from_sec!(-1, -ms!(300));
287 /// assert_eq!(dt.to_sec64_floor(), -2);
288 /// assert_eq!(dt.to_sec_ufrac(), ms!(700) as u64);
289 ///
290 /// // +1.3 s → floor 1 s + 0.3 s leftover
291 /// let dt = from_sec!(1, ms!(300));
292 /// assert_eq!(dt.to_sec64_floor(), 1);
293 /// assert_eq!(dt.to_sec_ufrac(), ms!(300) as u64);
294 /// ```
295 #[inline(always)]
296 pub const fn to_sec_ufrac(&self) -> u64 {
297 self.attos.rem_euclid(ATTOS_PER_SEC_I128) as u64
298 }
299
300 /// Returns a new [`Dt`] rounded to the nearest whole second.
301 ///
302 /// Halfway cases go away from zero (same rule as [`Dt::round`]). For the
303 /// rounded value as an integer count, see
304 /// [`to_sec_round`](../struct.Dt.html#method.to_sec_round).
305 ///
306 /// ## Examples
307 ///
308 /// ```rust
309 /// use deep_time::Dt;
310 /// use deep_time::macros::{from_sec, ms};
311 ///
312 /// assert_eq!(from_sec!(1, ms!(300)).round_to_sec(), from_sec!(1));
313 /// assert_eq!(from_sec!(1, ms!(600)).round_to_sec(), from_sec!(2));
314 /// assert_eq!(from_sec!(0, ms!(500)).round_to_sec(), from_sec!(1));
315 /// assert_eq!(from_sec!(0, -ms!(500)).round_to_sec(), from_sec!(-1));
316 /// ```
317 #[inline(always)]
318 pub const fn round_to_sec(&self) -> Dt {
319 self.round(dt!(ATTOS_PER_SEC_I128))
320 }
321
322 /// Returns the total time in attoseconds.
323 ///
324 /// ## Examples
325 ///
326 /// ```rust
327 /// use deep_time::Dt;
328 /// use deep_time::macros::{from_sec, ms, sec};
329 ///
330 /// assert_eq!(Dt::ZERO.to_attos(), 0);
331 /// assert_eq!(from_sec!(1, ms!(300)).to_attos(), sec!(1) + ms!(300));
332 /// ```
333 #[inline(always)]
334 pub const fn to_attos(&self) -> i128 {
335 self.attos
336 }
337
338 /// Splits into whole femtoseconds and leftover attoseconds, rounding the
339 /// whole part down so the leftover is always ≥ 0.
340 ///
341 /// Returns `(whole, frac_attos)`. Same floor rule as
342 /// [`to_sec_floor`](../struct.Dt.html#method.to_sec_floor).
343 /// For truncation toward zero, use [`to_fs`](../struct.Dt.html#method.to_fs).
344 ///
345 /// ## Examples
346 ///
347 /// ```rust
348 /// use deep_time::Dt;
349 /// use deep_time::macros::from_fs;
350 ///
351 /// // +1.5 fs → 1 fs + 500 attoseconds leftover
352 /// assert_eq!(from_fs!(1, 500).to_fs_floor(), (1, 500));
353 /// // -1.5 fs → -2 fs + 500 attoseconds leftover
354 /// assert_eq!(from_fs!(-1, -500).to_fs_floor(), (-2, 500));
355 /// ```
356 #[inline(always)]
357 pub const fn to_fs_floor(&self) -> (i128, i128) {
358 (
359 self.attos.div_euclid(ATTOS_PER_FS_I128),
360 self.attos.rem_euclid(ATTOS_PER_FS_I128),
361 )
362 }
363
364 /// Splits into whole picoseconds and leftover attoseconds, rounding the
365 /// whole part down so the leftover is always ≥ 0.
366 ///
367 /// Returns `(whole, frac_attos)`. Same floor rule as
368 /// [`to_sec_floor`](../struct.Dt.html#method.to_sec_floor).
369 /// For truncation toward zero, use [`to_ps`](../struct.Dt.html#method.to_ps).
370 ///
371 /// ## Examples
372 ///
373 /// ```rust
374 /// use deep_time::Dt;
375 /// use deep_time::macros::{from_ps, fs};
376 ///
377 /// // +1.3 ps → 1 ps + 0.3 ps leftover
378 /// assert_eq!(from_ps!(1, fs!(300)).to_ps_floor(), (1, fs!(300)));
379 /// // -1.3 ps → -2 ps + 0.7 ps leftover
380 /// assert_eq!(from_ps!(-1, -fs!(300)).to_ps_floor(), (-2, fs!(700)));
381 /// ```
382 #[inline(always)]
383 pub const fn to_ps_floor(&self) -> (i128, i128) {
384 (
385 self.attos.div_euclid(ATTOS_PER_PS_I128),
386 self.attos.rem_euclid(ATTOS_PER_PS_I128),
387 )
388 }
389
390 /// Splits into whole nanoseconds and leftover attoseconds, rounding the
391 /// whole part down so the leftover is always ≥ 0.
392 ///
393 /// Returns `(whole, frac_attos)`. Same floor rule as
394 /// [`to_sec_floor`](../struct.Dt.html#method.to_sec_floor).
395 /// For truncation toward zero, use [`to_ns`](../struct.Dt.html#method.to_ns).
396 ///
397 /// ## Examples
398 ///
399 /// ```rust
400 /// use deep_time::Dt;
401 /// use deep_time::macros::{from_ns, ps};
402 ///
403 /// // +1.3 ns → 1 ns + 0.3 ns leftover
404 /// assert_eq!(from_ns!(1, ps!(300)).to_ns_floor(), (1, ps!(300)));
405 /// // -1.3 ns → -2 ns + 0.7 ns leftover
406 /// assert_eq!(from_ns!(-1, -ps!(300)).to_ns_floor(), (-2, ps!(700)));
407 /// ```
408 #[inline(always)]
409 pub const fn to_ns_floor(&self) -> (i128, i128) {
410 (
411 self.attos.div_euclid(ATTOS_PER_NS_I128),
412 self.attos.rem_euclid(ATTOS_PER_NS_I128),
413 )
414 }
415
416 /// Splits into whole microseconds and leftover attoseconds, rounding the
417 /// whole part down so the leftover is always ≥ 0.
418 ///
419 /// Returns `(whole, frac_attos)`. Same floor rule as
420 /// [`to_sec_floor`](../struct.Dt.html#method.to_sec_floor).
421 /// For truncation toward zero, use [`to_us`](../struct.Dt.html#method.to_us).
422 ///
423 /// ## Examples
424 ///
425 /// ```rust
426 /// use deep_time::Dt;
427 /// use deep_time::macros::{from_us, ns};
428 ///
429 /// // +1.3 µs → 1 µs + 0.3 µs leftover
430 /// assert_eq!(from_us!(1, ns!(300)).to_us_floor(), (1, ns!(300)));
431 /// // -1.3 µs → -2 µs + 0.7 µs leftover
432 /// assert_eq!(from_us!(-1, -ns!(300)).to_us_floor(), (-2, ns!(700)));
433 /// ```
434 #[inline(always)]
435 pub const fn to_us_floor(&self) -> (i128, i128) {
436 (
437 self.attos.div_euclid(ATTOS_PER_US_I128),
438 self.attos.rem_euclid(ATTOS_PER_US_I128),
439 )
440 }
441
442 /// Splits into whole milliseconds and leftover attoseconds, rounding the
443 /// whole part down so the leftover is always ≥ 0.
444 ///
445 /// Returns `(whole, frac_attos)`. Same floor rule as
446 /// [`to_sec_floor`](../struct.Dt.html#method.to_sec_floor)
447 /// (e.g. −1.3 ms → `(-2, 700 µs in attoseconds)`). For truncation toward
448 /// zero, use [`to_ms`](../struct.Dt.html#method.to_ms).
449 ///
450 /// ## Examples
451 ///
452 /// ```rust
453 /// use deep_time::Dt;
454 /// use deep_time::macros::{from_ms, us};
455 ///
456 /// // +1.3 ms → 1 ms + 0.3 ms leftover
457 /// assert_eq!(from_ms!(1, us!(300)).to_ms_floor(), (1, us!(300)));
458 ///
459 /// // -1.3 ms → -2 ms + 0.7 ms leftover
460 /// assert_eq!(from_ms!(-1, -us!(300)).to_ms_floor(), (-2, us!(700)));
461 /// ```
462 #[inline(always)]
463 pub const fn to_ms_floor(&self) -> (i128, i128) {
464 (
465 self.attos.div_euclid(ATTOS_PER_MS_I128),
466 self.attos.rem_euclid(ATTOS_PER_MS_I128),
467 )
468 }
469
470 /// Splits into whole minutes and leftover attoseconds, rounding the whole
471 /// part down so the leftover is always ≥ 0.
472 ///
473 /// Returns `(whole, frac_attos)`. For truncation toward zero (signed
474 /// leftover), use [`to_mins`](../struct.Dt.html#method.to_mins).
475 ///
476 /// ## Examples
477 ///
478 /// ```rust
479 /// use deep_time::Dt;
480 /// use deep_time::macros::{from_sec, sec};
481 ///
482 /// // 90 s → 1 min + 30 s leftover
483 /// assert_eq!(from_sec!(90).to_mins_floor(), (1, sec!(30)));
484 /// // -90 s → -2 min + 30 s leftover
485 /// assert_eq!(from_sec!(-90).to_mins_floor(), (-2, sec!(30)));
486 /// ```
487 #[inline(always)]
488 pub const fn to_mins_floor(&self) -> (i128, i128) {
489 (
490 self.attos.div_euclid(ATTOS_PER_MIN),
491 self.attos.rem_euclid(ATTOS_PER_MIN),
492 )
493 }
494
495 /// Splits into whole minutes and leftover attoseconds, dropping any
496 /// fraction toward zero (1.5 → 1, −1.5 → −1).
497 ///
498 /// Returns `(whole, frac_attos)`. When negative with a fraction,
499 /// `frac_attos` is negative too. For a leftover that is always ≥ 0, use
500 /// [`to_mins_floor`](../struct.Dt.html#method.to_mins_floor).
501 ///
502 /// ## Examples
503 ///
504 /// ```rust
505 /// use deep_time::Dt;
506 /// use deep_time::macros::{from_sec, sec};
507 ///
508 /// // 90 s → 1 min + 30 s leftover (signed on negatives)
509 /// assert_eq!(from_sec!(90).to_mins(), (1, sec!(30)));
510 /// assert_eq!(from_sec!(-90).to_mins(), (-1, sec!(-30)));
511 /// ```
512 #[inline(always)]
513 pub const fn to_mins(&self) -> (i128, i128) {
514 (self.attos / ATTOS_PER_MIN, self.attos % ATTOS_PER_MIN)
515 }
516
517 /// Converts this duration to minutes as a floating-point number.
518 ///
519 /// ## Examples
520 ///
521 /// ```rust
522 /// use deep_time::Dt;
523 /// use deep_time::macros::from_sec;
524 ///
525 /// assert_eq!(Dt::ZERO.to_mins_f(), 0.0);
526 /// assert_eq!(from_sec!(90).to_mins_f(), 1.5);
527 /// assert_eq!(from_sec!(-90).to_mins_f(), -1.5);
528 /// ```
529 #[inline(always)]
530 pub const fn to_mins_f(&self) -> Real {
531 Self::attos_to_unit_f(self.attos, ATTOS_PER_MIN)
532 }
533
534 /// Splits into whole hours and leftover attoseconds, rounding the whole
535 /// part down so the leftover is always ≥ 0.
536 ///
537 /// Returns `(whole, frac_attos)`. For truncation toward zero (signed
538 /// leftover), use [`to_hours`](../struct.Dt.html#method.to_hours).
539 ///
540 /// ## Examples
541 ///
542 /// ```rust
543 /// use deep_time::Dt;
544 /// use deep_time::macros::{dt, mins};
545 ///
546 /// // 90 minutes → 1 hour + 30 minutes leftover
547 /// assert_eq!(dt!(mins!(90)).to_hours_floor(), (1, mins!(30)));
548 /// // -90 minutes → -2 hours + 30 minutes leftover
549 /// assert_eq!(dt!(mins!(-90)).to_hours_floor(), (-2, mins!(30)));
550 /// ```
551 #[inline(always)]
552 pub const fn to_hours_floor(&self) -> (i128, i128) {
553 (
554 self.attos.div_euclid(ATTOS_PER_HOUR),
555 self.attos.rem_euclid(ATTOS_PER_HOUR),
556 )
557 }
558
559 /// Splits into whole hours and leftover attoseconds, dropping any
560 /// fraction toward zero (1.5 → 1, −1.5 → −1).
561 ///
562 /// Returns `(whole, frac_attos)`. When negative with a fraction,
563 /// `frac_attos` is negative too. For a leftover that is always ≥ 0, use
564 /// [`to_hours_floor`](../struct.Dt.html#method.to_hours_floor).
565 ///
566 /// ## Examples
567 ///
568 /// ```rust
569 /// use deep_time::Dt;
570 /// use deep_time::macros::{dt, mins};
571 ///
572 /// assert_eq!(dt!(mins!(90)).to_hours(), (1, mins!(30)));
573 /// assert_eq!(dt!(mins!(-90)).to_hours(), (-1, -mins!(30)));
574 /// ```
575 #[inline(always)]
576 pub const fn to_hours(&self) -> (i128, i128) {
577 (self.attos / ATTOS_PER_HOUR, self.attos % ATTOS_PER_HOUR)
578 }
579
580 /// Converts this duration to hours as a floating-point number.
581 ///
582 /// ## Examples
583 ///
584 /// ```rust
585 /// use deep_time::Dt;
586 /// use deep_time::macros::{dt, mins};
587 ///
588 /// assert_eq!(Dt::ZERO.to_hours_f(), 0.0);
589 /// assert_eq!(dt!(mins!(90)).to_hours_f(), 1.5);
590 /// assert_eq!(dt!(mins!(-90)).to_hours_f(), -1.5);
591 /// ```
592 #[inline(always)]
593 pub const fn to_hours_f(&self) -> Real {
594 Self::attos_to_unit_f(self.attos, ATTOS_PER_HOUR)
595 }
596
597 /// Splits into whole days and leftover attoseconds, dropping any fraction
598 /// toward zero (1.25 → 1, −1.25 → −1).
599 ///
600 /// Returns `(whole, frac_attos)`. When negative with a fraction,
601 /// `frac_attos` is negative too — e.g. −1.25 days is
602 /// `(-1, −6 hours in attoseconds)`.
603 ///
604 /// For a leftover that is always ≥ 0, use
605 /// [`to_days_floor`](../struct.Dt.html#method.to_days_floor).
606 ///
607 /// ## Examples
608 ///
609 /// ```rust
610 /// use deep_time::Dt;
611 /// use deep_time::macros::{from_days_f, hours};
612 ///
613 /// // 1.25 d → 1 day + 6 hours leftover
614 /// assert_eq!(from_days_f!(1.25).to_days(), (1, hours!(6)));
615 ///
616 /// // -1.25 d → -1 day with signed -6 hours leftover
617 /// assert_eq!(from_days_f!(-1.25).to_days(), (-1, -hours!(6)));
618 /// ```
619 ///
620 /// ## See also
621 ///
622 /// - [`Dt::to_days_floor`](../struct.Dt.html#method.to_days_floor)
623 /// - [`Dt::from_days`](../struct.Dt.html#method.from_days)
624 #[inline(always)]
625 pub const fn to_days(&self) -> (i128, i128) {
626 (self.attos / ATTOS_PER_DAY, self.attos % ATTOS_PER_DAY)
627 }
628
629 /// Splits into whole days and leftover attoseconds, rounding the whole
630 /// part down so the leftover is always ≥ 0.
631 ///
632 /// Returns `(whole, frac_attos)`. Same floor rule as
633 /// [`to_sec_floor`](../struct.Dt.html#method.to_sec_floor) (e.g. −1.5 days →
634 /// `(-2, half a day in attoseconds)`). For truncation toward zero, use
635 /// [`to_days`](../struct.Dt.html#method.to_days).
636 ///
637 /// ## Examples
638 ///
639 /// ```rust
640 /// use deep_time::Dt;
641 /// use deep_time::macros::{from_days_f, hours};
642 ///
643 /// // 1.25 d → 1 day + 6 hours leftover
644 /// assert_eq!(from_days_f!(1.25).to_days_floor(), (1, hours!(6)));
645 ///
646 /// // -1.25 d → -2 whole days + 18 hours leftover
647 /// assert_eq!(from_days_f!(-1.25).to_days_floor(), (-2, hours!(18)));
648 /// ```
649 ///
650 /// ## See also
651 ///
652 /// - [`Dt::to_days`](../struct.Dt.html#method.to_days)
653 #[inline(always)]
654 pub const fn to_days_floor(&self) -> (i128, i128) {
655 (
656 self.attos.div_euclid(ATTOS_PER_DAY),
657 self.attos.rem_euclid(ATTOS_PER_DAY),
658 )
659 }
660
661 /// Converts this duration to days as a floating-point number.
662 ///
663 /// ## Examples
664 ///
665 /// ```rust
666 /// use deep_time::Dt;
667 /// use deep_time::macros::from_days_f;
668 ///
669 /// assert_eq!(Dt::ZERO.to_days_f(), 0.0);
670 /// assert_eq!(from_days_f!(1.5).to_days_f(), 1.5);
671 /// assert_eq!(from_days_f!(-1.5).to_days_f(), -1.5);
672 /// ```
673 ///
674 /// ## See also
675 ///
676 /// - [`Dt::from_days_f`](../struct.Dt.html#method.from_days_f)
677 #[inline(always)]
678 pub const fn to_days_f(&self) -> Real {
679 Self::attos_to_unit_f(self.attos, ATTOS_PER_DAY)
680 }
681
682 /// Splits into whole weeks and leftover attoseconds, dropping any fraction
683 /// toward zero (1.25 → 1, −1.25 → −1).
684 ///
685 /// A week is 7 civil days (`604_800` seconds). Returns `(whole, frac_attos)`.
686 /// When negative with a fraction, `frac_attos` is negative too — e.g.
687 /// −1.25 weeks is `(-1, −1 day − 18 hours in attoseconds)`.
688 ///
689 /// For a leftover that is always ≥ 0, use
690 /// [`to_weeks_floor`](../struct.Dt.html#method.to_weeks_floor).
691 ///
692 /// ## Examples
693 ///
694 /// ```rust
695 /// use deep_time::Dt;
696 /// use deep_time::macros::{days, dt, hours, weeks};
697 ///
698 /// // Quarter week = 1 day + 18 hours
699 /// let q = days!(1) + hours!(18);
700 /// assert_eq!(dt!(weeks!(1) + q).to_weeks(), (1, q));
701 /// assert_eq!(dt!(-weeks!(1) - q).to_weeks(), (-1, -q));
702 /// ```
703 ///
704 /// ## See also
705 ///
706 /// - [`Dt::to_weeks_floor`](../struct.Dt.html#method.to_weeks_floor)
707 /// - [`Dt::from_weeks`](../struct.Dt.html#method.from_weeks)
708 #[inline(always)]
709 pub const fn to_weeks(&self) -> (i128, i128) {
710 (self.attos / ATTOS_PER_WEEK, self.attos % ATTOS_PER_WEEK)
711 }
712
713 /// Splits into whole weeks and leftover attoseconds, rounding the whole
714 /// part down so the leftover is always ≥ 0.
715 ///
716 /// A week is 7 civil days (`604_800` seconds). Returns `(whole, frac_attos)`.
717 /// Same floor rule as
718 /// [`to_sec_floor`](../struct.Dt.html#method.to_sec_floor) (e.g. −1.5 weeks →
719 /// `(-2, half a week in attoseconds)`). For truncation toward zero, use
720 /// [`to_weeks`](../struct.Dt.html#method.to_weeks).
721 ///
722 /// ## Examples
723 ///
724 /// ```rust
725 /// use deep_time::Dt;
726 /// use deep_time::macros::{days, dt, hours, weeks};
727 ///
728 /// // Quarter week = 1 day + 18 hours; three-quarter = 5 days + 6 hours
729 /// let q = days!(1) + hours!(18);
730 /// let three_q = days!(5) + hours!(6);
731 /// assert_eq!(dt!(weeks!(1) + q).to_weeks_floor(), (1, q));
732 ///
733 /// // -1.25 weeks → -2 whole weeks + 0.75 week leftover
734 /// assert_eq!(dt!(-weeks!(1) - q).to_weeks_floor(), (-2, three_q));
735 /// ```
736 ///
737 /// ## See also
738 ///
739 /// - [`Dt::to_weeks`](../struct.Dt.html#method.to_weeks)
740 #[inline(always)]
741 pub const fn to_weeks_floor(&self) -> (i128, i128) {
742 (
743 self.attos.div_euclid(ATTOS_PER_WEEK),
744 self.attos.rem_euclid(ATTOS_PER_WEEK),
745 )
746 }
747
748 /// Converts this duration to weeks as a floating-point number.
749 ///
750 /// A week is 7 civil days (`604_800` seconds).
751 ///
752 /// ## Examples
753 ///
754 /// ```rust
755 /// use deep_time::Dt;
756 /// use deep_time::macros::{days, dt, hours, weeks};
757 ///
758 /// assert_eq!(Dt::ZERO.to_weeks_f(), 0.0);
759 ///
760 /// // Half week = 3 days + 12 hours
761 /// let half = days!(3) + hours!(12);
762 /// assert_eq!(dt!(weeks!(1) + half).to_weeks_f(), 1.5);
763 /// assert_eq!(dt!(-weeks!(1) - half).to_weeks_f(), -1.5);
764 /// ```
765 ///
766 /// ## See also
767 ///
768 /// - [`Dt::from_weeks`](../struct.Dt.html#method.from_weeks)
769 /// - [`Dt::to_weeks`](../struct.Dt.html#method.to_weeks)
770 #[inline(always)]
771 pub const fn to_weeks_f(&self) -> Real {
772 Self::attos_to_unit_f(self.attos, ATTOS_PER_WEEK)
773 }
774
775 /// Splits into whole milliseconds and leftover attoseconds, dropping any
776 /// fraction toward zero (1.7 → 1, −1.7 → −1).
777 ///
778 /// Returns `(whole, frac_attos)`. When negative with a fraction,
779 /// `frac_attos` is negative too. For a leftover that is always ≥ 0, use
780 /// [`to_ms_floor`](../struct.Dt.html#method.to_ms_floor).
781 ///
782 /// ## Examples
783 ///
784 /// ```rust
785 /// use deep_time::Dt;
786 /// use deep_time::macros::{from_ms, us};
787 ///
788 /// assert_eq!(from_ms!(1, us!(300)).to_ms(), (1, us!(300)));
789 /// assert_eq!(from_ms!(-1, -us!(300)).to_ms(), (-1, -us!(300)));
790 /// ```
791 #[inline(always)]
792 pub const fn to_ms(&self) -> (i128, i128) {
793 (
794 self.attos / ATTOS_PER_MS_I128,
795 self.attos % ATTOS_PER_MS_I128,
796 )
797 }
798
799 /// Splits into whole microseconds and leftover attoseconds, dropping any
800 /// fraction toward zero (1.7 → 1, −1.7 → −1).
801 ///
802 /// Returns `(whole, frac_attos)`. When negative with a fraction,
803 /// `frac_attos` is negative too. For a leftover that is always ≥ 0, use
804 /// [`to_us_floor`](../struct.Dt.html#method.to_us_floor).
805 ///
806 /// ## Examples
807 ///
808 /// ```rust
809 /// use deep_time::Dt;
810 /// use deep_time::macros::{from_us, ns};
811 ///
812 /// assert_eq!(from_us!(1, ns!(300)).to_us(), (1, ns!(300)));
813 /// assert_eq!(from_us!(-1, -ns!(300)).to_us(), (-1, -ns!(300)));
814 /// ```
815 #[inline(always)]
816 pub const fn to_us(&self) -> (i128, i128) {
817 (
818 self.attos / ATTOS_PER_US_I128,
819 self.attos % ATTOS_PER_US_I128,
820 )
821 }
822
823 /// Splits into whole nanoseconds and leftover attoseconds, dropping any
824 /// fraction toward zero (1.7 → 1, −1.7 → −1).
825 ///
826 /// Returns `(whole, frac_attos)`. When negative with a fraction,
827 /// `frac_attos` is negative too. For a leftover that is always ≥ 0, use
828 /// [`to_ns_floor`](../struct.Dt.html#method.to_ns_floor).
829 ///
830 /// ## Examples
831 ///
832 /// ```rust
833 /// use deep_time::Dt;
834 /// use deep_time::macros::{from_ns, ps};
835 ///
836 /// assert_eq!(from_ns!(1, ps!(300)).to_ns(), (1, ps!(300)));
837 /// assert_eq!(from_ns!(-1, -ps!(300)).to_ns(), (-1, -ps!(300)));
838 /// ```
839 #[inline(always)]
840 pub const fn to_ns(&self) -> (i128, i128) {
841 (
842 self.attos / ATTOS_PER_NS_I128,
843 self.attos % ATTOS_PER_NS_I128,
844 )
845 }
846
847 /// Splits into whole picoseconds and leftover attoseconds, dropping any
848 /// fraction toward zero (1.7 → 1, −1.7 → −1).
849 ///
850 /// Returns `(whole, frac_attos)`. When negative with a fraction,
851 /// `frac_attos` is negative too. For a leftover that is always ≥ 0, use
852 /// [`to_ps_floor`](../struct.Dt.html#method.to_ps_floor).
853 ///
854 /// ## Examples
855 ///
856 /// ```rust
857 /// use deep_time::Dt;
858 /// use deep_time::macros::{from_ps, fs};
859 ///
860 /// assert_eq!(from_ps!(1, fs!(300)).to_ps(), (1, fs!(300)));
861 /// assert_eq!(from_ps!(-1, -fs!(300)).to_ps(), (-1, -fs!(300)));
862 /// ```
863 #[inline(always)]
864 pub const fn to_ps(&self) -> (i128, i128) {
865 (
866 self.attos / ATTOS_PER_PS_I128,
867 self.attos % ATTOS_PER_PS_I128,
868 )
869 }
870
871 /// Splits into whole femtoseconds and leftover attoseconds, dropping any
872 /// fraction toward zero (1.7 → 1, −1.7 → −1).
873 ///
874 /// Returns `(whole, frac_attos)`. When negative with a fraction,
875 /// `frac_attos` is negative too. For a leftover that is always ≥ 0, use
876 /// [`to_fs_floor`](../struct.Dt.html#method.to_fs_floor).
877 ///
878 /// ## Examples
879 ///
880 /// ```rust
881 /// use deep_time::Dt;
882 /// use deep_time::macros::from_fs;
883 ///
884 /// assert_eq!(from_fs!(1, 500).to_fs(), (1, 500));
885 /// assert_eq!(from_fs!(-1, -500).to_fs(), (-1, -500));
886 /// ```
887 #[inline(always)]
888 pub const fn to_fs(&self) -> (i128, i128) {
889 (
890 self.attos / ATTOS_PER_FS_I128,
891 self.attos % ATTOS_PER_FS_I128,
892 )
893 }
894}