Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Versions with only mechanical changes will be omitted from the following list.
* Implement `DurationRound` for `NaiveDateTime`
* Add `DateTime::from_local()` to construct from given local date and time (#572)
* Correct build for wasm32-unknown-emscripten target (#568)
* Make `Duration.seconds()` and siblings const fn

## 0.4.19

Expand Down
116 changes: 92 additions & 24 deletions src/oldtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,69 +70,87 @@ impl Duration {
/// Equivalent to `Duration::seconds(weeks * 7 * 24 * 60 * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
pub fn weeks(weeks: i64) -> Duration {
let secs = weeks.checked_mul(SECS_PER_WEEK).expect("Duration::weeks out of bounds");
Duration::seconds(secs)
pub const fn weeks(weeks: i64) -> Duration {
let secs = weeks.checked_mul(SECS_PER_WEEK);
if let Some(secs) = secs {
Duration::seconds(secs)
} else {
panic!("Duration::weeks out of bounds");
}
}

/// Makes a new `Duration` with given number of days.
/// Equivalent to `Duration::seconds(days * 24 * 60 * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
pub fn days(days: i64) -> Duration {
let secs = days.checked_mul(SECS_PER_DAY).expect("Duration::days out of bounds");
Duration::seconds(secs)
pub const fn days(days: i64) -> Duration {
let secs = days.checked_mul(SECS_PER_DAY);
if let Some(secs) = secs {
Duration::seconds(secs)
} else {
panic!("Duration::days out of bounds");
}
}

/// Makes a new `Duration` with given number of hours.
/// Equivalent to `Duration::seconds(hours * 60 * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
pub fn hours(hours: i64) -> Duration {
let secs = hours.checked_mul(SECS_PER_HOUR).expect("Duration::hours ouf of bounds");
Duration::seconds(secs)
pub const fn hours(hours: i64) -> Duration {
let secs = hours.checked_mul(SECS_PER_HOUR);
if let Some(secs) = secs {
Duration::seconds(secs)
} else {
panic!("Duration::hours out of bounds");
}
}

/// Makes a new `Duration` with given number of minutes.
/// Equivalent to `Duration::seconds(minutes * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
pub fn minutes(minutes: i64) -> Duration {
let secs = minutes.checked_mul(SECS_PER_MINUTE).expect("Duration::minutes out of bounds");
Duration::seconds(secs)
pub const fn minutes(minutes: i64) -> Duration {
let secs = minutes.checked_mul(SECS_PER_MINUTE);
if let Some(secs) = secs {
Duration::seconds(secs)
} else {
panic!("Duration::minutes out of bounds");
}
}

/// Makes a new `Duration` with given number of seconds.
/// Panics when the duration is more than `i64::MAX` seconds
/// or less than `i64::MIN` seconds.
#[inline]
pub fn seconds(seconds: i64) -> Duration {
pub const fn seconds(seconds: i64) -> Duration {
let d = Duration { secs: seconds, nanos: 0 };
if d < MIN || d > MAX {
// Using the equivalent d < MIN || d > MAX isn't const
if d.out_of_bounds() {
panic!("Duration::seconds out of bounds");
} else {
d
}
d
}

/// Makes a new `Duration` with given number of milliseconds.
#[inline]
pub fn milliseconds(milliseconds: i64) -> Duration {
pub const fn milliseconds(milliseconds: i64) -> Duration {
let (secs, millis) = div_mod_floor_64(milliseconds, MILLIS_PER_SEC);
let nanos = millis as i32 * NANOS_PER_MILLI;
Duration { secs: secs, nanos: nanos }
}

/// Makes a new `Duration` with given number of microseconds.
#[inline]
pub fn microseconds(microseconds: i64) -> Duration {
pub const fn microseconds(microseconds: i64) -> Duration {
let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
let nanos = micros as i32 * NANOS_PER_MICRO;
Duration { secs: secs, nanos: nanos }
}

/// Makes a new `Duration` with given number of nanoseconds.
#[inline]
pub fn nanoseconds(nanos: i64) -> Duration {
pub const fn nanoseconds(nanos: i64) -> Duration {
let (secs, nanos) = div_mod_floor_64(nanos, NANOS_PER_SEC as i64);
Duration { secs: secs, nanos: nanos as i32 }
}
Expand Down Expand Up @@ -276,13 +294,13 @@ impl Duration {
///
/// This function errors when original duration is larger than the maximum
/// value supported for this type.
pub fn from_std(duration: StdDuration) -> Result<Duration, OutOfRangeError> {
pub const fn from_std(duration: StdDuration) -> Result<Duration, OutOfRangeError> {
// We need to check secs as u64 before coercing to i64
if duration.as_secs() > MAX.secs as u64 {
return Err(OutOfRangeError(()));
}
let d = Duration { secs: duration.as_secs() as i64, nanos: duration.subsec_nanos() as i32 };
if d > MAX {
if d.out_of_bounds() {
return Err(OutOfRangeError(()));
}
Ok(d)
Expand All @@ -298,6 +316,14 @@ impl Duration {
}
Ok(StdDuration::new(self.secs as u64, self.nanos as u32))
}

/// Returns true if and only if d < MIN || d > MAX
const fn out_of_bounds(&self) -> bool {
self.secs < MIN.secs
|| self.secs > MAX.secs
|| (self.secs == MAX.secs && self.nanos > MAX.nanos)
|| (self.secs == MIN.secs && self.nanos < MIN.nanos)
}
}

impl Neg for Duration {
Expand Down Expand Up @@ -432,28 +458,28 @@ impl Error for OutOfRangeError {

// Copied from libnum
#[inline]
fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) {
const fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) {
(div_floor_64(this, other), mod_floor_64(this, other))
}

#[inline]
fn div_floor_64(this: i64, other: i64) -> i64 {
const fn div_floor_64(this: i64, other: i64) -> i64 {
match div_rem_64(this, other) {
(d, r) if (r > 0 && other < 0) || (r < 0 && other > 0) => d - 1,
(d, _) => d,
}
}

#[inline]
fn mod_floor_64(this: i64, other: i64) -> i64 {
const fn mod_floor_64(this: i64, other: i64) -> i64 {
match this % other {
r if (r > 0 && other < 0) || (r < 0 && other > 0) => r + other,
r => r,
}
}

#[inline]
fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
const fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
(this / other, this % other)
}

Expand Down Expand Up @@ -685,4 +711,46 @@ mod tests {
Err(OutOfRangeError(()))
);
}

#[test]
fn test_duration_const() {
const ONE_WEEK: Duration = Duration::weeks(1);
const ONE_DAY: Duration = Duration::days(1);
const ONE_HOUR: Duration = Duration::hours(1);
const ONE_MINUTE: Duration = Duration::minutes(1);
const ONE_SECOND: Duration = Duration::seconds(1);
const ONE_MILLI: Duration = Duration::milliseconds(1);
const ONE_MICRO: Duration = Duration::microseconds(1);
const ONE_NANO: Duration = Duration::nanoseconds(1);
let combo: Duration = ONE_WEEK
+ ONE_DAY
+ ONE_HOUR
+ ONE_MINUTE
+ ONE_SECOND
+ ONE_MILLI
+ ONE_MICRO
+ ONE_NANO;

assert!(ONE_WEEK != Duration::zero());
assert!(ONE_DAY != Duration::zero());
assert!(ONE_HOUR != Duration::zero());
assert!(ONE_MINUTE != Duration::zero());
assert!(ONE_SECOND != Duration::zero());
assert!(ONE_MILLI != Duration::zero());
assert!(ONE_MICRO != Duration::zero());
assert!(ONE_NANO != Duration::zero());
assert_eq!(
combo,
Duration::seconds(86400 * 7 + 86400 + 3600 + 60 + 1)
+ Duration::nanoseconds(1 + 1_000 + 1_000_000)
);
}

#[test]
fn test_from_std_const() {
const ONE_SECOND: StdDuration = StdDuration::from_secs(1);
const ONE_SECOND_CHRONO: Result<Duration, OutOfRangeError> = Duration::from_std(ONE_SECOND);

assert_eq!(Ok(Duration::seconds(1)), ONE_SECOND_CHRONO);
}
}