/rust/registry/src/index.crates.io-1949cf8c6b5b557f/hifitime-4.3.1/src/duration/mod.rs
Line | Count | Source |
1 | | /* |
2 | | * Hifitime |
3 | | * Copyright (C) 2017-onward Christopher Rabotin <christopher.rabotin@gmail.com> et al. (cf. https://github.com/nyx-space/hifitime/graphs/contributors) |
4 | | * This Source Code Form is subject to the terms of the Mozilla Public |
5 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
6 | | * file, You can obtain one at https://mozilla.org/MPL/2.0/. |
7 | | * |
8 | | * Documentation: https://nyxspace.com/ |
9 | | */ |
10 | | |
11 | | use crate::errors::{DurationError, HifitimeError}; |
12 | | use crate::{SECONDS_PER_CENTURY, SECONDS_PER_DAY, SECONDS_PER_HOUR, SECONDS_PER_MINUTE}; |
13 | | |
14 | | pub use crate::{Freq, Frequencies, TimeUnits, Unit}; |
15 | | |
16 | | use core::cmp::Ordering; |
17 | | use core::fmt; |
18 | | use core::hash::{Hash, Hasher}; |
19 | | |
20 | | #[cfg(feature = "serde")] |
21 | | use serde::{Deserialize, Deserializer, Serialize, Serializer}; |
22 | | |
23 | | #[cfg(feature = "serde")] |
24 | | use core::str::FromStr; |
25 | | |
26 | | pub mod parse; |
27 | | |
28 | | #[cfg(feature = "python")] |
29 | | mod python; |
30 | | |
31 | | #[cfg(feature = "python")] |
32 | | use pyo3::prelude::pyclass; |
33 | | |
34 | | #[cfg(not(feature = "std"))] |
35 | | #[allow(unused_imports)] // Import is indeed used. |
36 | | use num_traits::Float; |
37 | | |
38 | | #[cfg(kani)] |
39 | | mod kani_verif; |
40 | | |
41 | | pub const DAYS_PER_CENTURY_U64: u64 = 36_525; |
42 | | pub const NANOSECONDS_PER_MICROSECOND: u64 = 1_000; |
43 | | pub const NANOSECONDS_PER_MILLISECOND: u64 = 1_000 * NANOSECONDS_PER_MICROSECOND; |
44 | | pub const NANOSECONDS_PER_SECOND: u64 = 1_000 * NANOSECONDS_PER_MILLISECOND; |
45 | | pub(crate) const NANOSECONDS_PER_SECOND_U32: u32 = 1_000_000_000; |
46 | | pub const NANOSECONDS_PER_MINUTE: u64 = 60 * NANOSECONDS_PER_SECOND; |
47 | | pub const NANOSECONDS_PER_HOUR: u64 = 60 * NANOSECONDS_PER_MINUTE; |
48 | | pub const NANOSECONDS_PER_DAY: u64 = 24 * NANOSECONDS_PER_HOUR; |
49 | | pub const NANOSECONDS_PER_CENTURY: u64 = DAYS_PER_CENTURY_U64 * NANOSECONDS_PER_DAY; |
50 | | |
51 | | pub mod ops; |
52 | | |
53 | | /// Defines generally usable durations for nanosecond precision valid for 32,768 centuries in either direction, and only on 80 bits / 10 octets. |
54 | | /// |
55 | | /// **Important conventions:** |
56 | | /// 1. The negative durations can be mentally modeled "BC" years. One hours before 01 Jan 0000, it was "-1" years but 365 days and 23h into the current day. |
57 | | /// It was decided that the nanoseconds corresponds to the nanoseconds _into_ the current century. In other words, |
58 | | /// a duration with centuries = -1 and nanoseconds = 0 is _a greater duration_ (further from zero) than centuries = -1 and nanoseconds = 1. |
59 | | /// Duration zero minus one nanosecond returns a century of -1 and a nanosecond set to the number of nanoseconds in one century minus one. |
60 | | /// That difference is exactly 1 nanoseconds, where the former duration is "closer to zero" than the latter. |
61 | | /// As such, the largest negative duration that can be represented sets the centuries to i16::MAX and its nanoseconds to NANOSECONDS_PER_CENTURY. |
62 | | /// 2. Negative and positive durations are distinct: -15 minutes != 15 minutes. Use the signum function to check the sign, and abs() to get the absolute value. |
63 | | /// |
64 | | /// :type string_repr: str |
65 | | #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)] |
66 | | #[repr(C)] |
67 | | #[cfg_attr(feature = "python", pyclass)] |
68 | | #[cfg_attr(feature = "python", pyo3(module = "hifitime"))] |
69 | | pub struct Duration { |
70 | | pub(crate) centuries: i16, |
71 | | pub(crate) nanoseconds: u64, |
72 | | } |
73 | | |
74 | | impl Hash for Duration { |
75 | 0 | fn hash<H: Hasher>(&self, hasher: &mut H) { |
76 | 0 | self.centuries.hash(hasher); |
77 | 0 | self.nanoseconds.hash(hasher); |
78 | 0 | } |
79 | | } |
80 | | |
81 | | impl Default for Duration { |
82 | 0 | fn default() -> Self { |
83 | 0 | Duration::ZERO |
84 | 0 | } |
85 | | } |
86 | | |
87 | | #[cfg(feature = "serde")] |
88 | | impl Serialize for Duration { |
89 | 0 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
90 | 0 | where |
91 | 0 | S: Serializer, |
92 | | { |
93 | 0 | let s = self.to_string(); |
94 | 0 | serializer.serialize_str(&s) |
95 | 0 | } Unexecuted instantiation: <hifitime::duration::Duration as serde_core::ser::Serialize>::serialize::<serde_lexpr::value::ser::Serializer> Unexecuted instantiation: <hifitime::duration::Duration as serde_core::ser::Serialize>::serialize::<_> |
96 | | } |
97 | | |
98 | | #[cfg(feature = "serde")] |
99 | | impl<'de> Deserialize<'de> for Duration { |
100 | 0 | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
101 | 0 | where |
102 | 0 | D: Deserializer<'de>, |
103 | | { |
104 | 0 | let s = String::deserialize(deserializer)?; |
105 | 0 | Duration::from_str(&s).map_err(serde::de::Error::custom) |
106 | 0 | } Unexecuted instantiation: <hifitime::duration::Duration as serde_core::de::Deserialize>::deserialize::<serde::private::de::missing_field::MissingFieldDeserializer<serde_lexpr::error::Error>> Unexecuted instantiation: <hifitime::duration::Duration as serde_core::de::Deserialize>::deserialize::<&mut serde_lexpr::value::de::Deserializer> Unexecuted instantiation: <hifitime::duration::Duration as serde_core::de::Deserialize>::deserialize::<_> |
107 | | } |
108 | | |
109 | | // Defines the methods that should be classmethods in Python, but must be redefined as per https://github.com/PyO3/pyo3/issues/1003#issuecomment-844433346 |
110 | | impl Duration { |
111 | | /// A duration of exactly zero nanoseconds |
112 | | pub const ZERO: Self = Self { |
113 | | centuries: 0, |
114 | | nanoseconds: 0, |
115 | | }; |
116 | | |
117 | | /// Maximum duration that can be represented |
118 | | pub const MAX: Self = Self { |
119 | | centuries: i16::MAX, |
120 | | nanoseconds: NANOSECONDS_PER_CENTURY, |
121 | | }; |
122 | | |
123 | | /// Minimum duration that can be represented |
124 | | pub const MIN: Self = Self { |
125 | | centuries: i16::MIN, |
126 | | nanoseconds: 0, |
127 | | }; |
128 | | |
129 | | /// Smallest duration that can be represented |
130 | | pub const EPSILON: Self = Self { |
131 | | centuries: 0, |
132 | | nanoseconds: 1, |
133 | | }; |
134 | | |
135 | | /// Minimum positive duration is one nanoseconds |
136 | | pub const MIN_POSITIVE: Self = Self::EPSILON; |
137 | | |
138 | | /// Minimum negative duration is minus one nanosecond |
139 | | pub const MIN_NEGATIVE: Self = Self { |
140 | | centuries: -1, |
141 | | nanoseconds: NANOSECONDS_PER_CENTURY - 1, |
142 | | }; |
143 | | |
144 | | #[must_use] |
145 | | /// Create a normalized duration from its parts |
146 | | #[cfg_attr(kani, kani::ensures(|result: &Self| { |
147 | | result.nanoseconds < NANOSECONDS_PER_CENTURY |
148 | | || result.parts_are_equal(Self::MAX) |
149 | | || result.parts_are_equal(Self::MIN) |
150 | | }))] |
151 | 275M | pub const fn from_parts(centuries: i16, nanoseconds: u64) -> Self { |
152 | 275M | Self { |
153 | 275M | centuries, |
154 | 275M | nanoseconds, |
155 | 275M | } |
156 | 275M | .as_normalized() |
157 | 275M | } |
158 | | |
159 | | #[must_use] |
160 | | /// Converts the total nanoseconds as i128 into this Duration (saving 48 bits) |
161 | | #[cfg_attr(kani, kani::ensures(|result: &Self| { |
162 | | result.nanoseconds < NANOSECONDS_PER_CENTURY |
163 | | || result.parts_are_equal(Self::MAX) |
164 | | || result.parts_are_equal(Self::MIN) |
165 | | }))] |
166 | 142k | pub const fn from_total_nanoseconds(nanos: i128) -> Self { |
167 | | // In this function, we simply check that the input data can be casted. The `normalize` function will check whether more work needs to be done. |
168 | 142k | if nanos == 0 { |
169 | 27.1k | Self::ZERO |
170 | | } else { |
171 | 115k | let centuries_i128 = nanos.div_euclid(NANOSECONDS_PER_CENTURY as i128); |
172 | 115k | let remaining_nanos_i128 = nanos.rem_euclid(NANOSECONDS_PER_CENTURY as i128); |
173 | 115k | if centuries_i128 > (i16::MAX as i128) { |
174 | 40.2k | Self::MAX |
175 | 75.1k | } else if centuries_i128 < (i16::MIN as i128) { |
176 | 11.2k | Self::MIN |
177 | | } else { |
178 | | // We know that the centuries fit, and we know that the nanos are less than the number |
179 | | // of nanos per centuries, and rem_euclid guarantees that it's positive, so the |
180 | | // casting will work fine every time. |
181 | 63.8k | Self::from_parts(centuries_i128 as i16, remaining_nanos_i128 as u64) |
182 | | } |
183 | | } |
184 | 142k | } |
185 | | |
186 | | #[must_use] |
187 | | /// Create a new duration from the truncated nanoseconds (+/- 2927.1 years of duration) |
188 | | #[cfg_attr(kani, kani::ensures(|result: &Self| { |
189 | | result.nanoseconds < NANOSECONDS_PER_CENTURY |
190 | | || result.parts_are_equal(Self::MAX) |
191 | | || result.parts_are_equal(Self::MIN) |
192 | | }))] |
193 | 275M | pub const fn from_truncated_nanoseconds(nanos: i64) -> Self { |
194 | 275M | if nanos < 0 { |
195 | 46.3k | let ns = nanos.unsigned_abs(); |
196 | | // Note: i64::MIN corresponds to a duration just past -3 centuries, so we can't hit the Duration::MIN here. |
197 | 46.3k | let extra_centuries = ns.div_euclid(NANOSECONDS_PER_CENTURY); |
198 | 46.3k | let rem_nanos = ns.rem_euclid(NANOSECONDS_PER_CENTURY); |
199 | 46.3k | Self::from_parts( |
200 | 46.3k | -1 - (extra_centuries as i16), |
201 | 46.3k | NANOSECONDS_PER_CENTURY - rem_nanos, |
202 | | ) |
203 | | } else { |
204 | 275M | Self::from_parts(0, nanos.unsigned_abs()) |
205 | | } |
206 | 275M | } |
207 | | |
208 | | /// Creates a new duration from the provided number of days |
209 | | #[must_use] |
210 | | #[cfg_attr(kani, kani::ensures(|result: &Self| { |
211 | | result.nanoseconds < NANOSECONDS_PER_CENTURY |
212 | | || result.parts_are_equal(Self::MAX) |
213 | | || result.parts_are_equal(Self::MIN) |
214 | | }))] |
215 | | #[cfg_attr(kani, kani::requires(value.is_finite()))] |
216 | 0 | pub const fn from_days(value: f64) -> Self { |
217 | 0 | Unit::Day.const_multiply(value) |
218 | 0 | } |
219 | | |
220 | | /// Creates a new duration from the provided number of hours |
221 | | #[must_use] |
222 | | #[cfg_attr(kani, kani::ensures(|result: &Self| { |
223 | | result.nanoseconds < NANOSECONDS_PER_CENTURY |
224 | | || result.parts_are_equal(Self::MAX) |
225 | | || result.parts_are_equal(Self::MIN) |
226 | | }))] |
227 | | #[cfg_attr(kani, kani::requires(value.is_finite()))] |
228 | 0 | pub const fn from_hours(value: f64) -> Self { |
229 | 0 | Unit::Hour.const_multiply(value) |
230 | 0 | } |
231 | | |
232 | | /// Creates a new duration from the provided number of seconds |
233 | | #[must_use] |
234 | | #[cfg_attr(kani, kani::requires(value.is_finite()))] |
235 | | #[cfg_attr(kani, kani::ensures(|result: &Self| { |
236 | | result.nanoseconds < NANOSECONDS_PER_CENTURY |
237 | | || result.parts_are_equal(Self::MAX) |
238 | | || result.parts_are_equal(Self::MIN) |
239 | | }))] |
240 | 0 | pub const fn from_seconds(value: f64) -> Self { |
241 | 0 | Unit::Second.const_multiply(value) |
242 | 0 | } |
243 | | |
244 | | /// Creates a new duration from the provided number of milliseconds |
245 | | #[must_use] |
246 | | #[cfg_attr(kani, kani::ensures(|result: &Self| { |
247 | | result.nanoseconds < NANOSECONDS_PER_CENTURY |
248 | | || result.parts_are_equal(Self::MAX) |
249 | | || result.parts_are_equal(Self::MIN) |
250 | | }))] |
251 | | #[cfg_attr(kani, kani::requires(value.is_finite()))] |
252 | 0 | pub const fn from_milliseconds(value: f64) -> Self { |
253 | 0 | Unit::Millisecond.const_multiply(value) |
254 | 0 | } |
255 | | |
256 | | /// Creates a new duration from the provided number of microsecond |
257 | | #[must_use] |
258 | | #[cfg_attr(kani, kani::ensures(|result: &Self| { |
259 | | result.nanoseconds < NANOSECONDS_PER_CENTURY |
260 | | || result.parts_are_equal(Self::MAX) |
261 | | || result.parts_are_equal(Self::MIN) |
262 | | }))] |
263 | | #[cfg_attr(kani, kani::requires(value.is_finite()))] |
264 | 0 | pub const fn from_microseconds(value: f64) -> Self { |
265 | 0 | Unit::Microsecond.const_multiply(value) |
266 | 0 | } |
267 | | |
268 | | /// Creates a new duration from the provided number of nanoseconds |
269 | | #[must_use] |
270 | | #[cfg_attr(kani, kani::ensures(|result: &Self| { |
271 | | result.nanoseconds < NANOSECONDS_PER_CENTURY |
272 | | || result.parts_are_equal(Self::MAX) |
273 | | || result.parts_are_equal(Self::MIN) |
274 | | }))] |
275 | | #[cfg_attr(kani, kani::requires(value.is_finite()))] |
276 | 0 | pub const fn from_nanoseconds(value: f64) -> Self { |
277 | 0 | Unit::Nanosecond.const_multiply(value) |
278 | 0 | } |
279 | | |
280 | | /// Creates a new duration from its parts. Set the sign to a negative number for the duration to be negative. |
281 | | #[allow(clippy::too_many_arguments)] |
282 | | #[must_use] |
283 | | #[cfg_attr(kani, kani::ensures(|result: &Self| { |
284 | | result.nanoseconds < NANOSECONDS_PER_CENTURY |
285 | | || result.parts_are_equal(Self::MAX) |
286 | | || result.parts_are_equal(Self::MIN) |
287 | | }))] |
288 | 0 | pub fn compose( |
289 | 0 | sign: i8, |
290 | 0 | days: u64, |
291 | 0 | hours: u64, |
292 | 0 | minutes: u64, |
293 | 0 | seconds: u64, |
294 | 0 | milliseconds: u64, |
295 | 0 | microseconds: u64, |
296 | 0 | nanoseconds: u64, |
297 | 0 | ) -> Self { |
298 | 0 | Self::compose_f64( |
299 | 0 | sign, |
300 | 0 | days as f64, |
301 | 0 | hours as f64, |
302 | 0 | minutes as f64, |
303 | 0 | seconds as f64, |
304 | 0 | milliseconds as f64, |
305 | 0 | microseconds as f64, |
306 | 0 | nanoseconds as f64, |
307 | | ) |
308 | 0 | } |
309 | | |
310 | | /// Creates a new duration from its parts. Set the sign to a negative number for the duration to be negative. |
311 | | #[allow(clippy::too_many_arguments)] |
312 | | #[must_use] |
313 | 0 | pub fn compose_f64( |
314 | 0 | sign: i8, |
315 | 0 | days: f64, |
316 | 0 | hours: f64, |
317 | 0 | minutes: f64, |
318 | 0 | seconds: f64, |
319 | 0 | milliseconds: f64, |
320 | 0 | microseconds: f64, |
321 | 0 | nanoseconds: f64, |
322 | 0 | ) -> Self { |
323 | 0 | let me: Self = days.days() |
324 | 0 | + hours.hours() |
325 | 0 | + minutes.minutes() |
326 | 0 | + seconds.seconds() |
327 | 0 | + milliseconds.milliseconds() |
328 | 0 | + microseconds.microseconds() |
329 | 0 | + nanoseconds.nanoseconds(); |
330 | 0 | if sign < 0 { |
331 | 0 | -me |
332 | | } else { |
333 | 0 | me |
334 | | } |
335 | 0 | } |
336 | | |
337 | | /// Initializes a Duration from a timezone offset |
338 | | #[must_use] |
339 | | #[cfg_attr(kani, kani::ensures(|result: &Self| { |
340 | | result.nanoseconds < NANOSECONDS_PER_CENTURY |
341 | | || result.parts_are_equal(Self::MAX) |
342 | | || result.parts_are_equal(Self::MIN) |
343 | | }))] |
344 | 0 | pub fn from_tz_offset(sign: i8, hours: i64, minutes: i64) -> Self { |
345 | 0 | let dur = hours * Unit::Hour + minutes * Unit::Minute; |
346 | 0 | if sign < 0 { |
347 | 0 | -dur |
348 | | } else { |
349 | 0 | dur |
350 | | } |
351 | 0 | } |
352 | | } |
353 | | |
354 | | impl Duration { |
355 | | /// Change the value of a [Duration] to its normalized equivalent. |
356 | 824M | fn normalize(&mut self) { |
357 | 824M | *self = self.as_normalized(); |
358 | 824M | } |
359 | | |
360 | | /// Return the normalized equivalent of a [Duration]. |
361 | | #[cfg_attr(kani, kani::ensures(|result: &Self| { |
362 | | result.nanoseconds < NANOSECONDS_PER_CENTURY |
363 | | || result.parts_are_equal(Self::MAX) |
364 | | || result.parts_are_equal(Self::MIN) |
365 | | }))] |
366 | 1.10G | const fn as_normalized(self) -> Self { |
367 | 1.10G | let mut normalized_self = self; |
368 | | |
369 | 1.10G | let extra_centuries = self.nanoseconds / NANOSECONDS_PER_CENTURY; |
370 | | |
371 | | // We can skip this whole step if the division shows that we didn't overflow the number of nanoseconds per century |
372 | 1.10G | if extra_centuries > 0 { |
373 | 305M | let rem_nanos = self.nanoseconds % NANOSECONDS_PER_CENTURY; |
374 | | |
375 | 305M | if self.centuries == i16::MAX { |
376 | 305M | if self.nanoseconds.saturating_add(rem_nanos) > Self::MAX.nanoseconds { |
377 | 152M | // Saturated max |
378 | 152M | normalized_self = Self::MAX; |
379 | 152M | } |
380 | | // Else, we're near the MAX but we're within the MAX in nanoseconds, so let's not do anything here. |
381 | 151k | } else if !self.parts_are_equal(Self::MIN) { |
382 | | // The bounds are valid as is, no wrapping needed when rem_nanos is not zero. |
383 | 151k | match self.centuries.checked_add(extra_centuries as i16) { |
384 | 151k | Some(centuries) => { |
385 | 151k | normalized_self.centuries = centuries; |
386 | 151k | normalized_self.nanoseconds = rem_nanos; |
387 | 151k | } |
388 | | None => { |
389 | 0 | if self.centuries >= 0 { |
390 | 0 | // Saturated max again |
391 | 0 | normalized_self = Self::MAX; |
392 | 0 | } else { |
393 | 0 | // Saturated min |
394 | 0 | normalized_self = Self::MIN; |
395 | 0 | } |
396 | | } |
397 | | } |
398 | 0 | } |
399 | 794M | } |
400 | | |
401 | 1.10G | normalized_self |
402 | 1.10G | } |
403 | | |
404 | | /// `const`-compatible equality check between `self` and `other`. |
405 | | /// |
406 | | /// Note that this only checks whether the fields of `self` and `other` are |
407 | | /// the same, not whether they would (if [`Self::normalize`]d) represent |
408 | | /// the same duration. |
409 | 151k | const fn parts_are_equal(&self, other: Duration) -> bool { |
410 | 151k | self.centuries == other.centuries && self.nanoseconds == other.nanoseconds |
411 | 151k | } |
412 | | |
413 | | #[must_use] |
414 | | /// Returns the centuries and nanoseconds of this duration |
415 | | /// NOTE: These items are not public to prevent incorrect durations from being created by modifying the values of the structure directly. |
416 | | #[cfg_attr(kani, kani::ensures(|result| result.0 == self.centuries && result.1 == self.nanoseconds))] |
417 | 71.9k | pub const fn to_parts(&self) -> (i16, u64) { |
418 | 71.9k | (self.centuries, self.nanoseconds) |
419 | 71.9k | } |
420 | | |
421 | | /// Returns the total nanoseconds in a signed 128 bit integer |
422 | | #[must_use] |
423 | | #[cfg_attr(kani, kani::ensures(|result| { |
424 | | *result == i128::from(self.centuries) * i128::from(NANOSECONDS_PER_CENTURY) |
425 | | + i128::from(self.nanoseconds) |
426 | | }))] |
427 | 267k | pub fn total_nanoseconds(&self) -> i128 { |
428 | 267k | i128::from(self.centuries) * i128::from(NANOSECONDS_PER_CENTURY) |
429 | 267k | + i128::from(self.nanoseconds) |
430 | 267k | } |
431 | | |
432 | | /// Returns the truncated nanoseconds in a signed 64 bit integer, if the duration fits. |
433 | 0 | pub fn try_truncated_nanoseconds(&self) -> Result<i64, HifitimeError> { |
434 | 0 | let total = self.total_nanoseconds(); |
435 | 0 | if total > i64::MAX as i128 { |
436 | 0 | Err(HifitimeError::Duration { |
437 | 0 | source: DurationError::Overflow, |
438 | 0 | }) |
439 | 0 | } else if total < i64::MIN as i128 { |
440 | 0 | Err(HifitimeError::Duration { |
441 | 0 | source: DurationError::Underflow, |
442 | 0 | }) |
443 | | } else { |
444 | 0 | Ok(total as i64) |
445 | | } |
446 | 0 | } |
447 | | |
448 | | /// Returns the truncated nanoseconds in a signed 64 bit integer, if the duration fits. |
449 | | /// WARNING: This function will NOT fail and will return the i64::MIN or i64::MAX depending on |
450 | | /// the sign of the centuries if the Duration does not fit on aa i64 |
451 | | #[must_use] |
452 | 0 | pub fn truncated_nanoseconds(&self) -> i64 { |
453 | 0 | match self.try_truncated_nanoseconds() { |
454 | 0 | Ok(val) => val, |
455 | | Err(_) => { |
456 | 0 | if self.centuries < 0 { |
457 | 0 | i64::MIN |
458 | | } else { |
459 | 0 | i64::MAX |
460 | | } |
461 | | } |
462 | | } |
463 | 0 | } |
464 | | |
465 | | /// Returns this duration in seconds f64. |
466 | | /// For high fidelity comparisons, it is recommended to keep using the Duration structure. |
467 | | #[must_use] |
468 | | #[cfg_attr(kani, kani::ensures(|result: &f64| result.is_finite() && result.abs() < 1.1e14))] |
469 | 1.69M | pub fn to_seconds(&self) -> f64 { |
470 | | // Compute the seconds and nanoseconds that we know this fits on a 64bit float |
471 | 1.69M | let seconds = self.nanoseconds.div_euclid(NANOSECONDS_PER_SECOND); |
472 | 1.69M | let subseconds = self.nanoseconds.rem_euclid(NANOSECONDS_PER_SECOND); |
473 | 1.69M | if self.centuries == 0 { |
474 | 1.58M | (seconds as f64) + (subseconds as f64) * 1e-9 |
475 | | } else { |
476 | 108k | f64::from(self.centuries) * SECONDS_PER_CENTURY |
477 | 108k | + (seconds as f64) |
478 | 108k | + (subseconds as f64) * 1e-9 |
479 | | } |
480 | 1.69M | } |
481 | | |
482 | | #[must_use] |
483 | 377k | pub fn to_unit(&self, unit: Unit) -> f64 { |
484 | 377k | self.to_seconds() * unit.from_seconds() |
485 | 377k | } |
486 | | |
487 | | /// Returns the absolute value of this duration |
488 | | #[must_use] |
489 | | #[cfg_attr(kani, kani::ensures(|result: &Self| !result.centuries.is_negative()))] |
490 | 96.2k | pub fn abs(&self) -> Self { |
491 | 96.2k | if self.centuries.is_negative() { |
492 | 2.14k | -*self |
493 | | } else { |
494 | 94.0k | *self |
495 | | } |
496 | 96.2k | } |
497 | | |
498 | | /// Returns the sign of this duration |
499 | | /// + 0 if the number is zero |
500 | | /// + 1 if the number is positive |
501 | | /// + -1 if the number is negative |
502 | | #[must_use] |
503 | | #[cfg_attr(kani, kani::ensures(|result| *result == -1 || *result == 0 || *result == 1))] |
504 | 98.3k | pub const fn signum(&self) -> i8 { |
505 | 98.3k | self.centuries.signum() as i8 |
506 | 98.3k | } |
507 | | |
508 | | /// Decomposes a Duration in its sign, days, hours, minutes, seconds, ms, us, ns |
509 | | #[must_use] |
510 | | #[cfg_attr(kani, kani::ensures(|result: &(i8, u64, u64, u64, u64, u64, u64, u64)| { |
511 | | let (sign, _days, hours, minutes, seconds, ms, us, ns) = *result; |
512 | | (sign == -1 || sign == 0 || sign == 1) |
513 | | && hours < 24 |
514 | | && minutes < 60 |
515 | | && seconds < 60 |
516 | | && ms < 1000 |
517 | | && us < 1000 |
518 | | && ns < 1000 |
519 | | }))] |
520 | 53.8k | pub fn decompose(&self) -> (i8, u64, u64, u64, u64, u64, u64, u64) { |
521 | 53.8k | let mut me = *self; |
522 | 53.8k | let sign = me.signum(); |
523 | 53.8k | me = me.abs(); |
524 | 53.8k | let days = me.to_unit(Unit::Day).floor(); |
525 | 53.8k | me -= days.days(); |
526 | 53.8k | let hours = me.to_unit(Unit::Hour).floor(); |
527 | 53.8k | me -= hours.hours(); |
528 | 53.8k | let minutes = me.to_unit(Unit::Minute).floor(); |
529 | 53.8k | me -= minutes.minutes(); |
530 | 53.8k | let seconds = me.to_unit(Unit::Second).floor(); |
531 | 53.8k | me -= seconds.seconds(); |
532 | 53.8k | let milliseconds = me.to_unit(Unit::Millisecond).floor(); |
533 | 53.8k | me -= milliseconds.milliseconds(); |
534 | 53.8k | let microseconds = me.to_unit(Unit::Microsecond).floor(); |
535 | 53.8k | me -= microseconds.microseconds(); |
536 | 53.8k | let nanoseconds = me.to_unit(Unit::Nanosecond).round(); |
537 | | |
538 | | // Everything should fit in the expected types now |
539 | 53.8k | ( |
540 | 53.8k | sign, |
541 | 53.8k | days as u64, |
542 | 53.8k | hours as u64, |
543 | 53.8k | minutes as u64, |
544 | 53.8k | seconds as u64, |
545 | 53.8k | milliseconds as u64, |
546 | 53.8k | microseconds as u64, |
547 | 53.8k | nanoseconds as u64, |
548 | 53.8k | ) |
549 | 53.8k | } |
550 | | |
551 | | /// Returns the subdivision of duration in this unit, if such is available. Does not work with Week or Century. |
552 | | /// |
553 | | /// # Example |
554 | | /// ``` |
555 | | /// use hifitime::{Duration, TimeUnits, Unit}; |
556 | | /// |
557 | | /// let two_hours_three_min = 2.hours() + 3.minutes(); |
558 | | /// assert_eq!(two_hours_three_min.subdivision(Unit::Hour), Some(2.hours())); |
559 | | /// assert_eq!(two_hours_three_min.subdivision(Unit::Minute), Some(3.minutes())); |
560 | | /// assert_eq!(two_hours_three_min.subdivision(Unit::Second), Some(Duration::ZERO)); |
561 | | /// assert_eq!(two_hours_three_min.subdivision(Unit::Week), None); |
562 | | /// ``` |
563 | | #[must_use] |
564 | | #[cfg_attr(kani, kani::ensures(|result: &Option<Duration>| { |
565 | | match result { |
566 | | Some(d) => { |
567 | | d.nanoseconds < NANOSECONDS_PER_CENTURY |
568 | | || d.parts_are_equal(Self::MAX) |
569 | | || d.parts_are_equal(Self::MIN) |
570 | | } |
571 | | None => true, |
572 | | } |
573 | | }))] |
574 | 49.3k | pub fn subdivision(&self, unit: Unit) -> Option<Duration> { |
575 | 49.3k | let (_, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds) = |
576 | 49.3k | self.decompose(); |
577 | | |
578 | 49.3k | match unit { |
579 | 0 | Unit::Nanosecond => Some((nanoseconds as i64) * unit), |
580 | 0 | Unit::Microsecond => Some((microseconds as i64) * unit), |
581 | 0 | Unit::Millisecond => Some((milliseconds as i64) * unit), |
582 | 49.3k | Unit::Second => Some((seconds as i64) * unit), |
583 | 0 | Unit::Minute => Some((minutes as i64) * unit), |
584 | 0 | Unit::Hour => Some((hours as i64) * unit), |
585 | 0 | Unit::Day => Some((days as i64) * unit), |
586 | 0 | Unit::Week | Unit::Century => None, |
587 | | } |
588 | 49.3k | } |
589 | | |
590 | | /// Floors this duration to the closest duration from the bottom |
591 | | /// |
592 | | /// # Example |
593 | | /// ``` |
594 | | /// use hifitime::{Duration, TimeUnits}; |
595 | | /// |
596 | | /// let two_hours_three_min = 2.hours() + 3.minutes(); |
597 | | /// assert_eq!(two_hours_three_min.floor(1.hours()), 2.hours()); |
598 | | /// assert_eq!(two_hours_three_min.floor(30.minutes()), 2.hours()); |
599 | | /// // This is zero because we floor by a duration longer than the current duration, rounding it down |
600 | | /// assert_eq!(two_hours_three_min.floor(4.hours()), 0.hours()); |
601 | | /// assert_eq!(two_hours_three_min.floor(1.seconds()), two_hours_three_min); |
602 | | /// assert_eq!(two_hours_three_min.floor(1.hours() + 1.minutes()), 2.hours() + 2.minutes()); |
603 | | /// assert_eq!(two_hours_three_min.floor(1.hours() + 5.minutes()), 1.hours() + 5.minutes()); |
604 | | /// ``` |
605 | | #[cfg_attr(kani, kani::ensures(|result: &Self| { |
606 | | result.nanoseconds < NANOSECONDS_PER_CENTURY |
607 | | || result.parts_are_equal(Self::MAX) |
608 | | || result.parts_are_equal(Self::MIN) |
609 | | }))] |
610 | 42.3k | pub fn floor(&self, duration: Self) -> Self { |
611 | 42.3k | Self::from_total_nanoseconds(if duration.total_nanoseconds() == 0 { |
612 | 0 | 0 |
613 | | } else { |
614 | 42.3k | self.total_nanoseconds() - self.total_nanoseconds() % duration.total_nanoseconds() |
615 | | }) |
616 | 42.3k | } |
617 | | |
618 | | /// Ceils this duration to the closest provided duration |
619 | | /// |
620 | | /// This simply floors then adds the requested duration |
621 | | /// |
622 | | /// # Example |
623 | | /// ``` |
624 | | /// use hifitime::{Duration, TimeUnits}; |
625 | | /// |
626 | | /// let two_hours_three_min = 2.hours() + 3.minutes(); |
627 | | /// assert_eq!(two_hours_three_min.ceil(1.hours()), 3.hours()); |
628 | | /// assert_eq!(two_hours_three_min.ceil(30.minutes()), 2.hours() + 30.minutes()); |
629 | | /// assert_eq!(two_hours_three_min.ceil(4.hours()), 4.hours()); |
630 | | /// assert_eq!(two_hours_three_min.ceil(1.seconds()), two_hours_three_min + 1.seconds()); |
631 | | /// assert_eq!(two_hours_three_min.ceil(1.hours() + 5.minutes()), 2.hours() + 10.minutes()); |
632 | | /// ``` |
633 | 21.1k | pub fn ceil(&self, duration: Self) -> Self { |
634 | 21.1k | let floored = self.floor(duration); |
635 | 21.1k | match floored |
636 | 21.1k | .total_nanoseconds() |
637 | 21.1k | .checked_add(duration.abs().total_nanoseconds()) |
638 | | { |
639 | 21.1k | Some(total_ns) => Self::from_total_nanoseconds(total_ns), |
640 | 0 | None => Self::MAX, |
641 | | } |
642 | 21.1k | } |
643 | | |
644 | | /// Rounds this duration to the closest provided duration |
645 | | /// |
646 | | /// This performs both a `ceil` and `floor` and returns the value which is the closest to current one. |
647 | | /// # Example |
648 | | /// ``` |
649 | | /// use hifitime::{Duration, TimeUnits}; |
650 | | /// |
651 | | /// let two_hours_three_min = 2.hours() + 3.minutes(); |
652 | | /// assert_eq!(two_hours_three_min.round(1.hours()), 2.hours()); |
653 | | /// assert_eq!(two_hours_three_min.round(30.minutes()), 2.hours()); |
654 | | /// assert_eq!(two_hours_three_min.round(4.hours()), 4.hours()); |
655 | | /// assert_eq!(two_hours_three_min.round(1.seconds()), two_hours_three_min); |
656 | | /// assert_eq!(two_hours_three_min.round(1.hours() + 5.minutes()), 2.hours() + 10.minutes()); |
657 | | /// ``` |
658 | 21.1k | pub fn round(&self, duration: Self) -> Self { |
659 | 21.1k | let floored = self.floor(duration); |
660 | 21.1k | let ceiled = self.ceil(duration); |
661 | 21.1k | if *self - floored < (ceiled - *self).abs() { |
662 | 17.6k | floored |
663 | | } else { |
664 | 3.54k | ceiled |
665 | | } |
666 | 21.1k | } |
667 | | |
668 | | /// Rounds this duration to the largest units represented in this duration. |
669 | | /// |
670 | | /// This is useful to provide an approximate human duration. Under the hood, this function uses `round`, |
671 | | /// so the "tipping point" of the rounding is half way to the next increment of the greatest unit. |
672 | | /// As shown below, one example is that 35 hours and 59 minutes rounds to 1 day, but 36 hours and 1 minute rounds |
673 | | /// to 2 days because 2 days is closer to 36h 1 min than 36h 1 min is to 1 day. |
674 | | /// |
675 | | /// # Example |
676 | | /// |
677 | | /// ``` |
678 | | /// use hifitime::{Duration, TimeUnits}; |
679 | | /// |
680 | | /// assert_eq!((2.hours() + 3.minutes()).approx(), 2.hours()); |
681 | | /// assert_eq!((24.hours() + 3.minutes()).approx(), 1.days()); |
682 | | /// assert_eq!((35.hours() + 59.minutes()).approx(), 1.days()); |
683 | | /// assert_eq!((36.hours() + 1.minutes()).approx(), 2.days()); |
684 | | /// assert_eq!((47.hours() + 3.minutes()).approx(), 2.days()); |
685 | | /// assert_eq!((49.hours() + 3.minutes()).approx(), 2.days()); |
686 | | /// ``` |
687 | 0 | pub fn approx(&self) -> Self { |
688 | 0 | let (_, days, hours, minutes, seconds, milli, us, _) = self.decompose(); |
689 | | |
690 | 0 | let round_to = if days > 0 { |
691 | 0 | 1 * Unit::Day |
692 | 0 | } else if hours > 0 { |
693 | 0 | 1 * Unit::Hour |
694 | 0 | } else if minutes > 0 { |
695 | 0 | 1 * Unit::Minute |
696 | 0 | } else if seconds > 0 { |
697 | 0 | 1 * Unit::Second |
698 | 0 | } else if milli > 0 { |
699 | 0 | 1 * Unit::Millisecond |
700 | 0 | } else if us > 0 { |
701 | 0 | 1 * Unit::Microsecond |
702 | | } else { |
703 | 0 | 1 * Unit::Nanosecond |
704 | | }; |
705 | | |
706 | 0 | self.round(round_to) |
707 | 0 | } |
708 | | |
709 | | // Returns the minimum of the two durations. |
710 | | /// |
711 | | /// ``` |
712 | | /// use hifitime::TimeUnits; |
713 | | /// |
714 | | /// let d0 = 20.seconds(); |
715 | | /// let d1 = 21.seconds(); |
716 | | /// |
717 | | /// assert_eq!(d0, d1.min(d0)); |
718 | | /// assert_eq!(d0, d0.min(d1)); |
719 | | /// ``` |
720 | | #[cfg_attr(kani, kani::ensures(|result: &Self| *result <= self && *result <= other && (*result == self || *result == other)))] |
721 | 0 | pub fn min(self, other: Self) -> Self { |
722 | 0 | if self < other { |
723 | 0 | self |
724 | | } else { |
725 | 0 | other |
726 | | } |
727 | 0 | } |
728 | | |
729 | | /// Returns the maximum of the two durations. |
730 | | /// |
731 | | /// ``` |
732 | | /// use hifitime::TimeUnits; |
733 | | /// |
734 | | /// let d0 = 20.seconds(); |
735 | | /// let d1 = 21.seconds(); |
736 | | /// |
737 | | /// assert_eq!(d1, d1.max(d0)); |
738 | | /// assert_eq!(d1, d0.max(d1)); |
739 | | /// ``` |
740 | | #[cfg_attr(kani, kani::ensures(|result: &Self| *result >= self && *result >= other && (*result == self || *result == other)))] |
741 | 0 | pub fn max(self, other: Self) -> Self { |
742 | 0 | if self > other { |
743 | 0 | self |
744 | | } else { |
745 | 0 | other |
746 | | } |
747 | 0 | } |
748 | | |
749 | | /// Returns whether this is a negative or positive duration. |
750 | | #[cfg_attr(kani, kani::ensures(|result| *result == self.centuries.is_negative()))] |
751 | 0 | pub const fn is_negative(&self) -> bool { |
752 | 0 | self.centuries.is_negative() |
753 | 0 | } |
754 | | } |
755 | | |
756 | | impl fmt::Display for Duration { |
757 | | // Prints this duration with automatic selection of the units, i.e. everything that isn't zero is ignored |
758 | 11.6k | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
759 | 11.6k | if self.total_nanoseconds() == 0 { |
760 | 7.09k | write!(f, "0 ns") |
761 | | } else { |
762 | 4.58k | let (sign, days, hours, minutes, seconds, milli, us, nano) = self.decompose(); |
763 | 4.58k | if sign == -1 { |
764 | 2.14k | write!(f, "-")?; |
765 | 2.43k | } |
766 | | |
767 | 4.58k | let values = [days, hours, minutes, seconds, milli, us, nano]; |
768 | 4.58k | let units = [ |
769 | 4.58k | if days > 1 { "days" } else { "day" }, |
770 | 4.58k | "h", |
771 | 4.58k | "min", |
772 | 4.58k | "s", |
773 | 4.58k | "ms", |
774 | 4.58k | "μs", |
775 | 4.58k | "ns", |
776 | | ]; |
777 | | |
778 | 4.58k | let mut insert_space = false; |
779 | 32.0k | for (val, unit) in values.iter().zip(units.iter()) { |
780 | 32.0k | if *val > 0 { |
781 | 7.91k | if insert_space { |
782 | 3.33k | write!(f, " ")?; |
783 | 4.58k | } |
784 | 7.91k | write!(f, "{val} {unit}")?; |
785 | 7.91k | insert_space = true; |
786 | 24.1k | } |
787 | | } |
788 | 4.58k | Ok(()) |
789 | | } |
790 | 11.6k | } |
791 | | } |
792 | | |
793 | | impl fmt::LowerExp for Duration { |
794 | | // Prints the duration with appropriate units |
795 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
796 | 0 | let seconds_f64 = self.to_seconds(); |
797 | 0 | let seconds_f64_abs = seconds_f64.abs(); |
798 | 0 | if seconds_f64_abs < 1e-5 { |
799 | 0 | fmt::Display::fmt(&(seconds_f64 * 1e9), f)?; |
800 | 0 | write!(f, " ns") |
801 | 0 | } else if seconds_f64_abs < 1e-2 { |
802 | 0 | fmt::Display::fmt(&(seconds_f64 * 1e3), f)?; |
803 | 0 | write!(f, " ms") |
804 | 0 | } else if seconds_f64_abs < 3.0 * SECONDS_PER_MINUTE { |
805 | 0 | fmt::Display::fmt(&(seconds_f64), f)?; |
806 | 0 | write!(f, " s") |
807 | 0 | } else if seconds_f64_abs < SECONDS_PER_HOUR { |
808 | 0 | fmt::Display::fmt(&(seconds_f64 / SECONDS_PER_MINUTE), f)?; |
809 | 0 | write!(f, " min") |
810 | 0 | } else if seconds_f64_abs < SECONDS_PER_DAY { |
811 | 0 | fmt::Display::fmt(&(seconds_f64 / SECONDS_PER_HOUR), f)?; |
812 | 0 | write!(f, " h") |
813 | | } else { |
814 | 0 | fmt::Display::fmt(&(seconds_f64 / SECONDS_PER_DAY), f)?; |
815 | 0 | write!(f, " days") |
816 | | } |
817 | 0 | } |
818 | | } |
819 | | |
820 | | impl PartialEq<Unit> for Duration { |
821 | | #[allow(clippy::identity_op)] |
822 | 0 | fn eq(&self, unit: &Unit) -> bool { |
823 | 0 | *self == *unit * 1 |
824 | 0 | } |
825 | | } |
826 | | |
827 | | impl PartialOrd<Unit> for Duration { |
828 | | #[allow(clippy::identity_op, clippy::comparison_chain)] |
829 | 0 | fn partial_cmp(&self, unit: &Unit) -> Option<Ordering> { |
830 | 0 | let unit_deref = *unit; |
831 | 0 | let unit_as_duration: Duration = unit_deref * 1; |
832 | 0 | if self < &unit_as_duration { |
833 | 0 | Some(Ordering::Less) |
834 | 0 | } else if self > &unit_as_duration { |
835 | 0 | Some(Ordering::Greater) |
836 | | } else { |
837 | 0 | Some(Ordering::Equal) |
838 | | } |
839 | 0 | } |
840 | | } |
841 | | |
842 | | impl From<Duration> for core::time::Duration { |
843 | | /// Converts a [`Duration`] into a [`core::time::Duration`] |
844 | | /// |
845 | | /// # Limitations |
846 | | /// 1. If the [`Duration`] is negative, this will return a [`core::time::Duration::ZERO`]. |
847 | | /// 2. If the [`Duration`] is [`Duration::MAX`], this will return the equivalent of [`core::time::Duration::from_secs(103407943680000)`] |
848 | 0 | fn from(hf_duration: Duration) -> Self { |
849 | | use crate::NANOSECONDS_PER_SECOND; |
850 | 0 | if hf_duration.signum().is_negative() { |
851 | 0 | core::time::Duration::ZERO |
852 | | } else { |
853 | 0 | let unsigned_nanos = hf_duration.total_nanoseconds() as u128; |
854 | 0 | let secs: u64 = (unsigned_nanos / NANOSECONDS_PER_SECOND as u128) |
855 | 0 | .try_into() |
856 | 0 | .unwrap_or(u64::MAX); |
857 | 0 | let subsec_nanos = (unsigned_nanos % NANOSECONDS_PER_SECOND as u128) as u32; |
858 | | |
859 | 0 | core::time::Duration::new(secs, subsec_nanos) |
860 | | } |
861 | 0 | } |
862 | | } |
863 | | |
864 | | impl From<core::time::Duration> for Duration { |
865 | | /// Converts a [`core::time::Duration`] into a [`Duration`] |
866 | | /// |
867 | | /// # Limitations |
868 | | /// 1. If the [`core::time::Duration`] is larger than [`Duration::MAX`], this will return [`Duration::MAX`] |
869 | 23.9k | fn from(core_duration: core::time::Duration) -> Self { |
870 | 23.9k | Duration::from_total_nanoseconds(core_duration.as_nanos() as i128) |
871 | 23.9k | } |
872 | | } |
873 | | |
874 | | #[cfg(test)] |
875 | | mod ut_duration { |
876 | | use super::{Duration, TimeUnits, Unit, NANOSECONDS_PER_CENTURY}; |
877 | | |
878 | | #[test] |
879 | | #[cfg(feature = "serde")] |
880 | | fn test_serdes() { |
881 | | for (dt, content) in [ |
882 | | (Duration::from_seconds(10.1), r#""10 s 100 ms""#), |
883 | | (1.0_f64.days() + 99.nanoseconds(), r#""1 day 99 ns""#), |
884 | | ( |
885 | | 1.0_f64.centuries() + 99.seconds(), |
886 | | r#""36525 days 1 min 39 s""#, |
887 | | ), |
888 | | ] { |
889 | | assert_eq!(content, serde_json::to_string(&dt).unwrap()); |
890 | | let parsed: Duration = serde_json::from_str(content).unwrap(); |
891 | | assert_eq!(dt, parsed); |
892 | | } |
893 | | } |
894 | | |
895 | | #[test] |
896 | | fn test_bounds() { |
897 | | let min = Duration::MIN; |
898 | | assert_eq!(min.centuries, i16::MIN); |
899 | | assert_eq!(min.nanoseconds, 0); |
900 | | |
901 | | let max = Duration::MAX; |
902 | | assert_eq!(max.centuries, i16::MAX); |
903 | | assert_eq!(max.nanoseconds, NANOSECONDS_PER_CENTURY); |
904 | | |
905 | | let min_p = Duration::MIN_POSITIVE; |
906 | | assert_eq!(min_p.centuries, 0); |
907 | | assert_eq!(min_p.nanoseconds, 1); |
908 | | |
909 | | let min_n = Duration::MIN_NEGATIVE; |
910 | | assert_eq!(min_n.centuries, -1); |
911 | | assert_eq!(min_n.nanoseconds, NANOSECONDS_PER_CENTURY - 1); |
912 | | |
913 | | let min_n1 = Duration::MIN - 1 * Unit::Nanosecond; |
914 | | assert_eq!(min_n1, Duration::MIN); |
915 | | |
916 | | let max_n1 = Duration::MAX - 1 * Unit::Nanosecond; |
917 | | assert_eq!(max_n1.centuries, i16::MAX); |
918 | | assert_eq!(max_n1.nanoseconds, NANOSECONDS_PER_CENTURY - 1); |
919 | | } |
920 | | |
921 | | #[test] |
922 | | fn test_decompose() { |
923 | | let d = -73000.days(); |
924 | | let out_days = d.to_unit(Unit::Day); |
925 | | assert_eq!(out_days, -73000.0); |
926 | | let (sign, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds) = |
927 | | d.decompose(); |
928 | | assert_eq!(sign, -1); |
929 | | assert_eq!(days, 73000); |
930 | | assert_eq!(hours, 0); |
931 | | assert_eq!(minutes, 0); |
932 | | assert_eq!(seconds, 0); |
933 | | assert_eq!(milliseconds, 0); |
934 | | assert_eq!(microseconds, 0); |
935 | | assert_eq!(nanoseconds, 0); |
936 | | } |
937 | | |
938 | | #[test] |
939 | | fn test_conversion() { |
940 | | let d = Duration::MIN; |
941 | | let core_d: core::time::Duration = d.into(); |
942 | | assert_eq!(core_d, core::time::Duration::ZERO); |
943 | | |
944 | | let d = Duration::MAX; |
945 | | let core_d: core::time::Duration = d.into(); |
946 | | assert_eq!(core_d, core::time::Duration::from_secs(103407943680000)); |
947 | | } |
948 | | } |