1
Fork 0

auto merge of #16626 : ruud-v-a/rust/duration-reform, r=brson

This changes the internal representation of `Duration` from

    days: i32,
    secs: i32,
    nanos: u32

to

    secs: i64,
    nanos: i32

This resolves #16466. Note that `nanos` is an `i32` and not `u32` as suggested, because `i32` is easier to deal with, and it is not exposed anyway. Some methods now take `i64` instead of `i32` due to the increased range. Some methods, like `num_milliseconds`, now return an `Option<i64>` instead of `i64`, because the range of `Duration` is now larger than e.g. 2^63 milliseconds.

A few remarks:
- Negating `MIN` is impossible. I chose to return `MAX` as `-MIN`, but it is one nanosecond less than the actual negation. Is this the desired behaviour?
- In `std::io::timer`, some functions accept a `Duration`, which is internally converted into a number of milliseconds. However, the range of `Duration` is now larger than 2^64 milliseconds. There is already a FIXME in the file that this should be addressed (without a ticket number though). I chose to silently use 0 ms if the duration is too long. Is that right, as long as the backend still uses milliseconds?
- Negative durations are not formatted correctly, but they were not formatted correctly before either.
This commit is contained in:
bors 2014-08-28 22:11:18 +00:00
commit 1a33d7a541
3 changed files with 200 additions and 279 deletions

View file

