Skip to main content

deep_time/dt/
to.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,
5};
6
7impl Dt {
8    /// Returns the whole seconds portion of this [`Dt`] using truncation towards zero
9    /// (i.e., the integer part obtained via truncating division, without rounding).
10    ///
11    /// This is equivalent to `self.attos / ATTOS_PER_SEC_I128`.
12    ///
13    /// Unlike
14    /// [`to_sec_floor`](../struct.Dt.html#method.to_sec_floor)
15    /// (which uses Euclidean division, flooring towards
16    /// negative infinity for negative values to keep the fractional part non-negative),
17    /// this version truncates towards zero.
18    ///
19    /// Consequently, for values in `(-1, 0)` seconds (e.g. -0.3 s or -0.8 s),
20    /// both return `0`.
21    ///
22    /// ## Examples
23    ///
24    /// ```rust
25    /// use deep_time::{Dt, dt};
26    ///
27    /// // -0.3 seconds → truncates to 0
28    /// let dt = dt!(-300_000_000_000_000_000);
29    /// assert_eq!(dt.to_sec(), 0);
30    ///
31    /// // -0.8 seconds → truncates to 0
32    /// let dt = dt!(-800_000_000_000_000_000);
33    /// assert_eq!(dt.to_sec(), 0);
34    ///
35    /// // -1.3 seconds → truncates to -1 (while to_sec_floor gives -2)
36    /// let dt = dt!(-1_300_000_000_000_000_000);
37    /// assert_eq!(dt.to_sec(), -1);
38    /// assert_eq!(dt.to_sec_floor(), -2);
39    ///
40    /// // Positive values behave the same as `to_sec_floor`
41    /// let dt = dt!(1_300_000_000_000_000_000);
42    /// assert_eq!(dt.to_sec(), 1);
43    /// assert_eq!(dt.to_sec_floor(), 1);
44    /// ```
45    #[inline(always)]
46    pub const fn to_sec(&self) -> i128 {
47        self.attos / ATTOS_PER_SEC_I128
48    }
49
50    /// Returns the whole seconds portion of this [`Dt`] using truncation towards zero,
51    /// then clamped to an [`i64`].
52    ///
53    /// If the truncated seconds value lies outside the `i64` range, the result
54    /// saturates to [`i64::MAX`] or [`i64::MIN`].
55    ///
56    /// See
57    /// [`to_sec`](../struct.Dt.html#method.to_sec)
58    /// for the truncation semantics
59    /// (towards zero, no rounding).
60    ///
61    /// ## Examples
62    ///
63    /// ```rust
64    /// use deep_time::{Dt, dt};
65    ///
66    /// let dt = dt!(-1_300_000_000_000_000_000);
67    /// assert_eq!(dt.to_sec64(), -1);
68    ///
69    /// let dt = dt!(1_300_000_000_000_000_000);
70    /// assert_eq!(dt.to_sec64_floor(), 1);
71    /// ```
72    #[inline(always)]
73    pub const fn to_sec64(&self) -> i64 {
74        Self::to_i64(self.attos / ATTOS_PER_SEC_I128)
75    }
76
77    /// If this time were turned into [`i128`] seconds and [`u64`] (always
78    /// pushing to the positive) fractional attoseconds, this returns the
79    /// whole seconds part.
80    ///
81    /// To just get seconds rounded to the nearest second use
82    /// [`Dt::to_sec_round`](../struct.Dt.html#method.to_sec_round)
83    /// instead.
84    ///
85    /// ## Examples
86    ///
87    /// ```rust
88    /// use deep_time::{Dt, Scale, dt};
89    ///
90    /// // negative 1.3 seconds
91    /// let dt = dt!(-1_300_000_000_000_000_000);
92    ///
93    /// // becomes positive 700ms
94    /// let frac = dt.to_sec_ufrac();
95    /// assert_eq!(frac, 700_000_000_000_000_000);
96    ///
97    /// // becomes negative 2 seconds
98    /// let sec = dt.to_sec_floor();
99    /// assert_eq!(sec, -2);
100    ///
101    /// let dt = dt!(1_300_000_000_000_000_000);
102    ///
103    /// assert_eq!(dt.to_sec_floor(), 1);
104    /// assert_eq!(dt.to_sec_ufrac(), 300_000_000_000_000_000);
105    ///
106    /// // if you just want rounded seconds
107    /// // use to_sec_round() instead
108    /// let dt = dt!(-1_300_000_000_000_000_000);
109    /// let sec = dt.to_sec_round();
110    /// assert_eq!(sec, -1);
111    /// ```
112    #[inline(always)]
113    pub const fn to_sec_floor(&self) -> i128 {
114        self.attos.div_euclid(ATTOS_PER_SEC_I128)
115    }
116
117    /// If this time were turned into [`i64`] seconds and [`u64`] (always
118    /// pushing to the positive) fractional attoseconds, this returns the
119    /// whole seconds part.
120    ///
121    /// To just get seconds rounded to the nearest second use
122    /// [`Dt::to_sec_round`](../struct.Dt.html#method.to_sec_round)
123    /// instead.
124    ///
125    /// ## Examples
126    ///
127    /// ```rust
128    /// use deep_time::{Dt, Scale, dt};
129    ///
130    /// // negative 1.3 seconds
131    /// let dt = dt!(-1_300_000_000_000_000_000);
132    ///
133    /// // becomes positive 700ms
134    /// let frac = dt.to_sec_ufrac();
135    /// assert_eq!(frac, 700_000_000_000_000_000);
136    ///
137    /// // becomes negative 2 seconds
138    /// let sec = dt.to_sec64_floor();
139    /// assert_eq!(sec, -2);
140    ///
141    /// let dt = dt!(1_300_000_000_000_000_000);
142    ///
143    /// assert_eq!(dt.to_sec64_floor(), 1);
144    /// assert_eq!(dt.to_sec_ufrac(), 300_000_000_000_000_000);
145    ///
146    /// // if you just want rounded seconds
147    /// // use to_sec_round() instead
148    /// let dt = dt!(-1_300_000_000_000_000_000);
149    /// let sec = dt.to_sec_round();
150    /// assert_eq!(sec, -1);
151    /// ```
152    #[inline(always)]
153    pub const fn to_sec64_floor(&self) -> i64 {
154        Self::to_i64(self.attos.div_euclid(ATTOS_PER_SEC_I128))
155    }
156
157    /// Returns this [`Dt`] rounded to the nearest whole second, then
158    /// converted to an [`i128`] number of seconds.
159    ///
160    /// - Exactly halfway cases (e.g. 0.5 s, -0.5 s) round as follows:
161    ///   0.5 becomes 1 and -0.5 becomes -1.
162    /// - Matches the behavior of [`Dt::round`].
163    ///
164    /// ## Examples
165    ///
166    /// ```rust
167    /// use deep_time::{Dt, dt};
168    ///
169    /// // 1.3 seconds → rounds to 1
170    /// assert_eq!(dt!(1_300_000_000_000_000_000).to_sec_round(), 1);
171    ///
172    /// // -1.3 seconds → rounds to -1
173    /// assert_eq!(dt!(-1_300_000_000_000_000_000).to_sec_round(), -1);
174    ///
175    /// // 1.6 seconds → rounds to 2
176    /// assert_eq!(dt!(1_600_000_000_000_000_000).to_sec_round(), 2);
177    ///
178    /// // Halfway cases
179    /// assert_eq!(dt!(500_000_000_000_000_000).to_sec_round(), 1);
180    /// assert_eq!(dt!(-500_000_000_000_000_000).to_sec_round(), -1);
181    /// ```
182    #[inline(always)]
183    pub const fn to_sec_round(&self) -> i128 {
184        self.round_to_sec().to_sec()
185    }
186
187    /// Returns this [`Dt`] rounded to the nearest whole second, then
188    /// converted to an [`i64`] number of seconds.
189    ///
190    /// - Exactly halfway cases round as follows: 0.5 becomes 1 and -0.5 becomes -1,
191    ///   same as
192    ///   [`to_sec_round`](../struct.Dt.html#method.to_sec_round).
193    /// - If the rounded value is outside the representable `i64` range,
194    ///   it saturates to [`i64::MAX`] or [`i64::MIN`].
195    ///
196    /// ## Examples
197    ///
198    /// ```rust
199    /// use deep_time::{Dt, dt};
200    ///
201    /// let dt = dt!(1_300_000_000_000_000_000);
202    /// assert_eq!(dt.to_sec64_round(), 1);
203    ///
204    /// let dt = dt!(-1_300_000_000_000_000_000);
205    /// assert_eq!(dt.to_sec64_round(), -1);
206    /// ```
207    #[inline(always)]
208    pub const fn to_sec64_round(&self) -> i64 {
209        Self::to_i64(self.round_to_sec().to_sec())
210    }
211
212    /// Converts this [`Dt`] to an f64 number of seconds since the reference
213    /// epoch of its associated scale.
214    ///
215    /// - The conversion is lossy, as [`f64`] provides approximately 15.95 decimal
216    ///   digits of precision.
217    #[inline(always)]
218    pub const fn to_f64(&self) -> f64 {
219        self.to_sec_f()
220    }
221
222    /// Converts this [`Dt`] to a floating-point number of seconds since the reference
223    /// epoch of its associated scale.
224    ///
225    /// - The conversion is lossy, as [`f64`] provides approximately 15.95 decimal
226    ///   digits of precision.
227    pub const fn to_sec_f(&self) -> Real {
228        let attos = self.attos;
229
230        if attos == 0 {
231            return 0.0;
232        }
233        let sec = attos.div_euclid(ATTOS_PER_SEC_I128);
234        let rem = attos.rem_euclid(ATTOS_PER_SEC_I128); // always in [0, aps)
235
236        if sec < 0 && rem > ATTOS_PER_SEC_I128 / 2 {
237            // original cancellation-avoidance path
238            let small = ATTOS_PER_SEC_I128 - rem;
239            let small_f = f!(small as u64) / ATTOS_PER_SECF;
240            (sec as f64) + 1.0 - small_f
241        } else {
242            (sec as f64) + f!(rem as u64) / ATTOS_PER_SECF
243        }
244    }
245
246    /// If this time were turned into seconds, this returns the signed fractional
247    /// attoseconds part — the amount left over after removing whole seconds, with the
248    /// same sign as the original value when non-zero.
249    ///
250    /// Pairs with [`from_sec_and_frac`](../struct.Dt.html#method.from_sec_and_frac).
251    #[inline(always)]
252    pub const fn to_sec_frac(&self) -> i64 {
253        (self.attos % ATTOS_PER_SEC_I128) as i64
254    }
255
256    /// If this time were turned into i64 seconds and u64 (always pushing to the positive)
257    /// fractional attoseconds, this returns the fractional attoseconds part.
258    ///
259    /// - Always returns a value in the range `0 ≤ x < ATTOS_PER_SEC`.
260    /// - For negative [`Dt`]s this is **not** simply the decimal part of the time in seconds.
261    ///
262    /// ## Examples
263    ///
264    /// ```rust
265    /// use deep_time::{Dt, Scale, dt};
266    ///
267    /// // negative 1.3 seconds
268    /// let dt = dt!(-1_300_000_000_000_000_000);
269    ///
270    /// // becomes positive 700ms
271    /// let frac = dt.to_sec_ufrac();
272    /// assert_eq!(frac, 700_000_000_000_000_000);
273    ///
274    /// // becomes -2 seconds
275    /// let sec = dt.to_sec64_floor();
276    /// assert_eq!(sec, -2);
277    ///
278    /// let dt = dt!(1_300_000_000_000_000_000);
279    ///
280    /// assert_eq!(dt.to_sec64_floor(), 1);
281    /// assert_eq!(dt.to_sec_ufrac(), 300_000_000_000_000_000);
282    /// ```
283    #[inline(always)]
284    pub const fn to_sec_ufrac(&self) -> u64 {
285        self.attos.rem_euclid(ATTOS_PER_SEC_I128) as u64
286    }
287
288    /// Returns a new [`Dt`] rounded to the nearest second.
289    #[inline(always)]
290    pub const fn round_to_sec(&self) -> Dt {
291        self.round(crate::dt!(ATTOS_PER_SEC_I128))
292    }
293
294    /// Returns the total time in attoseconds.
295    #[inline(always)]
296    pub const fn to_attos(&self) -> i128 {
297        self.attos
298    }
299
300    /// Converts this [`Dt`] into whole femtoseconds and a fractional part within one femtosecond.
301    ///
302    /// - Returns `(whole, frac_attos)` where `frac_attos` is always non-negative.
303    /// - For negative values this does **not** split at the decimal point — see
304    ///   [`to_sec64_floor`](../struct.Dt.html#method.to_sec64_floor) and
305    ///   [`to_sec_ufrac`](../struct.Dt.html#method.to_sec_ufrac).
306    /// - For truncation toward zero, use [`to_fs`](../struct.Dt.html#method.to_fs).
307    #[inline(always)]
308    pub const fn to_fs_floor(&self) -> (i128, u128) {
309        (
310            self.attos.div_euclid(ATTOS_PER_FS_I128),
311            self.attos.rem_euclid(ATTOS_PER_FS_I128) as u128,
312        )
313    }
314
315    /// Converts this [`Dt`] into whole picoseconds and a fractional part within one picosecond.
316    ///
317    /// - Returns `(whole, frac_attos)` where `frac_attos` is always non-negative.
318    /// - For negative values this does **not** split at the decimal point — see
319    ///   [`to_sec64_floor`](../struct.Dt.html#method.to_sec64_floor) and
320    ///   [`to_sec_ufrac`](../struct.Dt.html#method.to_sec_ufrac).
321    /// - For truncation toward zero, use [`to_ps`](../struct.Dt.html#method.to_ps).
322    #[inline(always)]
323    pub const fn to_ps_floor(&self) -> (i128, u128) {
324        (
325            self.attos.div_euclid(ATTOS_PER_PS_I128),
326            self.attos.rem_euclid(ATTOS_PER_PS_I128) as u128,
327        )
328    }
329
330    /// Converts this [`Dt`] into whole nanoseconds and a fractional part within one nanosecond.
331    ///
332    /// - Returns `(whole, frac_attos)` where `frac_attos` is always non-negative.
333    /// - For negative values this does **not** split at the decimal point — see
334    ///   [`to_sec64_floor`](../struct.Dt.html#method.to_sec64_floor) and
335    ///   [`to_sec_ufrac`](../struct.Dt.html#method.to_sec_ufrac).
336    /// - For truncation toward zero, use [`to_ns`](../struct.Dt.html#method.to_ns).
337    #[inline(always)]
338    pub const fn to_ns_floor(&self) -> (i128, u128) {
339        (
340            self.attos.div_euclid(ATTOS_PER_NS_I128),
341            self.attos.rem_euclid(ATTOS_PER_NS_I128) as u128,
342        )
343    }
344
345    /// Converts this [`Dt`] into whole microseconds and a fractional part within one microsecond.
346    ///
347    /// - Returns `(whole, frac_attos)` where `frac_attos` is always non-negative.
348    /// - For negative values this does **not** split at the decimal point — see
349    ///   [`to_sec64_floor`](../struct.Dt.html#method.to_sec64_floor) and
350    ///   [`to_sec_ufrac`](../struct.Dt.html#method.to_sec_ufrac).
351    /// - For truncation toward zero, use [`to_us`](../struct.Dt.html#method.to_us).
352    #[inline(always)]
353    pub const fn to_us_floor(&self) -> (i128, u128) {
354        (
355            self.attos.div_euclid(ATTOS_PER_US_I128),
356            self.attos.rem_euclid(ATTOS_PER_US_I128) as u128,
357        )
358    }
359
360    /// Converts this [`Dt`] into whole milliseconds and a fractional part within one millisecond.
361    ///
362    /// - Returns `(whole, frac_attos)` where `frac_attos` is always non-negative.
363    /// - For negative values this does **not** split at the decimal point — see
364    ///   [`to_sec64_floor`](../struct.Dt.html#method.to_sec64_floor) and
365    ///   [`to_sec_ufrac`](../struct.Dt.html#method.to_sec_ufrac).
366    /// - For truncation toward zero, use [`to_ms`](../struct.Dt.html#method.to_ms).
367    #[inline(always)]
368    pub const fn to_ms_floor(&self) -> (i128, u128) {
369        (
370            self.attos.div_euclid(ATTOS_PER_MS_I128),
371            self.attos.rem_euclid(ATTOS_PER_MS_I128) as u128,
372        )
373    }
374
375    /// Converts this [`Dt`] into whole minutes and a fractional part within one minute.
376    ///
377    /// - Returns `(whole, frac_attos)` where `frac_attos` is always non-negative.
378    /// - For negative values this does **not** split at the decimal point — see
379    ///   [`to_sec64_floor`](../struct.Dt.html#method.to_sec64_floor) and
380    ///   [`to_sec_ufrac`](../struct.Dt.html#method.to_sec_ufrac).
381    #[inline(always)]
382    pub const fn to_mins_floor(&self) -> (i128, u128) {
383        (
384            self.attos.div_euclid(ATTOS_PER_MIN),
385            self.attos.rem_euclid(ATTOS_PER_MIN) as u128,
386        )
387    }
388
389    /// Converts this [`Dt`] into whole hours and a fractional part within one hour.
390    ///
391    /// - Returns `(whole, frac_attos)` where `frac_attos` is always non-negative.
392    /// - For negative values this does **not** split at the decimal point — see
393    ///   [`to_sec64_floor`](../struct.Dt.html#method.to_sec64_floor) and
394    ///   [`to_sec_ufrac`](../struct.Dt.html#method.to_sec_ufrac).
395    #[inline(always)]
396    pub const fn to_hours_floor(&self) -> (i128, u128) {
397        (
398            self.attos.div_euclid(ATTOS_PER_HOUR),
399            self.attos.rem_euclid(ATTOS_PER_HOUR) as u128,
400        )
401    }
402
403    /// Converts this [`Dt`] into whole days and a fractional part within one day.
404    ///
405    /// - Returns `(whole, frac_attos)` where `frac_attos` is always non-negative.
406    /// - For negative values this does **not** split at the decimal point — see
407    ///   [`to_sec64_floor`](../struct.Dt.html#method.to_sec64_floor) and
408    ///   [`to_sec_ufrac`](../struct.Dt.html#method.to_sec_ufrac).
409    ///
410    /// ## Examples
411    ///
412    /// ```rust
413    /// use deep_time::{Dt, Scale, dt};
414    /// use deep_time::consts::ATTOS_PER_HALF_DAY_U128;
415    ///
416    /// // library epoch is 2000-01-01 12:00:00 TAI
417    /// // so result will be negative 2 + half a day
418    /// // effectively -1.5 days
419    /// let dt = Dt::from_ymd(1999, 12, 31, Scale::TAI, 0, 0, 0, 0);
420    /// let (days, attos) = dt.to_days_floor();
421    /// assert_eq!(days, -2);
422    /// assert_eq!(attos, ATTOS_PER_HALF_DAY_U128);
423    /// ```
424    #[inline(always)]
425    pub const fn to_days_floor(&self) -> (i128, u128) {
426        (
427            self.attos.div_euclid(ATTOS_PER_DAY),
428            self.attos.rem_euclid(ATTOS_PER_DAY) as u128,
429        )
430    }
431
432    /// Converts this [`Dt`] into whole milliseconds and a fractional part within one millisecond,
433    /// truncating toward zero.
434    ///
435    /// Returns `(whole, frac_attos)`. `frac_attos` will be negative when `whole` is negative and
436    /// there is a non-zero fraction. See
437    /// [`to_sec`](../struct.Dt.html#method.to_sec)
438    /// for the same rule in seconds.
439    #[inline(always)]
440    pub const fn to_ms(&self) -> (i128, i128) {
441        (
442            self.attos / ATTOS_PER_MS_I128,
443            self.attos % ATTOS_PER_MS_I128,
444        )
445    }
446
447    /// Converts this [`Dt`] into whole microseconds and a fractional part within one microsecond,
448    /// truncating toward zero.
449    ///
450    /// Returns `(whole, frac_attos)`. `frac_attos` will be negative when `whole` is negative and
451    /// there is a non-zero fraction. See
452    /// [`to_sec`](../struct.Dt.html#method.to_sec)
453    /// for the same rule in seconds.
454    #[inline(always)]
455    pub const fn to_us(&self) -> (i128, i128) {
456        (
457            self.attos / ATTOS_PER_US_I128,
458            self.attos % ATTOS_PER_US_I128,
459        )
460    }
461
462    /// Converts this [`Dt`] into whole nanoseconds and a fractional part within one nanosecond,
463    /// truncating toward zero.
464    ///
465    /// Returns `(whole, frac_attos)`. `frac_attos` will be negative when `whole` is negative and
466    /// there is a non-zero fraction. See
467    /// [`to_sec`](../struct.Dt.html#method.to_sec)
468    /// for the same rule in seconds.
469    #[inline(always)]
470    pub const fn to_ns(&self) -> (i128, i128) {
471        (
472            self.attos / ATTOS_PER_NS_I128,
473            self.attos % ATTOS_PER_NS_I128,
474        )
475    }
476
477    /// Converts this [`Dt`] into whole picoseconds and a fractional part within one picosecond,
478    /// truncating toward zero.
479    ///
480    /// Returns `(whole, frac_attos)`. `frac_attos` will be negative when `whole` is negative and
481    /// there is a non-zero fraction. See
482    /// [`to_sec`](../struct.Dt.html#method.to_sec)
483    /// for the same rule in seconds.
484    #[inline(always)]
485    pub const fn to_ps(&self) -> (i128, i128) {
486        (
487            self.attos / ATTOS_PER_PS_I128,
488            self.attos % ATTOS_PER_PS_I128,
489        )
490    }
491
492    /// Converts this [`Dt`] into whole femtoseconds and a fractional part within one femtosecond,
493    /// truncating toward zero.
494    ///
495    /// Returns `(whole, frac_attos)`. `frac_attos` will be negative when `whole` is negative and
496    /// there is a non-zero fraction. See
497    /// [`to_sec`](../struct.Dt.html#method.to_sec)
498    /// for the same rule in seconds.
499    #[inline(always)]
500    pub const fn to_fs(&self) -> (i128, i128) {
501        (
502            self.attos / ATTOS_PER_FS_I128,
503            self.attos % ATTOS_PER_FS_I128,
504        )
505    }
506}