]> git.lizzy.rs Git - rust.git/commitdiff
libstd: Refactor Duration.
authorRuud van Asseldonk <dev@veniogames.com>
Tue, 19 Aug 2014 12:32:20 +0000 (14:32 +0200)
committerRuud van Asseldonk <dev@veniogames.com>
Wed, 20 Aug 2014 11:55:02 +0000 (13:55 +0200)
This changes the internal representation of `Duration` from

    days: i32,
    secs: i32,
    nanos: u32

to

    secs: i64,
    nanos: i32

This resolves #16466. 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.

src/libstd/io/net/tcp.rs
src/libstd/io/net/unix.rs
src/libstd/io/timer.rs
src/libstd/time/duration.rs

index 7055b9d7a473887c5680905eb889372428f047ca..52d3a04432a358e742559d64bd313912253934be 100644 (file)
@@ -97,25 +97,31 @@ pub fn connect(host: &str, port: u16) -> IoResult<TcpStream> {
     /// the specified duration.
     ///
     /// This is the same as the `connect` method, except that if the timeout
-    /// specified (in milliseconds) elapses before a connection is made an error
-    /// will be returned. The error's kind will be `TimedOut`.
+    /// specified elapses before a connection is made an error will be
+    /// returned. The error's kind will be `TimedOut`.
     ///
     /// Note that the `addr` argument may one day be split into a separate host
     /// and port, similar to the API seen in `connect`.
     ///
     /// If a `timeout` with zero or negative duration is specified then
     /// the function returns `Err`, with the error kind set to `TimedOut`.
+    /// If the timeout is larger than 2^63 milliseconds, the function also
+    /// returns `Err` with the error kind set to `TimedOut`.
     #[experimental = "the timeout argument may eventually change types"]
     pub fn connect_timeout(addr: SocketAddr,
                            timeout: Duration) -> IoResult<TcpStream> {
         if timeout <= Duration::milliseconds(0) {
             return Err(standard_error(TimedOut));
         }
+        let timeout_ms = timeout.num_milliseconds().map(|x| { x as u64 });
+        if timeout_ms.is_none() {
+            return Err(standard_error(TimedOut));
+        }
 
         let SocketAddr { ip, port } = addr;
         let addr = rtio::SocketAddr { ip: super::to_rtio(ip), port: port };
         LocalIo::maybe_raise(|io| {
-            io.tcp_connect(addr, Some(timeout.num_milliseconds() as u64)).map(TcpStream::new)
+            io.tcp_connect(addr, timeout_ms).map(TcpStream::new)
         }).map_err(IoError::from_rtio_error)
     }
 
index eb251075418390a27100092c483b11b02a499cbe..179855003f9bdece2b969a2d1d8dd18d85bcd0b0 100644 (file)
@@ -61,20 +61,26 @@ pub fn connect<P: ToCStr>(path: &P) -> IoResult<UnixStream> {
     /// Connect to a pipe named by `path`, timing out if the specified number of
     /// 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`.
     ///
     /// If a `timeout` with zero or negative duration is specified then
     /// the function returns `Err`, with the error kind set to `TimedOut`.
+    /// If the timeout is larger than 2^63 milliseconds, the function also
+    /// returns `Err` with the error kind set to `TimedOut`.
     #[experimental = "the timeout argument is likely to change types"]
     pub fn connect_timeout<P: ToCStr>(path: &P,
                                       timeout: Duration) -> IoResult<UnixStream> {
         if timeout <= Duration::milliseconds(0) {
             return Err(standard_error(TimedOut));
         }
+        let timeout_ms = timeout.num_milliseconds().map(|x| { x as u64 });
+        if timeout_ms.is_none() {
+            return Err(standard_error(TimedOut));
+        }
 
         LocalIo::maybe_raise(|io| {
-            let s = io.unix_connect(&path.to_c_str(), Some(timeout.num_milliseconds() as u64));
+            let s = io.unix_connect(&path.to_c_str(), timeout_ms);
             s.map(|p| UnixStream { obj: p })
         }).map_err(IoError::from_rtio_error)
     }
index 39c6c74e45eef62c59028409b2f1875bd3d0d449..205132aca1d35a4d1fd37eaa5fbaa9be96cd0537 100644 (file)
@@ -225,7 +225,7 @@ fn call(&mut self) {
 }
 
 fn in_ms_u64(d: Duration) -> u64 {
-    let ms = d.num_milliseconds();
+    let ms = d.num_milliseconds().unwrap_or(0);
     if ms < 0 { return 0 };
     return ms as u64;
 }
index 155dfbc66c00897ccf8c4ee64ec617f6435042c1..c2030ccceb06de5dbde24bbf0fa09eabc23b5427 100644 (file)
 
 #![experimental]
 
-use {fmt, i32};
+use {fmt, i64};
 use ops::{Add, Sub, Mul, Div, Neg};
 use option::{Option, Some, None};
 use num;
 use num::{CheckedAdd, CheckedMul};
 use result::{Result, Ok, Err};
 
-
-/// `Duration`'s `days` component should have no more than this value.
-static MIN_DAYS: i32 = i32::MIN;
-/// `Duration`'s `days` component should have no less than this value.
-static MAX_DAYS: i32 = i32::MAX;
-
+/// The number of nanoseconds in a microsecond.
+static NANOS_PER_MICRO: i32 = 1000;
+/// The number of nanosecdons in a millisecond.
+static NANOS_PER_MILLI: i32 = 1000_000;
 /// The number of nanoseconds in seconds.
 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.
-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(
     ($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.
 /// This also allows for the negative duration; see individual methods for details.
 #[deriving(Clone, PartialEq, Eq, PartialOrd, Ord)]
 pub struct Duration {
-    days: i32,
-    secs: u32,  // Always < SECS_PER_DAY
-    nanos: u32, // Always < NANOS_PR_SECOND
+    secs: i64,
+    nanos: i32, // Always 0 <= nanos < NANOS_PER_SEC
 }
 
 /// The minimum possible `Duration`.
-pub static MIN: Duration = Duration { days: MIN_DAYS, secs: 0, nanos: 0 };
+pub static MIN: Duration = Duration { secs: i64::MIN, nanos: 0 };
 /// The maximum possible `Duration`.
-pub static MAX: Duration = Duration { days: MAX_DAYS, secs: SECS_PER_DAY as u32 - 1,
-                                      nanos: NANOS_PER_SEC as u32 - 1 };
+pub static MAX: Duration = Duration { secs: i64::MAX, nanos: NANOS_PER_SEC - 1 };
 
 impl Duration {
     /// 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.
     #[inline]
-    pub fn weeks(weeks: i32) -> Duration {
-        let days = weeks.checked_mul(&7).expect("Duration::weeks out of bounds");
-        Duration::days(days)
+    pub fn weeks(weeks: i64) -> Duration {
+        let secs = weeks.checked_mul(&SECS_PER_WEEK).expect("Duration::weeks out of bounds");
+        Duration::seconds(secs)
     }
 
     /// 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]
-    pub fn days(days: i32) -> Duration {
-        Duration { days: days, secs: 0, nanos: 0 }
+    pub fn days(days: i64) -> Duration {
+        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.
-    /// 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]
-    pub fn hours(hours: i32) -> Duration {
-        let (days, hours) = div_mod_floor(hours, (SECS_PER_DAY / 3600));
-        let secs = hours * 3600;
-        Duration { secs: secs as u32, ..Duration::days(days) }
+    pub fn hours(hours: i64) -> Duration {
+        let secs = hours.checked_mul(&SECS_PER_HOUR).expect("Duration::hours ouf of bounds");
+        Duration::seconds(secs)
     }
 
     /// 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]
-    pub fn minutes(mins: i32) -> Duration {
-        let (days, mins) = div_mod_floor(mins, (SECS_PER_DAY / 60));
-        let secs = mins * 60;
-        Duration { secs: secs as u32, ..Duration::days(days) }
+    pub fn minutes(minutes: i64) -> Duration {
+        let secs = minutes.checked_mul(&SECS_PER_MINUTE).expect("Duration::minutes out of bounds");
+        Duration::seconds(secs)
     }
 
     /// Makes a new `Duration` with given number of seconds.
-    /// Equivalent to `Duration::new(0, secs, 0)`.
     #[inline]
-    pub fn seconds(secs: i32) -> Duration {
-        let (days, secs) = div_mod_floor(secs, SECS_PER_DAY);
-        Duration { secs: secs as u32, ..Duration::days(days) }
+    pub fn seconds(seconds: i64) -> Duration {
+        Duration { secs: seconds, nanos: 0 }
     }
 
     /// Makes a new `Duration` with given number of milliseconds.
-    /// Equivalent to `Duration::new(0, 0, millis * 1_000_000)` with overflow checks.
     #[inline]
-    pub fn milliseconds(millis: i32) -> Duration {
-        let (secs, millis) = div_mod_floor(millis, (NANOS_PER_SEC / 1_000_000));
-        let nanos = millis * 1_000_000;
-        Duration { nanos: nanos as u32, ..Duration::seconds(secs) }
+    pub 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.
-    /// Equivalent to `Duration::new(0, 0, micros * 1_000)` with overflow checks.
     #[inline]
-    pub fn microseconds(micros: i32) -> Duration {
-        let (secs, micros) = div_mod_floor(micros, (NANOS_PER_SEC / 1_000));
-        let nanos = micros * 1_000;
-        Duration { nanos: nanos as u32, ..Duration::seconds(secs) }
+    pub 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.
-    /// Equivalent to `Duration::new(0, 0, nanos)`.
     #[inline]
-    pub fn nanoseconds(nanos: i32) -> Duration {
-        let (secs, nanos) = div_mod_floor(nanos, NANOS_PER_SEC);
-        Duration { nanos: nanos as u32, ..Duration::seconds(secs) }
-    }
-
-    /// 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)
+    pub 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 }
     }
 
     /// Returns the total number of whole weeks in the duration.
     #[inline]
-    pub fn num_weeks(&self) -> i32 {
+    pub fn num_weeks(&self) -> i64 {
         self.num_days() / 7
     }
 
     /// Returns the total number of whole days in the duration.
-    pub fn num_days(&self) -> i32 {
-        if self.days < 0 {
-            let negated = -*self;
-            -negated.days
-        } else {
-            self.days
-        }
+    pub fn num_days(&self) -> i64 {
+        self.num_seconds() / SECS_PER_DAY
     }
 
     /// Returns the total number of whole hours in the duration.
     #[inline]
     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.
     #[inline]
     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.
     pub fn num_seconds(&self) -> i64 {
-        // cannot overflow, 2^32 * 86400 < 2^64
-        fn secs((days, secs, _): (i64, u32, u32)) -> i64 {
-            days as i64 * SECS_PER_DAY as i64 + secs as i64
+        // If secs is negative, nanos should be subtracted from the duration.
+        if self.secs < 0 && self.nanos > 0 {
+            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.
-    pub fn num_milliseconds(&self) -> i64 {
-        // cannot overflow, 2^32 * 86400 * 1000 < 2^64
-        fn millis((days, secs, nanos): (i64, u32, u32)) -> i64 {
-            static MILLIS_PER_SEC: i64 = 1_000;
-            static NANOS_PER_MILLI: i64 = 1_000_000;
-            (days as i64 * MILLIS_PER_SEC * SECS_PER_DAY as i64 +
-             secs as i64 * MILLIS_PER_SEC +
-             nanos as i64 / NANOS_PER_MILLI)
+    }
+
+    /// Returns the number of nanoseconds such that
+    /// `nanos_mod_sec() + num_seconds() * NANOS_PER_SEC` is the total number of
+    /// nanoseconds in the duration.
+    fn nanos_mod_sec(&self) -> i32 {
+        if self.secs < 0 && self.nanos > 0 {
+            self.nanos - NANOS_PER_SEC
+        } else {
+            self.nanos
         }
-        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,
+    /// or `None` on overflow (exceeding 2^63 milliseconds in either direction).
+    pub fn num_milliseconds(&self) -> Option<i64> {
+        let secs_part = try_opt!(self.num_seconds().checked_mul(&MILLIS_PER_SEC));
+        let nanos_part = self.nanos_mod_sec() / NANOS_PER_MILLI;
+        secs_part.checked_add(&(nanos_part as i64))
     }
 
     /// 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> {
-        fn micros((days, secs, nanos): (i64, u32, u32)) -> Option<i64> {
-            static MICROS_PER_SEC: i64 = 1_000_000;
-            static MICROS_PER_DAY: i64 = MICROS_PER_SEC * SECS_PER_DAY 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())
-        }
+        let secs_part = try_opt!(self.num_seconds().checked_mul(&MICROS_PER_SEC));
+        let nanos_part = self.nanos_mod_sec() / NANOS_PER_MICRO;
+        secs_part.checked_add(&(nanos_part as i64))
     }
 
     /// 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> {
-        fn nanos((days, secs, nanos): (i64, u32, u32)) -> Option<i64> {
-            static NANOS_PER_DAY: i64 = NANOS_PER_SEC as i64 * SECS_PER_DAY as i64;
-            let nnanos = try_opt!((days as i64).checked_mul(&NANOS_PER_DAY));
-            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())
-        }
+        let secs_part = try_opt!(self.num_seconds().checked_mul(&(NANOS_PER_SEC as i64)));
+        let nanos_part = self.nanos_mod_sec();
+        secs_part.checked_add(&(nanos_part as i64))
     }
 }
 
@@ -244,156 +199,130 @@ impl num::Bounded for Duration {
 impl num::Zero for Duration {
     #[inline]
     fn zero() -> Duration {
-        Duration { days: 0, secs: 0, nanos: 0 }
+        Duration { secs: 0, nanos: 0 }
     }
 
     #[inline]
     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 {
     #[inline]
     fn neg(&self) -> Duration {
-        let (days, secs, nanos) = self.to_negated_tuple_64();
-        Duration { days: days as i32, secs: secs, nanos: nanos } // FIXME can overflow
+        if self.secs == i64::MIN && self.nanos == 0 {
+            // The minimum value cannot be negated due to overflow. Use the
+            // maximum value, which is one nanosecond less than the negated minimum.
+            MAX
+        } else if self.secs == i64::MIN {
+            Duration { secs: i64::MAX, nanos: NANOS_PER_SEC - self.nanos }
+        } else if self.nanos == 0 {
+            Duration { secs: -self.secs, nanos: 0 }
+        } else {
+            Duration { secs: -self.secs - 1, nanos: NANOS_PER_SEC - self.nanos }
+        }
     }
 }
 
 impl Add<Duration,Duration> for Duration {
     fn add(&self, rhs: &Duration) -> Duration {
-        let mut days = self.days + rhs.days;
         let mut secs = self.secs + rhs.secs;
         let mut nanos = self.nanos + rhs.nanos;
-        if nanos >= NANOS_PER_SEC as u32 {
-            nanos -= NANOS_PER_SEC as u32;
+        if nanos >= NANOS_PER_SEC {
+            nanos -= NANOS_PER_SEC;
             secs += 1;
         }
-        if secs >= SECS_PER_DAY as u32 {
-            secs -= SECS_PER_DAY as u32;
-            days += 1;
-        }
-        Duration { days: days, secs: secs, nanos: nanos }
+        Duration { secs: secs, nanos: nanos }
     }
 }
 
 impl num::CheckedAdd for Duration {
     fn checked_add(&self, rhs: &Duration) -> Option<Duration> {
-        let mut days = try_opt!(self.days.checked_add(&rhs.days));
-        let mut secs = self.secs + rhs.secs;
+        let mut secs = try_opt!(self.secs.checked_add(&rhs.secs));
         let mut nanos = self.nanos + rhs.nanos;
-        if nanos >= NANOS_PER_SEC as u32 {
-            nanos -= NANOS_PER_SEC as u32;
-            secs += 1;
+        if nanos >= NANOS_PER_SEC {
+            nanos -= NANOS_PER_SEC;
+            secs = try_opt!(secs.checked_add(&1));
         }
-        if secs >= SECS_PER_DAY as u32 {
-            secs -= SECS_PER_DAY as u32;
-            days = try_opt!(days.checked_add(&1));
-        }
-        Some(Duration { days: days, secs: secs, nanos: nanos })
+        Some(Duration { secs: secs, nanos: nanos }) 
     }
 }
 
 impl Sub<Duration,Duration> for Duration {
     fn sub(&self, rhs: &Duration) -> Duration {
-        let mut days = self.days - rhs.days;
-        let mut secs = self.secs as i32 - rhs.secs as i32;
-        let mut nanos = self.nanos as i32 - rhs.nanos as i32;
+        let mut secs = self.secs - rhs.secs;
+        let mut nanos = self.nanos - rhs.nanos;
         if nanos < 0 {
             nanos += NANOS_PER_SEC;
             secs -= 1;
         }
-        if secs < 0 {
-            secs += SECS_PER_DAY;
-            days -= 1;
-        }
-        Duration { days: days, secs: secs as u32, nanos: nanos as u32 }
+        Duration { secs: secs, nanos: nanos }
     }
 }
 
 impl num::CheckedSub for Duration {
     fn checked_sub(&self, rhs: &Duration) -> Option<Duration> {
-        let mut days = try_opt!(self.days.checked_sub(&rhs.days));
-        let mut secs = self.secs as i32 - rhs.secs as i32;
-        let mut nanos = self.nanos as i32 - rhs.nanos as i32;
+        let mut secs = try_opt!(self.secs.checked_sub(&rhs.secs));
+        let mut nanos = self.nanos - rhs.nanos;
         if nanos < 0 {
             nanos += NANOS_PER_SEC;
-            secs -= 1;
-        }
-        if secs < 0 {
-            secs += SECS_PER_DAY;
-            days = try_opt!(days.checked_sub(&1));
+            secs = try_opt!(secs.checked_sub(&1));
         }
-        Some(Duration { days: days, secs: secs as u32, nanos: nanos as u32 })
+        Some(Duration { secs: secs, nanos: nanos })
     }
 }
 
 impl Mul<i32,Duration> for Duration {
     fn mul(&self, rhs: &i32) -> Duration {
-        /// Given `0 <= y < limit <= 2^30`,
-        /// returns `(h,l)` such that `x * y = h * limit + l` where `0 <= l < limit`.
-        fn mul_i64_u32_limit(x: i64, y: u32, limit: u32) -> (i64,u32) {
-            let y = y as i64;
-            let limit = limit as i64;
-            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 }
+        // Multiply nanoseconds as i64, because it cannot overflow that way.
+        let total_nanos = self.nanos as i64 * *rhs as i64;
+        let (extra_secs, nanos) = div_mod_floor_64(total_nanos, NANOS_PER_SEC as i64);
+        let secs = self.secs * *rhs as i64 + extra_secs;
+        Duration { secs: secs, nanos: nanos as i32 }
     }
 }
 
 impl Div<i32,Duration> for Duration {
     fn div(&self, rhs: &i32) -> Duration {
-        let (rhs, days, secs, nanos) = if *rhs < 0 {
-            let (days, secs, nanos) = self.to_negated_tuple_64();
-            (-(*rhs as i64), days, secs as i64, nanos as i64)
-        } else {
-            (*rhs as i64, self.days as i64, self.secs as i64, self.nanos as i64)
-        };
-
-        let (days, carry) = div_mod_floor_64(days, rhs);
-        let secs = secs + carry * SECS_PER_DAY as i64;
-        let (secs, carry) = div_mod_floor_64(secs, rhs);
-        let nanos = nanos + carry * NANOS_PER_SEC as i64;
-        let nanos = nanos / rhs;
-        Duration { days: days as i32, secs: secs as u32, nanos: nanos as u32 }
+        let mut secs = self.secs / *rhs as i64;
+        let carry = self.secs - secs * *rhs as i64;
+        let extra_nanos = carry * NANOS_PER_SEC as i64 / *rhs as i64;
+        let mut nanos = self.nanos / *rhs + extra_nanos as i32;
+        if nanos >= NANOS_PER_SEC {
+            nanos -= NANOS_PER_SEC;
+            secs += 1;
+        }
+        if nanos < 0 {
+            nanos += NANOS_PER_SEC;
+            secs -= 1;
+        }
+        Duration { secs: secs, nanos: nanos }
     }
 }
 
 impl fmt::Show for Duration {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let hasdate = self.days != 0;
-        let hastime = (self.secs != 0 || self.nanos != 0) || !hasdate;
+        let days = self.num_days();
+        let secs = self.secs - days * SECS_PER_DAY;
+        let hasdate = days != 0;
+        let hastime = (secs != 0 || self.nanos != 0) || !hasdate;
 
         try!(write!(f, "P"));
         if hasdate {
             // technically speaking the negative part is not the valid ISO 8601,
             // but we need to print it anyway.
-            try!(write!(f, "{}D", self.days));
+            try!(write!(f, "{}D", days));
         }
         if hastime {
             if self.nanos == 0 {
-                try!(write!(f, "T{}S", self.secs));
-            } else if self.nanos % 1_000_000 == 0 {
-                try!(write!(f, "T{}.{:03}S", self.secs, self.nanos / 1_000_000));
-            } else if self.nanos % 1_000 == 0 {
-                try!(write!(f, "T{}.{:06}S", self.secs, self.nanos / 1_000));
+                try!(write!(f, "T{}S", secs));
+            } else if self.nanos % NANOS_PER_MILLI == 0 {
+                try!(write!(f, "T{}.{:03}S", secs, self.nanos / NANOS_PER_MILLI));
+            } else if self.nanos % NANOS_PER_MICRO == 0 {
+                try!(write!(f, "T{}.{:06}S", secs, self.nanos / NANOS_PER_MICRO));
             } else {
-                try!(write!(f, "T{}.{:09}S", self.secs, self.nanos));
+                try!(write!(f, "T{}.{:09}S", secs, self.nanos));
             }
         }
         Ok(())
@@ -401,34 +330,6 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 }
 
 // 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]
 fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) {
     (div_floor_64(this, other), mod_floor_64(this, other))
@@ -459,7 +360,7 @@ fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
 
 #[cfg(test)]
 mod tests {
-    use super::{Duration, MIN_DAYS, MAX_DAYS, MIN, MAX};
+    use super::{Duration, MIN, MAX};
     use {i32, i64};
     use num::{Zero, CheckedAdd, CheckedSub};
     use option::{Some, None};
@@ -492,10 +393,8 @@ fn test_duration_num_days() {
         assert_eq!(Duration::seconds(86401).num_days(), 1);
         assert_eq!(Duration::seconds(-86399).num_days(), 0);
         assert_eq!(Duration::seconds(-86401).num_days(), -1);
-        assert_eq!(Duration::days(i32::MAX).num_days(), i32::MAX);
-        assert_eq!(Duration::days(i32::MIN).num_days(), i32::MIN);
-        assert_eq!(MAX.num_days(), MAX_DAYS);
-        assert_eq!(MIN.num_days(), MIN_DAYS);
+        assert_eq!(Duration::days(i32::MAX as i64).num_days(), i32::MAX as i64);
+        assert_eq!(Duration::days(i32::MIN as i64).num_days(), i32::MIN as i64);
     }
 
     #[test]
@@ -508,26 +407,26 @@ fn test_duration_num_seconds() {
         assert_eq!(Duration::milliseconds(1001).num_seconds(), 1);
         assert_eq!(Duration::milliseconds(-999).num_seconds(), 0);
         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);
+        assert_eq!(Duration::seconds(i64::MAX).num_seconds(), i64::MAX);
+        assert_eq!(Duration::seconds(i64::MIN).num_seconds(), i64::MIN);
+        assert_eq!(MAX.num_seconds(), i64::MAX);
+        assert_eq!(MIN.num_seconds(), i64::MIN);
     }
 
     #[test]
     fn test_duration_num_milliseconds() {
         let d: Duration = Zero::zero();
-        assert_eq!(d.num_milliseconds(), 0);
-        assert_eq!(Duration::milliseconds(1).num_milliseconds(), 1);
-        assert_eq!(Duration::milliseconds(-1).num_milliseconds(), -1);
-        assert_eq!(Duration::microseconds(999).num_milliseconds(), 0);
-        assert_eq!(Duration::microseconds(1001).num_milliseconds(), 1);
-        assert_eq!(Duration::microseconds(-999).num_milliseconds(), 0);
-        assert_eq!(Duration::microseconds(-1001).num_milliseconds(), -1);
-        assert_eq!(Duration::milliseconds(i32::MAX).num_milliseconds(), i32::MAX as i64);
-        assert_eq!(Duration::milliseconds(i32::MIN).num_milliseconds(), i32::MIN as i64);
-        assert_eq!(MAX.num_milliseconds(), (MAX_DAYS as i64 + 1) * 86400_000 - 1);
-        assert_eq!(MIN.num_milliseconds(), MIN_DAYS as i64 * 86400_000);
+        assert_eq!(d.num_milliseconds(), Some(0));
+        assert_eq!(Duration::milliseconds(1).num_milliseconds(), Some(1));
+        assert_eq!(Duration::milliseconds(-1).num_milliseconds(), Some(-1));
+        assert_eq!(Duration::microseconds(999).num_milliseconds(), Some(0));
+        assert_eq!(Duration::microseconds(1001).num_milliseconds(), Some(1));
+        assert_eq!(Duration::microseconds(-999).num_milliseconds(), Some(0));
+        assert_eq!(Duration::microseconds(-1001).num_milliseconds(), Some(-1));
+        assert_eq!(Duration::milliseconds(i64::MAX).num_milliseconds(), Some(i64::MAX));
+        assert_eq!(Duration::milliseconds(i64::MIN).num_milliseconds(), Some(i64::MIN));
+        assert_eq!(MAX.num_milliseconds(), None);
+        assert_eq!(MIN.num_milliseconds(), None);
     }
 
     #[test]
@@ -540,19 +439,19 @@ fn test_duration_num_microseconds() {
         assert_eq!(Duration::nanoseconds(1001).num_microseconds(), Some(1));
         assert_eq!(Duration::nanoseconds(-999).num_microseconds(), Some(0));
         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(i32::MIN).num_microseconds(), Some(i32::MIN as i64));
+        assert_eq!(Duration::microseconds(i64::MAX).num_microseconds(), Some(i64::MAX));
+        assert_eq!(Duration::microseconds(i64::MIN).num_microseconds(), Some(i64::MIN));
         assert_eq!(MAX.num_microseconds(), None);
         assert_eq!(MIN.num_microseconds(), None);
 
         // overflow checks
         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));
-        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));
-        assert_eq!(Duration::days((i64::MAX / MICROS_PER_DAY + 1) as i32).num_microseconds(), None);
-        assert_eq!(Duration::days((i64::MIN / 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).num_microseconds(), None);
     }
 
     #[test]
@@ -561,30 +460,30 @@ fn test_duration_num_nanoseconds() {
         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(i32::MAX).num_nanoseconds(), Some(i32::MAX as i64));
-        assert_eq!(Duration::nanoseconds(i32::MIN).num_nanoseconds(), Some(i32::MIN as i64));
+        assert_eq!(Duration::nanoseconds(i64::MAX).num_nanoseconds(), Some(i64::MAX));
+        assert_eq!(Duration::nanoseconds(i64::MIN).num_nanoseconds(), Some(i64::MIN));
         assert_eq!(MAX.num_nanoseconds(), None);
         assert_eq!(MIN.num_nanoseconds(), None);
 
         // overflow checks
         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));
-        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));
-        assert_eq!(Duration::days((i64::MAX / NANOS_PER_DAY + 1) as i32).num_nanoseconds(), None);
-        assert_eq!(Duration::days((i64::MIN / 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).num_nanoseconds(), None);
     }
 
     #[test]
     fn test_duration_checked_ops() {
-        assert_eq!(Duration::days(MAX_DAYS).checked_add(&Duration::seconds(86399)),
-                   Some(Duration::days(MAX_DAYS - 1) + Duration::seconds(86400+86399)));
-        assert!(Duration::days(MAX_DAYS).checked_add(&Duration::seconds(86400)).is_none());
+        assert_eq!(Duration::seconds(i64::MAX).checked_add(&Duration::milliseconds(999)),
+                   Some(Duration::seconds(i64::MAX - 1) + Duration::milliseconds(1999)));
+        assert!(Duration::seconds(i64::MAX).checked_add(&Duration::milliseconds(1000)).is_none());
 
-        assert_eq!(Duration::days(MIN_DAYS).checked_sub(&Duration::seconds(0)),
-                   Some(Duration::days(MIN_DAYS)));
-        assert!(Duration::days(MIN_DAYS).checked_sub(&Duration::seconds(1)).is_none());
+        assert_eq!(Duration::seconds(i64::MIN).checked_sub(&Duration::seconds(0)),
+                   Some(Duration::seconds(i64::MIN)));
+        assert!(Duration::seconds(i64::MIN).checked_sub(&Duration::seconds(1)).is_none());
     }
 
     #[test]
@@ -601,6 +500,8 @@ fn test_duration_mul() {
                    Duration::seconds(10) - Duration::nanoseconds(10));
         assert_eq!((Duration::nanoseconds(1) + Duration::seconds(1) + Duration::days(1)) * 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]
@@ -612,6 +513,13 @@ fn test_duration_div() {
         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]