Skip to content

Commit cd80a54

Browse files
committed
Add duration_float methods
This adds `Duration::{as_secs_f64, as_secs_f32, from_secs_f64, from_secs_f32, mul_f64, mul_f32, div_f64, div_f32}` methods. They are based on `duration_float`](rust-lang/rust#54361) feature of the standard library that stabilized on Rust 1.38.
1 parent d76a82c commit cd80a54

File tree

4 files changed

+179
-5
lines changed

4 files changed

+179
-5
lines changed

CHANGELOG.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@ This project adheres to [Semantic Versioning](https://semver.org).
66

77
## [Unreleased]
88

9+
- Add `Duration::{as_secs_f64, as_secs_f32, from_secs_f64, from_secs_f32,
10+
mul_f64, mul_f32, div_f64, div_f32}` methods.
11+
They are based on [`duration_float`](https://github.com/rust-lang/rust/issues/54361)
12+
feature of the standard library that stabilized on Rust 1.38.
13+
914
- Make `Duration::{as_secs, subsec_millis, subsec_micros, subsec_nanos,
1015
as_millis, as_micros, as_nanos, is_some, is_none, unwrap_or}` const function
1116
on rustc 1.46+.
1217

13-
- Make `Instant::{is_some, is_none, unwrap_or}` const function on rustc 1.46+.
18+
- Make `Instant::{is_some, is_none, unwrap_or}` const function on Rust 1.46+.
1419

1520
- Implement `TryFrom` for `easytime::Instant` and `easytime::Duration`. With
1621
this change, the minimum required version of `easytime` with `--no-default-features` goes up to Rust 1.34 (the minimum required version of the default feature has not changed).

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ fn foo(secs: u64, nanos: u32, instant: Instant) -> Option<Duration> {
5555
let now = Instant::now();
5656

5757
let secs = Duration::from_secs(secs);
58-
let nanos = Duration::from_nanos(u64::from(nanos));
58+
let nanos = Duration::from_nanos(nanos as u64);
5959

6060
let dur = secs.checked_add(nanos)?;
6161
now.checked_duration_since(instant)?.checked_sub(dur)

src/duration.rs

+171-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use core::{
88

99
use super::{pair_and_then, TryFromTimeError};
1010

11+
const NANOS_PER_SEC: u32 = 1_000_000_000;
12+
1113
/// A `Duration` type to represent a span of time, typically used for system
1214
/// timeouts.
1315
///
@@ -28,7 +30,6 @@ pub struct Duration(pub(crate) Option<time::Duration>);
2830
impl Duration {
2931
// TODO: duration_constants https://github.com/rust-lang/rust/issues/57391
3032
// TODO: duration_zero https://github.com/rust-lang/rust/issues/73544
31-
// TODO: duration_float https://github.com/rust-lang/rust/issues/54361
3233
// TODO: div_duration https://github.com/rust-lang/rust/issues/63139
3334

3435
/// Creates a new `Duration` from the specified number of whole seconds and
@@ -39,7 +40,7 @@ impl Duration {
3940
#[inline]
4041
pub fn new(secs: u64, nanos: u32) -> Self {
4142
let secs = time::Duration::from_secs(secs);
42-
let nanos = time::Duration::from_nanos(u64::from(nanos));
43+
let nanos = time::Duration::from_nanos(nanos as u64);
4344
Self(secs.checked_add(nanos))
4445
}
4546

@@ -154,6 +155,174 @@ impl Duration {
154155
}
155156
}
156157

158+
/// Returns the number of seconds contained by this `Duration` as `f64`.
159+
///
160+
/// The returned value does include the fractional (nanosecond) part of the duration.
161+
///
162+
/// # Examples
163+
///
164+
/// ```
165+
/// use easytime::Duration;
166+
///
167+
/// let dur = Duration::new(2, 700_000_000);
168+
/// assert_eq!(dur.as_secs_f64(), Some(2.7));
169+
/// ```
170+
#[inline]
171+
pub fn as_secs_f64(&self) -> Option<f64> {
172+
// TODO: replace with `self.0.as_ref().map(time::Duration::as_secs_f64)` on Rust 1.38+.
173+
self.0.map(|this| {
174+
(this.as_secs() as f64) + (this.subsec_nanos() as f64) / (NANOS_PER_SEC as f64)
175+
})
176+
}
177+
178+
/// Returns the number of seconds contained by this `Duration` as `f32`.
179+
///
180+
/// The returned value does include the fractional (nanosecond) part of the duration.
181+
///
182+
/// # Examples
183+
///
184+
/// ```
185+
/// use easytime::Duration;
186+
///
187+
/// let dur = Duration::new(2, 700_000_000);
188+
/// assert_eq!(dur.as_secs_f32(), Some(2.7));
189+
/// ```
190+
#[inline]
191+
pub fn as_secs_f32(&self) -> Option<f32> {
192+
// TODO: replace with `self.0.as_ref().map(time::Duration::as_secs_f32)` on Rust 1.38+.
193+
self.0.map(|this| {
194+
(this.as_secs() as f32) + (this.subsec_nanos() as f32) / (NANOS_PER_SEC as f32)
195+
})
196+
}
197+
198+
/// Creates a new `Duration` from the specified number of seconds represented
199+
/// as `f64`.
200+
///
201+
/// # Examples
202+
///
203+
/// ```
204+
/// use easytime::Duration;
205+
///
206+
/// let dur = Duration::from_secs_f64(2.7);
207+
/// assert_eq!(dur, Duration::new(2, 700_000_000));
208+
/// ```
209+
#[inline]
210+
pub fn from_secs_f64(secs: f64) -> Self {
211+
const MAX_NANOS_F64: f64 =
212+
((u64::max_value() as u128 + 1) * (NANOS_PER_SEC as u128)) as f64;
213+
let nanos = secs * (NANOS_PER_SEC as f64);
214+
if !nanos.is_finite() || nanos >= MAX_NANOS_F64 || nanos < 0.0 {
215+
return Self(None);
216+
}
217+
let nanos = nanos as u128;
218+
Self::new(
219+
(nanos / (NANOS_PER_SEC as u128)) as u64,
220+
(nanos % (NANOS_PER_SEC as u128)) as u32,
221+
)
222+
}
223+
224+
/// Creates a new `Duration` from the specified number of seconds represented
225+
/// as `f32`.
226+
///
227+
/// # Examples
228+
///
229+
/// ```
230+
/// use easytime::Duration;
231+
///
232+
/// let dur = Duration::from_secs_f32(2.7);
233+
/// assert_eq!(dur, Duration::new(2, 700_000_000));
234+
/// ```
235+
#[inline]
236+
pub fn from_secs_f32(secs: f32) -> Duration {
237+
const MAX_NANOS_F32: f32 =
238+
((u64::max_value() as u128 + 1) * (NANOS_PER_SEC as u128)) as f32;
239+
let nanos = secs * (NANOS_PER_SEC as f32);
240+
if !nanos.is_finite() || nanos >= MAX_NANOS_F32 || nanos < 0.0 {
241+
return Self(None);
242+
}
243+
let nanos = nanos as u128;
244+
Self::new(
245+
(nanos / (NANOS_PER_SEC as u128)) as u64,
246+
(nanos % (NANOS_PER_SEC as u128)) as u32,
247+
)
248+
}
249+
250+
/// Multiplies `Duration` by `f64`.
251+
///
252+
/// # Examples
253+
///
254+
/// ```
255+
/// use easytime::Duration;
256+
///
257+
/// let dur = Duration::new(2, 700_000_000);
258+
/// assert_eq!(dur.mul_f64(3.14), Duration::new(8, 478_000_000));
259+
/// assert_eq!(dur.mul_f64(3.14e5), Duration::new(847_800, 0));
260+
/// ```
261+
#[inline]
262+
pub fn mul_f64(self, rhs: f64) -> Duration {
263+
self.as_secs_f64()
264+
.map(|secs| Duration::from_secs_f64(rhs * secs))
265+
.unwrap_or_else(|| Self(None))
266+
}
267+
268+
/// Multiplies `Duration` by `f32`.
269+
///
270+
/// # Examples
271+
///
272+
/// ```
273+
/// use easytime::Duration;
274+
///
275+
/// let dur = Duration::new(2, 700_000_000);
276+
/// // note that due to rounding errors result is slightly different
277+
/// // from 8.478 and 847800.0
278+
/// assert_eq!(dur.mul_f32(3.14), Duration::new(8, 478_000_640));
279+
/// assert_eq!(dur.mul_f32(3.14e5), Duration::new(847799, 969_120_256));
280+
/// ```
281+
#[inline]
282+
pub fn mul_f32(self, rhs: f32) -> Duration {
283+
self.as_secs_f32()
284+
.map(|secs| Duration::from_secs_f32(rhs * secs))
285+
.unwrap_or_else(|| Self(None))
286+
}
287+
288+
/// Divide `Duration` by `f64`.
289+
///
290+
/// # Examples
291+
///
292+
/// ```
293+
/// use easytime::Duration;
294+
///
295+
/// let dur = Duration::new(2, 700_000_000);
296+
/// assert_eq!(dur.div_f64(3.14), Duration::new(0, 859_872_611));
297+
/// // note that truncation is used, not rounding
298+
/// assert_eq!(dur.div_f64(3.14e5), Duration::new(0, 8_598));
299+
/// ```
300+
#[inline]
301+
pub fn div_f64(self, rhs: f64) -> Duration {
302+
self.as_secs_f64()
303+
.map(|secs| Duration::from_secs_f64(secs / rhs))
304+
.unwrap_or_else(|| Self(None))
305+
}
306+
307+
/// Divide `Duration` by `f32`.
308+
///
309+
/// # Examples
310+
///
311+
/// ```
312+
/// use easytime::Duration;
313+
///
314+
/// let dur = Duration::new(2, 700_000_000);
315+
/// assert_eq!(dur.div_f64(3.14), Duration::new(0, 859_872_611));
316+
/// // note that truncation is used, not rounding
317+
/// assert_eq!(dur.div_f64(3.14e5), Duration::new(0, 8_598));
318+
/// ```
319+
#[inline]
320+
pub fn div_f32(self, rhs: f32) -> Duration {
321+
self.as_secs_f32()
322+
.map(|secs| Duration::from_secs_f32(secs / rhs))
323+
.unwrap_or_else(|| Self(None))
324+
}
325+
157326
// =============================================================================
158327
// Option based method implementations
159328

src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
//! let now = Instant::now();
3737
//!
3838
//! let secs = Duration::from_secs(secs);
39-
//! let nanos = Duration::from_nanos(u64::from(nanos));
39+
//! let nanos = Duration::from_nanos(nanos as u64);
4040
//!
4141
//! let dur = secs.checked_add(nanos)?;
4242
//! now.checked_duration_since(instant)?.checked_sub(dur)

0 commit comments

Comments
 (0)