@ -97,8 +97,8 @@ impl TcpStream {
/// the specified duration. /// the specified duration.
/// ///
/// This is the same as the `connect` method, except that if the timeout /// This is the same as the `connect` method, except that if the timeout
/// specified (in milliseconds) elapses before a connection is made an error /// specified elapses before a connection is made an error will be
/// will be returned. The error's kind will be `TimedOut`. /// returned. The error's kind will be `TimedOut`.
/// ///
/// Note that the `addr` argument may one day be split into a separate host /// Note that the `addr` argument may one day be split into a separate host
/// and port, similar to the API seen in `connect`. /// and port, similar to the API seen in `connect`.

View file

@ -61,7 +61,7 @@ impl UnixStream {
/// Connect to a pipe named by `path`, timing out if the specified number of /// Connect to a pipe named by `path`, timing out if the specified number of
/// milliseconds. /// milliseconds.
/// ///
/// This function is similar to `connect`, except that if `timeout_ms` /// This function is similar to `connect`, except that if `timeout`
/// elapses the function will return an error of kind `TimedOut`. /// elapses the function will return an error of kind `TimedOut`.
/// ///
/// If a `timeout` with zero or negative duration is specified then /// If a `timeout` with zero or negative duration is specified then

View file

@ -12,227 +12,196 @@
#![experimental] #![experimental]
use {fmt, i32}; use {fmt, i64};
use ops::{Add, Sub, Mul, Div, Neg}; use ops::{Add, Sub, Mul, Div, Neg};
use option::{Option, Some, None}; use option::{Option, Some, None};
use num; use num;
use num::{CheckedAdd, CheckedMul}; use num::{CheckedAdd, CheckedMul};
use result::{Result, Ok, Err}; use result::{Result, Ok, Err};
/// The number of nanoseconds in a microsecond.
/// `Duration`'s `days` component should have no more than this value. static NANOS_PER_MICRO: i32 = 1000;
static MIN_DAYS: i32 = i32::MIN; /// The number of nanosecdons in a millisecond.
/// `Duration`'s `days` component should have no less than this value. static NANOS_PER_MILLI: i32 = 1000_000;
static MAX_DAYS: i32 = i32::MAX;
/// The number of nanoseconds in seconds. /// The number of nanoseconds in seconds.
static NANOS_PER_SEC: i32 = 1_000_000_000; static NANOS_PER_SEC: i32 = 1_000_000_000;
/// The number of microseconds per second.
static MICROS_PER_SEC: i64 = 1000_000;
/// The number of milliseconds per second.
static MILLIS_PER_SEC: i64 = 1000;
/// The number of seconds in a minute.
static SECS_PER_MINUTE: i64 = 60;
/// The number of seconds in an hour.
static SECS_PER_HOUR: i64 = 3600;
/// The number of (non-leap) seconds in days. /// The number of (non-leap) seconds in days.
static SECS_PER_DAY: i32 = 86400; static SECS_PER_DAY: i64 = 86400;
/// The number of (non-leap) seconds in a week.
static SECS_PER_WEEK: i64 = 604800;
macro_rules! try_opt( macro_rules! try_opt(
($e:expr) => (match $e { Some(v) => v, None => return None }) ($e:expr) => (match $e { Some(v) => v, None => return None })
) )
// FIXME #16466: This could be represented as (i64 seconds, u32 nanos)
/// ISO 8601 time duration with nanosecond precision. /// ISO 8601 time duration with nanosecond precision.
/// This also allows for the negative duration; see individual methods for details. /// This also allows for the negative duration; see individual methods for details.
#[deriving(Clone, PartialEq, Eq, PartialOrd, Ord)] #[deriving(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Duration { pub struct Duration {
days: i32, secs: i64,
secs: u32, // Always < SECS_PER_DAY nanos: i32, // Always 0 <= nanos < NANOS_PER_SEC
nanos: u32, // Always < NANOS_PR_SECOND
} }
/// The minimum possible `Duration`. /// The minimum possible `Duration`: `i64::MIN` milliseconds.
pub static MIN: Duration = Duration { days: MIN_DAYS, secs: 0, nanos: 0 }; pub static MIN: Duration = Duration {
/// The maximum possible `Duration`. secs: i64::MIN / MILLIS_PER_SEC - 1,
pub static MAX: Duration = Duration { days: MAX_DAYS, secs: SECS_PER_DAY as u32 - 1, nanos: NANOS_PER_SEC + (i64::MIN % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI
nanos: NANOS_PER_SEC as u32 - 1 }; };
/// The maximum possible `Duration`: `i64::MAX` milliseconds.
pub static MAX: Duration = Duration {
secs: i64::MAX / MILLIS_PER_SEC,
nanos: (i64::MAX % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI
};
impl Duration { impl Duration {
/// Makes a new `Duration` with given number of weeks. /// Makes a new `Duration` with given number of weeks.
/// Equivalent to `Duration::new(weeks * 7, 0, 0)` with overflow checks. /// Equivalent to `Duration::seconds(weeks * 7 * 24 * 60 * 60), with overflow checks.
///
/// Fails when the duration is out of bounds. /// Fails when the duration is out of bounds.
#[inline] #[inline]
pub fn weeks(weeks: i32) -> Duration { pub fn weeks(weeks: i64) -> Duration {
let days = weeks.checked_mul(&7).expect("Duration::weeks out of bounds"); let secs = weeks.checked_mul(&SECS_PER_WEEK).expect("Duration::weeks out of bounds");
Duration::days(days) Duration::seconds(secs)
} }
/// Makes a new `Duration` with given number of days. /// Makes a new `Duration` with given number of days.
/// Equivalent to `Duration::new(days, 0, 0)`. /// Equivalent to `Duration::seconds(days * 24 * 60 * 60)` with overflow checks.
/// Fails when the duration is out of bounds.
#[inline] #[inline]
pub fn days(days: i32) -> Duration { pub fn days(days: i64) -> Duration {
Duration { days: days, secs: 0, nanos: 0 } let secs = days.checked_mul(&SECS_PER_DAY).expect("Duration::days out of bounds");
Duration::seconds(secs)
} }
/// Makes a new `Duration` with given number of hours. /// Makes a new `Duration` with given number of hours.
/// Equivalent to `Duration::new(0, hours * 3600, 0)` with overflow checks. /// Equivalent to `Duration::seconds(hours * 60 * 60)` with overflow checks.
/// Fails when the duration is out of bounds.
#[inline] #[inline]
pub fn hours(hours: i32) -> Duration { pub fn hours(hours: i64) -> Duration {
let (days, hours) = div_mod_floor(hours, (SECS_PER_DAY / 3600)); let secs = hours.checked_mul(&SECS_PER_HOUR).expect("Duration::hours ouf of bounds");
let secs = hours * 3600; Duration::seconds(secs)
Duration { secs: secs as u32, ..Duration::days(days) }
} }
/// Makes a new `Duration` with given number of minutes. /// Makes a new `Duration` with given number of minutes.
/// Equivalent to `Duration::new(0, mins * 60, 0)` with overflow checks. /// Equivalent to `Duration::seconds(minutes * 60)` with overflow checks.
/// Fails when the duration is out of bounds.
#[inline] #[inline]
pub fn minutes(mins: i32) -> Duration { pub fn minutes(minutes: i64) -> Duration {
let (days, mins) = div_mod_floor(mins, (SECS_PER_DAY / 60)); let secs = minutes.checked_mul(&SECS_PER_MINUTE).expect("Duration::minutes out of bounds");
let secs = mins * 60; Duration::seconds(secs)
Duration { secs: secs as u32, ..Duration::days(days) }
} }
/// Makes a new `Duration` with given number of seconds. /// Makes a new `Duration` with given number of seconds.
/// Equivalent to `Duration::new(0, secs, 0)`. /// Fails when the duration is more than `i64::MAX` milliseconds
/// or less than `i64::MIN` milliseconds.
#[inline] #[inline]
pub fn seconds(secs: i32) -> Duration { pub fn seconds(seconds: i64) -> Duration {
let (days, secs) = div_mod_floor(secs, SECS_PER_DAY); let d = Duration { secs: seconds, nanos: 0 };
Duration { secs: secs as u32, ..Duration::days(days) } if d < MIN || d > MAX {
fail!("Duration::seconds out of bounds");
}
d
} }
/// Makes a new `Duration` with given number of milliseconds. /// Makes a new `Duration` with given number of milliseconds.
/// Equivalent to `Duration::new(0, 0, millis * 1_000_000)` with overflow checks.
#[inline] #[inline]
pub fn milliseconds(millis: i32) -> Duration { pub fn milliseconds(milliseconds: i64) -> Duration {
let (secs, millis) = div_mod_floor(millis, (NANOS_PER_SEC / 1_000_000)); let (secs, millis) = div_mod_floor_64(milliseconds, MILLIS_PER_SEC);
let nanos = millis * 1_000_000; let nanos = millis as i32 * NANOS_PER_MILLI;
Duration { nanos: nanos as u32, ..Duration::seconds(secs) } Duration { secs: secs, nanos: nanos }
} }
/// Makes a new `Duration` with given number of microseconds. /// Makes a new `Duration` with given number of microseconds.
/// Equivalent to `Duration::new(0, 0, micros * 1_000)` with overflow checks.
#[inline] #[inline]
pub fn microseconds(micros: i32) -> Duration { pub fn microseconds(microseconds: i64) -> Duration {
let (secs, micros) = div_mod_floor(micros, (NANOS_PER_SEC / 1_000)); let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
let nanos = micros * 1_000; let nanos = micros as i32 * NANOS_PER_MICRO;
Duration { nanos: nanos as u32, ..Duration::seconds(secs) } Duration { secs: secs, nanos: nanos }
} }
/// Makes a new `Duration` with given number of nanoseconds. /// Makes a new `Duration` with given number of nanoseconds.
/// Equivalent to `Duration::new(0, 0, nanos)`.
#[inline] #[inline]
pub fn nanoseconds(nanos: i32) -> Duration { pub fn nanoseconds(nanos: i64) -> Duration {
let (secs, nanos) = div_mod_floor(nanos, NANOS_PER_SEC); let (secs, nanos) = div_mod_floor_64(nanos, NANOS_PER_SEC as i64);
Duration { nanos: nanos as u32, ..Duration::seconds(secs) } Duration { secs: secs, nanos: nanos as i32 }
}
/// Returns a tuple of the number of days, (non-leap) seconds and
/// nanoseconds in the duration. Note that the number of seconds
/// and nanoseconds are always positive, so that for example
/// `-Duration::seconds(3)` has -1 days and 86,397 seconds.
#[inline]
fn to_tuple_64(&self) -> (i64, u32, u32) {
(self.days as i64, self.secs, self.nanos)
}
/// Negates the duration and returns a tuple like `to_tuple`.
/// This does not overflow and thus is internally used for several methods.
fn to_negated_tuple_64(&self) -> (i64, u32, u32) {
let mut days = -(self.days as i64);
let mut secs = -(self.secs as i32);
let mut nanos = -(self.nanos as i32);
if nanos < 0 {
nanos += NANOS_PER_SEC;
secs -= 1;
}
if secs < 0 {
secs += SECS_PER_DAY;
days -= 1;
}
(days, secs as u32, nanos as u32)
} }
/// Returns the total number of whole weeks in the duration. /// Returns the total number of whole weeks in the duration.
#[inline] #[inline]
pub fn num_weeks(&self) -> i32 { pub fn num_weeks(&self) -> i64 {
self.num_days() / 7 self.num_days() / 7
} }
/// Returns the total number of whole days in the duration. /// Returns the total number of whole days in the duration.
pub fn num_days(&self) -> i32 { pub fn num_days(&self) -> i64 {
if self.days < 0 { self.num_seconds() / SECS_PER_DAY
let negated = -*self;
-negated.days
} else {
self.days
}
} }
/// Returns the total number of whole hours in the duration. /// Returns the total number of whole hours in the duration.
#[inline] #[inline]
pub fn num_hours(&self) -> i64 { pub fn num_hours(&self) -> i64 {
self.num_seconds() / 3600 self.num_seconds() / SECS_PER_HOUR
} }
/// Returns the total number of whole minutes in the duration. /// Returns the total number of whole minutes in the duration.
#[inline] #[inline]
pub fn num_minutes(&self) -> i64 { pub fn num_minutes(&self) -> i64 {
self.num_seconds() / 60 self.num_seconds() / SECS_PER_MINUTE
} }
/// Returns the total number of whole seconds in the duration. /// Returns the total number of whole seconds in the duration.
pub fn num_seconds(&self) -> i64 { pub fn num_seconds(&self) -> i64 {
// cannot overflow, 2^32 * 86400 < 2^64 // If secs is negative, nanos should be subtracted from the duration.
fn secs((days, secs, _): (i64, u32, u32)) -> i64 { if self.secs < 0 && self.nanos > 0 {
days as i64 * SECS_PER_DAY as i64 + secs as i64 self.secs + 1
} else {
self.secs
} }
if self.days < 0 {-secs(self.to_negated_tuple_64())} else {secs(self.to_tuple_64())}
} }
/// Returns the total number of whole milliseconds in the duration. /// Returns the number of nanoseconds such that
pub fn num_milliseconds(&self) -> i64 { /// `nanos_mod_sec() + num_seconds() * NANOS_PER_SEC` is the total number of
// cannot overflow, 2^32 * 86400 * 1000 < 2^64 /// nanoseconds in the duration.
fn millis((days, secs, nanos): (i64, u32, u32)) -> i64 { fn nanos_mod_sec(&self) -> i32 {
static MILLIS_PER_SEC: i64 = 1_000; if self.secs < 0 && self.nanos > 0 {
static NANOS_PER_MILLI: i64 = 1_000_000; self.nanos - NANOS_PER_SEC
(days as i64 * MILLIS_PER_SEC * SECS_PER_DAY as i64 + } else {
secs as i64 * MILLIS_PER_SEC + self.nanos
nanos as i64 / NANOS_PER_MILLI)
} }
if self.days < 0 {-millis(self.to_negated_tuple_64())} else {millis(self.to_tuple_64())} }
/// Returns the total number of whole milliseconds in the duration,
pub fn num_milliseconds(&self) -> i64 {
// A proper Duration will not overflow, because MIN and MAX are defined
// such that the range is exactly i64 milliseconds.
let secs_part = self.num_seconds() * MILLIS_PER_SEC;
let nanos_part = self.nanos_mod_sec() / NANOS_PER_MILLI;
secs_part + nanos_part as i64
} }
/// Returns the total number of whole microseconds in the duration, /// Returns the total number of whole microseconds in the duration,
/// or `None` on the overflow (exceeding 2^63 microseconds in either directions). /// or `None` on overflow (exceeding 2^63 microseconds in either direction).
pub fn num_microseconds(&self) -> Option<i64> { pub fn num_microseconds(&self) -> Option<i64> {
fn micros((days, secs, nanos): (i64, u32, u32)) -> Option<i64> { let secs_part = try_opt!(self.num_seconds().checked_mul(&MICROS_PER_SEC));
static MICROS_PER_SEC: i64 = 1_000_000; let nanos_part = self.nanos_mod_sec() / NANOS_PER_MICRO;
static MICROS_PER_DAY: i64 = MICROS_PER_SEC * SECS_PER_DAY as i64; secs_part.checked_add(&(nanos_part as i64))
static NANOS_PER_MICRO: i64 = 1_000;
let nmicros = try_opt!((days as i64).checked_mul(&MICROS_PER_DAY));
let nmicros = try_opt!(nmicros.checked_add(&(secs as i64 * MICROS_PER_SEC)));
let nmicros = try_opt!(nmicros.checked_add(&(nanos as i64 / NANOS_PER_MICRO as i64)));
Some(nmicros)
}
if self.days < 0 {
// the final negation won't overflow since we start with positive numbers.
micros(self.to_negated_tuple_64()).map(|micros| -micros)
} else {
micros(self.to_tuple_64())
}
} }
/// Returns the total number of whole nanoseconds in the duration, /// Returns the total number of whole nanoseconds in the duration,
/// or `None` on the overflow (exceeding 2^63 nanoseconds in either directions). /// or `None` on overflow (exceeding 2^63 nanoseconds in either direction).
pub fn num_nanoseconds(&self) -> Option<i64> { pub fn num_nanoseconds(&self) -> Option<i64> {
fn nanos((days, secs, nanos): (i64, u32, u32)) -> Option<i64> { let secs_part = try_opt!(self.num_seconds().checked_mul(&(NANOS_PER_SEC as i64)));
static NANOS_PER_DAY: i64 = NANOS_PER_SEC as i64 * SECS_PER_DAY as i64; let nanos_part = self.nanos_mod_sec();
let nnanos = try_opt!((days as i64).checked_mul(&NANOS_PER_DAY)); secs_part.checked_add(&(nanos_part as i64))
let nnanos = try_opt!(nnanos.checked_add(&(secs as i64 * NANOS_PER_SEC as i64)));
let nnanos = try_opt!(nnanos.checked_add(&(nanos as i64)));
Some(nnanos)
}
if self.days < 0 {
// the final negation won't overflow since we start with positive numbers.
nanos(self.to_negated_tuple_64()).map(|micros| -micros)
} else {
nanos(self.to_tuple_64())
}
} }
} }
@ -244,156 +213,130 @@ impl num::Bounded for Duration {
impl num::Zero for Duration { impl num::Zero for Duration {
#[inline] #[inline]
fn zero() -> Duration { fn zero() -> Duration {
Duration { days: 0, secs: 0, nanos: 0 } Duration { secs: 0, nanos: 0 }
} }
#[inline] #[inline]
fn is_zero(&self) -> bool { fn is_zero(&self) -> bool {
self.days == 0 && self.secs == 0 && self.nanos == 0 self.secs == 0 && self.nanos == 0
} }
} }
impl Neg<Duration> for Duration { impl Neg<Duration> for Duration {
#[inline] #[inline]
fn neg(&self) -> Duration { fn neg(&self) -> Duration {
let (days, secs, nanos) = self.to_negated_tuple_64(); if self.nanos == 0 {
Duration { days: days as i32, secs: secs, nanos: nanos } // FIXME can overflow Duration { secs: -self.secs, nanos: 0 }
} else {
Duration { secs: -self.secs - 1, nanos: NANOS_PER_SEC - self.nanos }
}
} }
} }
impl Add<Duration,Duration> for Duration { impl Add<Duration,Duration> for Duration {
fn add(&self, rhs: &Duration) -> Duration { fn add(&self, rhs: &Duration) -> Duration {
let mut days = self.days + rhs.days;
let mut secs = self.secs + rhs.secs; let mut secs = self.secs + rhs.secs;
let mut nanos = self.nanos + rhs.nanos; let mut nanos = self.nanos + rhs.nanos;
if nanos >= NANOS_PER_SEC as u32 { if nanos >= NANOS_PER_SEC {
nanos -= NANOS_PER_SEC as u32; nanos -= NANOS_PER_SEC;
secs += 1; secs += 1;
} }
if secs >= SECS_PER_DAY as u32 { Duration { secs: secs, nanos: nanos }
secs -= SECS_PER_DAY as u32;
days += 1;
}
Duration { days: days, secs: secs, nanos: nanos }
} }
} }
impl num::CheckedAdd for Duration { impl num::CheckedAdd for Duration {
fn checked_add(&self, rhs: &Duration) -> Option<Duration> { fn checked_add(&self, rhs: &Duration) -> Option<Duration> {
let mut days = try_opt!(self.days.checked_add(&rhs.days)); let mut secs = try_opt!(self.secs.checked_add(&rhs.secs));
let mut secs = self.secs + rhs.secs;
let mut nanos = self.nanos + rhs.nanos; let mut nanos = self.nanos + rhs.nanos;
if nanos >= NANOS_PER_SEC as u32 { if nanos >= NANOS_PER_SEC {
nanos -= NANOS_PER_SEC as u32; nanos -= NANOS_PER_SEC;
secs += 1; secs = try_opt!(secs.checked_add(&1));
} }
if secs >= SECS_PER_DAY as u32 { let d = Duration { secs: secs, nanos: nanos };
secs -= SECS_PER_DAY as u32; // Even if d is within the bounds of i64 seconds,
days = try_opt!(days.checked_add(&1)); // it might still overflow i64 milliseconds.
} if d < MIN || d > MAX { None } else { Some(d) }
Some(Duration { days: days, secs: secs, nanos: nanos })
} }
} }
impl Sub<Duration,Duration> for Duration { impl Sub<Duration,Duration> for Duration {
fn sub(&self, rhs: &Duration) -> Duration { fn sub(&self, rhs: &Duration) -> Duration {
let mut days = self.days - rhs.days; let mut secs = self.secs - rhs.secs;
let mut secs = self.secs as i32 - rhs.secs as i32; let mut nanos = self.nanos - rhs.nanos;
let mut nanos = self.nanos as i32 - rhs.nanos as i32;
if nanos < 0 { if nanos < 0 {
nanos += NANOS_PER_SEC; nanos += NANOS_PER_SEC;
secs -= 1; secs -= 1;
} }
if secs < 0 { Duration { secs: secs, nanos: nanos }
secs += SECS_PER_DAY;
days -= 1;
}
Duration { days: days, secs: secs as u32, nanos: nanos as u32 }
} }
} }
impl num::CheckedSub for Duration { impl num::CheckedSub for Duration {
fn checked_sub(&self, rhs: &Duration) -> Option<Duration> { fn checked_sub(&self, rhs: &Duration) -> Option<Duration> {
let mut days = try_opt!(self.days.checked_sub(&rhs.days)); let mut secs = try_opt!(self.secs.checked_sub(&rhs.secs));
let mut secs = self.secs as i32 - rhs.secs as i32; let mut nanos = self.nanos - rhs.nanos;
let mut nanos = self.nanos as i32 - rhs.nanos as i32;
if nanos < 0 { if nanos < 0 {
nanos += NANOS_PER_SEC; nanos += NANOS_PER_SEC;
secs -= 1; secs = try_opt!(secs.checked_sub(&1));
} }
if secs < 0 { let d = Duration { secs: secs, nanos: nanos };
secs += SECS_PER_DAY; // Even if d is within the bounds of i64 seconds,
days = try_opt!(days.checked_sub(&1)); // it might still overflow i64 milliseconds.
} if d < MIN || d > MAX { None } else { Some(d) }
Some(Duration { days: days, secs: secs as u32, nanos: nanos as u32 })
} }
} }
impl Mul<i32,Duration> for Duration { impl Mul<i32,Duration> for Duration {
fn mul(&self, rhs: &i32) -> Duration { fn mul(&self, rhs: &i32) -> Duration {
/// Given `0 <= y < limit <= 2^30`, // Multiply nanoseconds as i64, because it cannot overflow that way.
/// returns `(h,l)` such that `x * y = h * limit + l` where `0 <= l < limit`. let total_nanos = self.nanos as i64 * *rhs as i64;
fn mul_i64_u32_limit(x: i64, y: u32, limit: u32) -> (i64,u32) { let (extra_secs, nanos) = div_mod_floor_64(total_nanos, NANOS_PER_SEC as i64);
let y = y as i64; let secs = self.secs * *rhs as i64 + extra_secs;
let limit = limit as i64; Duration { secs: secs, nanos: nanos as i32 }
let (xh, xl) = div_mod_floor_64(x, limit);
let (h, l) = (xh * y, xl * y);
let (h_, l) = div_rem_64(l, limit);
(h + h_, l as u32)
}
let rhs = *rhs as i64;
let (secs1, nanos) = mul_i64_u32_limit(rhs, self.nanos, NANOS_PER_SEC as u32);
let (days1, secs1) = div_mod_floor_64(secs1, (SECS_PER_DAY as i64));
let (days2, secs2) = mul_i64_u32_limit(rhs, self.secs, SECS_PER_DAY as u32);
let mut days = self.days as i64 * rhs + days1 + days2;
let mut secs = secs1 as u32 + secs2;
if secs >= SECS_PER_DAY as u32 {
secs -= 1;
days += 1;
}
Duration { days: days as i32, secs: secs, nanos: nanos }
} }
} }
impl Div<i32,Duration> for Duration { impl Div<i32,Duration> for Duration {
fn div(&self, rhs: &i32) -> Duration { fn div(&self, rhs: &i32) -> Duration {
let (rhs, days, secs, nanos) = if *rhs < 0 { let mut secs = self.secs / *rhs as i64;
let (days, secs, nanos) = self.to_negated_tuple_64(); let carry = self.secs - secs * *rhs as i64;
(-(*rhs as i64), days, secs as i64, nanos as i64) let extra_nanos = carry * NANOS_PER_SEC as i64 / *rhs as i64;
} else { let mut nanos = self.nanos / *rhs + extra_nanos as i32;
(*rhs as i64, self.days as i64, self.secs as i64, self.nanos as i64) if nanos >= NANOS_PER_SEC {
}; nanos -= NANOS_PER_SEC;
secs += 1;
let (days, carry) = div_mod_floor_64(days, rhs); }
let secs = secs + carry * SECS_PER_DAY as i64; if nanos < 0 {
let (secs, carry) = div_mod_floor_64(secs, rhs); nanos += NANOS_PER_SEC;
let nanos = nanos + carry * NANOS_PER_SEC as i64; secs -= 1;
let nanos = nanos / rhs; }
Duration { days: days as i32, secs: secs as u32, nanos: nanos as u32 } Duration { secs: secs, nanos: nanos }
} }
} }
impl fmt::Show for Duration { impl fmt::Show for Duration {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let hasdate = self.days != 0; let days = self.num_days();
let hastime = (self.secs != 0 || self.nanos != 0) || !hasdate; let secs = self.secs - days * SECS_PER_DAY;
let hasdate = days != 0;
let hastime = (secs != 0 || self.nanos != 0) || !hasdate;
try!(write!(f, "P")); try!(write!(f, "P"));
if hasdate { if hasdate {
// technically speaking the negative part is not the valid ISO 8601, // technically speaking the negative part is not the valid ISO 8601,
// but we need to print it anyway. // but we need to print it anyway.
try!(write!(f, "{}D", self.days)); try!(write!(f, "{}D", days));
} }
if hastime { if hastime {
if self.nanos == 0 { if self.nanos == 0 {
try!(write!(f, "T{}S", self.secs)); try!(write!(f, "T{}S", secs));
} else if self.nanos % 1_000_000 == 0 { } else if self.nanos % NANOS_PER_MILLI == 0 {
try!(write!(f, "T{}.{:03}S", self.secs, self.nanos / 1_000_000)); try!(write!(f, "T{}.{:03}S", secs, self.nanos / NANOS_PER_MILLI));
} else if self.nanos % 1_000 == 0 { } else if self.nanos % NANOS_PER_MICRO == 0 {
try!(write!(f, "T{}.{:06}S", self.secs, self.nanos / 1_000)); try!(write!(f, "T{}.{:06}S", secs, self.nanos / NANOS_PER_MICRO));
} else { } else {
try!(write!(f, "T{}.{:09}S", self.secs, self.nanos)); try!(write!(f, "T{}.{:09}S", secs, self.nanos));
} }
} }
Ok(()) Ok(())
@ -401,34 +344,6 @@ impl fmt::Show for Duration {
} }
// Copied from libnum // Copied from libnum
#[inline]
fn div_mod_floor(this: i32, other: i32) -> (i32, i32) {
(div_floor(this, other), mod_floor(this, other))
}
#[inline]
fn div_floor(this: i32, other: i32) -> i32 {
match div_rem(this, other) {
(d, r) if (r > 0 && other < 0)
|| (r < 0 && other > 0) => d - 1,
(d, _) => d,
}
}
#[inline]
fn mod_floor(this: i32, other: i32) -> i32 {
match this % other {
r if (r > 0 && other < 0)
|| (r < 0 && other > 0) => r + other,
r => r,
}
}
#[inline]
fn div_rem(this: i32, other: i32) -> (i32, i32) {
(this / other, this % other)
}
#[inline] #[inline]
fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) { fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) {
(div_floor_64(this, other), mod_floor_64(this, other)) (div_floor_64(this, other), mod_floor_64(this, other))
@ -459,7 +374,7 @@ fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{Duration, MIN_DAYS, MAX_DAYS, MIN, MAX}; use super::{Duration, MIN, MAX};
use {i32, i64}; use {i32, i64};
use num::{Zero, CheckedAdd, CheckedSub}; use num::{Zero, CheckedAdd, CheckedSub};
use option::{Some, None}; use option::{Some, None};
@ -475,7 +390,8 @@ mod tests {
Duration::days(1) + Duration::seconds(3)); Duration::days(1) + Duration::seconds(3));
assert_eq!(Duration::days(10) - Duration::seconds(1000), Duration::seconds(863000)); assert_eq!(Duration::days(10) - Duration::seconds(1000), Duration::seconds(863000));
assert_eq!(Duration::days(10) - Duration::seconds(1000000), Duration::seconds(-136000)); assert_eq!(Duration::days(10) - Duration::seconds(1000000), Duration::seconds(-136000));
assert_eq!(Duration::days(2) + Duration::seconds(86399) + Duration::nanoseconds(1234567890), assert_eq!(Duration::days(2) + Duration::seconds(86399) +
Duration::nanoseconds(1234567890),
Duration::days(3) + Duration::nanoseconds(234567890)); Duration::days(3) + Duration::nanoseconds(234567890));
assert_eq!(-Duration::days(3), Duration::days(-3)); assert_eq!(-Duration::days(3), Duration::days(-3));
assert_eq!(-(Duration::days(3) + Duration::seconds(70)), assert_eq!(-(Duration::days(3) + Duration::seconds(70)),
@ -492,10 +408,8 @@ mod tests {
assert_eq!(Duration::seconds(86401).num_days(), 1); assert_eq!(Duration::seconds(86401).num_days(), 1);
assert_eq!(Duration::seconds(-86399).num_days(), 0); assert_eq!(Duration::seconds(-86399).num_days(), 0);
assert_eq!(Duration::seconds(-86401).num_days(), -1); assert_eq!(Duration::seconds(-86401).num_days(), -1);
assert_eq!(Duration::days(i32::MAX).num_days(), i32::MAX); assert_eq!(Duration::days(i32::MAX as i64).num_days(), i32::MAX as i64);
assert_eq!(Duration::days(i32::MIN).num_days(), i32::MIN); assert_eq!(Duration::days(i32::MIN as i64).num_days(), i32::MIN as i64);
assert_eq!(MAX.num_days(), MAX_DAYS);
assert_eq!(MIN.num_days(), MIN_DAYS);
} }
#[test] #[test]
@ -508,10 +422,6 @@ mod tests {
assert_eq!(Duration::milliseconds(1001).num_seconds(), 1); assert_eq!(Duration::milliseconds(1001).num_seconds(), 1);
assert_eq!(Duration::milliseconds(-999).num_seconds(), 0); assert_eq!(Duration::milliseconds(-999).num_seconds(), 0);
assert_eq!(Duration::milliseconds(-1001).num_seconds(), -1); assert_eq!(Duration::milliseconds(-1001).num_seconds(), -1);
assert_eq!(Duration::seconds(i32::MAX).num_seconds(), i32::MAX as i64);
assert_eq!(Duration::seconds(i32::MIN).num_seconds(), i32::MIN as i64);
assert_eq!(MAX.num_seconds(), (MAX_DAYS as i64 + 1) * 86400 - 1);
assert_eq!(MIN.num_seconds(), MIN_DAYS as i64 * 86400);
} }
#[test] #[test]
@ -524,10 +434,10 @@ mod tests {
assert_eq!(Duration::microseconds(1001).num_milliseconds(), 1); assert_eq!(Duration::microseconds(1001).num_milliseconds(), 1);
assert_eq!(Duration::microseconds(-999).num_milliseconds(), 0); assert_eq!(Duration::microseconds(-999).num_milliseconds(), 0);
assert_eq!(Duration::microseconds(-1001).num_milliseconds(), -1); assert_eq!(Duration::microseconds(-1001).num_milliseconds(), -1);
assert_eq!(Duration::milliseconds(i32::MAX).num_milliseconds(), i32::MAX as i64); assert_eq!(Duration::milliseconds(i64::MAX).num_milliseconds(), i64::MAX);
assert_eq!(Duration::milliseconds(i32::MIN).num_milliseconds(), i32::MIN as i64); assert_eq!(Duration::milliseconds(i64::MIN).num_milliseconds(), i64::MIN);
assert_eq!(MAX.num_milliseconds(), (MAX_DAYS as i64 + 1) * 86400_000 - 1); assert_eq!(MAX.num_milliseconds(), i64::MAX);
assert_eq!(MIN.num_milliseconds(), MIN_DAYS as i64 * 86400_000); assert_eq!(MIN.num_milliseconds(), i64::MIN);
} }
#[test] #[test]
@ -540,19 +450,19 @@ mod tests {
assert_eq!(Duration::nanoseconds(1001).num_microseconds(), Some(1)); assert_eq!(Duration::nanoseconds(1001).num_microseconds(), Some(1));
assert_eq!(Duration::nanoseconds(-999).num_microseconds(), Some(0)); assert_eq!(Duration::nanoseconds(-999).num_microseconds(), Some(0));
assert_eq!(Duration::nanoseconds(-1001).num_microseconds(), Some(-1)); assert_eq!(Duration::nanoseconds(-1001).num_microseconds(), Some(-1));
assert_eq!(Duration::microseconds(i32::MAX).num_microseconds(), Some(i32::MAX as i64)); assert_eq!(Duration::microseconds(i64::MAX).num_microseconds(), Some(i64::MAX));
assert_eq!(Duration::microseconds(i32::MIN).num_microseconds(), Some(i32::MIN as i64)); assert_eq!(Duration::microseconds(i64::MIN).num_microseconds(), Some(i64::MIN));
assert_eq!(MAX.num_microseconds(), None); assert_eq!(MAX.num_microseconds(), None);
assert_eq!(MIN.num_microseconds(), None); assert_eq!(MIN.num_microseconds(), None);
// overflow checks // overflow checks
static MICROS_PER_DAY: i64 = 86400_000_000; static MICROS_PER_DAY: i64 = 86400_000_000;
assert_eq!(Duration::days((i64::MAX / MICROS_PER_DAY) as i32).num_microseconds(), assert_eq!(Duration::days(i64::MAX / MICROS_PER_DAY).num_microseconds(),
Some(i64::MAX / MICROS_PER_DAY * MICROS_PER_DAY)); Some(i64::MAX / MICROS_PER_DAY * MICROS_PER_DAY));
assert_eq!(Duration::days((i64::MIN / MICROS_PER_DAY) as i32).num_microseconds(), assert_eq!(Duration::days(i64::MIN / MICROS_PER_DAY).num_microseconds(),
Some(i64::MIN / MICROS_PER_DAY * MICROS_PER_DAY)); Some(i64::MIN / MICROS_PER_DAY * MICROS_PER_DAY));
assert_eq!(Duration::days((i64::MAX / MICROS_PER_DAY + 1) as i32).num_microseconds(), None); assert_eq!(Duration::days(i64::MAX / MICROS_PER_DAY + 1).num_microseconds(), None);
assert_eq!(Duration::days((i64::MIN / MICROS_PER_DAY - 1) as i32).num_microseconds(), None); assert_eq!(Duration::days(i64::MIN / MICROS_PER_DAY - 1).num_microseconds(), None);
} }
#[test] #[test]
@ -561,30 +471,32 @@ mod tests {
assert_eq!(d.num_nanoseconds(), Some(0)); assert_eq!(d.num_nanoseconds(), Some(0));
assert_eq!(Duration::nanoseconds(1).num_nanoseconds(), Some(1)); assert_eq!(Duration::nanoseconds(1).num_nanoseconds(), Some(1));
assert_eq!(Duration::nanoseconds(-1).num_nanoseconds(), Some(-1)); assert_eq!(Duration::nanoseconds(-1).num_nanoseconds(), Some(-1));
assert_eq!(Duration::nanoseconds(i32::MAX).num_nanoseconds(), Some(i32::MAX as i64)); assert_eq!(Duration::nanoseconds(i64::MAX).num_nanoseconds(), Some(i64::MAX));
assert_eq!(Duration::nanoseconds(i32::MIN).num_nanoseconds(), Some(i32::MIN as i64)); assert_eq!(Duration::nanoseconds(i64::MIN).num_nanoseconds(), Some(i64::MIN));
assert_eq!(MAX.num_nanoseconds(), None); assert_eq!(MAX.num_nanoseconds(), None);
assert_eq!(MIN.num_nanoseconds(), None); assert_eq!(MIN.num_nanoseconds(), None);
// overflow checks // overflow checks
static NANOS_PER_DAY: i64 = 86400_000_000_000; static NANOS_PER_DAY: i64 = 86400_000_000_000;
assert_eq!(Duration::days((i64::MAX / NANOS_PER_DAY) as i32).num_nanoseconds(), assert_eq!(Duration::days(i64::MAX / NANOS_PER_DAY).num_nanoseconds(),
Some(i64::MAX / NANOS_PER_DAY * NANOS_PER_DAY)); Some(i64::MAX / NANOS_PER_DAY * NANOS_PER_DAY));
assert_eq!(Duration::days((i64::MIN / NANOS_PER_DAY) as i32).num_nanoseconds(), assert_eq!(Duration::days(i64::MIN / NANOS_PER_DAY).num_nanoseconds(),
Some(i64::MIN / NANOS_PER_DAY * NANOS_PER_DAY)); Some(i64::MIN / NANOS_PER_DAY * NANOS_PER_DAY));
assert_eq!(Duration::days((i64::MAX / NANOS_PER_DAY + 1) as i32).num_nanoseconds(), None); assert_eq!(Duration::days(i64::MAX / NANOS_PER_DAY + 1).num_nanoseconds(), None);
assert_eq!(Duration::days((i64::MIN / NANOS_PER_DAY - 1) as i32).num_nanoseconds(), None); assert_eq!(Duration::days(i64::MIN / NANOS_PER_DAY - 1).num_nanoseconds(), None);
} }
#[test] #[test]
fn test_duration_checked_ops() { fn test_duration_checked_ops() {
assert_eq!(Duration::days(MAX_DAYS).checked_add(&Duration::seconds(86399)), assert_eq!(Duration::milliseconds(i64::MAX - 1).checked_add(&Duration::microseconds(999)),
Some(Duration::days(MAX_DAYS - 1) + Duration::seconds(86400+86399))); Some(Duration::milliseconds(i64::MAX - 2) + Duration::microseconds(1999)));
assert!(Duration::days(MAX_DAYS).checked_add(&Duration::seconds(86400)).is_none()); assert!(Duration::milliseconds(i64::MAX).checked_add(&Duration::microseconds(1000))
.is_none());
assert_eq!(Duration::days(MIN_DAYS).checked_sub(&Duration::seconds(0)), assert_eq!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(0)),
Some(Duration::days(MIN_DAYS))); Some(Duration::milliseconds(i64::MIN)));
assert!(Duration::days(MIN_DAYS).checked_sub(&Duration::seconds(1)).is_none()); assert!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(1))
.is_none());
} }
#[test] #[test]
@ -601,6 +513,8 @@ mod tests {
Duration::seconds(10) - Duration::nanoseconds(10)); Duration::seconds(10) - Duration::nanoseconds(10));
assert_eq!((Duration::nanoseconds(1) + Duration::seconds(1) + Duration::days(1)) * 3, assert_eq!((Duration::nanoseconds(1) + Duration::seconds(1) + Duration::days(1)) * 3,
Duration::nanoseconds(3) + Duration::seconds(3) + Duration::days(3)); Duration::nanoseconds(3) + Duration::seconds(3) + Duration::days(3));
assert_eq!(Duration::milliseconds(1500) * -2, Duration::seconds(-3));
assert_eq!(Duration::milliseconds(-1500) * 2, Duration::seconds(-3));
} }
#[test] #[test]
@ -612,6 +526,13 @@ mod tests {
assert_eq!(Duration::nanoseconds(123_456_789) / -1, -Duration::nanoseconds(123_456_789)); assert_eq!(Duration::nanoseconds(123_456_789) / -1, -Duration::nanoseconds(123_456_789));
assert_eq!(-Duration::nanoseconds(123_456_789) / -1, Duration::nanoseconds(123_456_789)); assert_eq!(-Duration::nanoseconds(123_456_789) / -1, Duration::nanoseconds(123_456_789));
assert_eq!(-Duration::nanoseconds(123_456_789) / 1, -Duration::nanoseconds(123_456_789)); assert_eq!(-Duration::nanoseconds(123_456_789) / 1, -Duration::nanoseconds(123_456_789));
assert_eq!(Duration::seconds(1) / 3, Duration::nanoseconds(333_333_333));
assert_eq!(Duration::seconds(4) / 3, Duration::nanoseconds(1_333_333_333));
assert_eq!(Duration::seconds(-1) / 2, Duration::milliseconds(-500));
assert_eq!(Duration::seconds(1) / -2, Duration::milliseconds(-500));
assert_eq!(Duration::seconds(-1) / -2, Duration::milliseconds(500));
assert_eq!(Duration::seconds(-4) / 3, Duration::nanoseconds(-1_333_333_333));
assert_eq!(Duration::seconds(-4) / -3, Duration::nanoseconds(1_333_333_333));
} }
#[test] #[test]