about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/compiletest/runtest.rs3
-rw-r--r--src/libgreen/sched.rs3
-rw-r--r--src/libstd/io/net/tcp.rs18
-rw-r--r--src/libstd/io/net/unix.rs29
-rw-r--r--src/libstd/io/process.rs2
-rw-r--r--src/libstd/io/signal.rs7
-rw-r--r--src/libstd/io/test.rs1
-rw-r--r--src/libstd/io/timer.rs189
-rw-r--r--src/libstd/lib.rs2
-rw-r--r--src/libstd/task.rs3
-rw-r--r--src/libstd/time/duration.rs634
-rw-r--r--src/libstd/time/mod.rs15
-rw-r--r--src/libsync/comm/mod.rs6
-rw-r--r--src/test/run-pass/core-run-destroy.rs3
-rw-r--r--src/test/run-pass/issue-12684.rs4
-rw-r--r--src/test/run-pass/issue-12699.rs3
-rw-r--r--src/test/run-pass/issue-9396.rs3
-rw-r--r--src/test/run-pass/tcp-connect-timeouts.rs18
-rw-r--r--src/test/run-pass/tcp-stress.rs3
19 files changed, 867 insertions, 79 deletions
diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs
index a9c7673d4dd..318609f44fd 100644
--- a/src/compiletest/runtest.rs
+++ b/src/compiletest/runtest.rs
@@ -30,6 +30,7 @@ use std::os;
 use std::str;
 use std::string::String;
 use std::task;
+use std::time::Duration;
 use test::MetricMap;
 
 pub fn run(config: Config, testfile: String) {
@@ -400,7 +401,7 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
                 .expect(format!("failed to exec `{}`", config.adb_path).as_slice());
             loop {
                 //waiting 1 second for gdbserver start
-                timer::sleep(1000);
+                timer::sleep(Duration::milliseconds(1000));
                 let result = task::try(proc() {
                     tcp::TcpStream::connect("127.0.0.1", 5039).unwrap();
                 });
diff --git a/src/libgreen/sched.rs b/src/libgreen/sched.rs
index b9144047df5..e0415470828 100644
--- a/src/libgreen/sched.rs
+++ b/src/libgreen/sched.rs
@@ -1029,6 +1029,7 @@ mod test {
     use std::rt::task::TaskOpts;
     use std::rt::task::Task;
     use std::rt::local::Local;
+    use std::time::Duration;
 
     use {TaskState, PoolConfig, SchedPool};
     use basic;
@@ -1291,7 +1292,7 @@ mod test {
         // doesn't exit before emptying the work queue
         pool.spawn(TaskOpts::new(), proc() {
             spawn(proc() {
-                timer::sleep(10);
+                timer::sleep(Duration::milliseconds(10));
             });
         });
 
diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs
index 0b0c22ed887..5c91c48c55d 100644
--- a/src/libstd/io/net/tcp.rs
+++ b/src/libstd/io/net/tcp.rs
@@ -27,6 +27,7 @@ use io::net::addrinfo::get_host_addresses;
 use io::net::ip::SocketAddr;
 use io::{IoError, ConnectionFailed, InvalidInput};
 use io::{Reader, Writer, Listener, Acceptor};
+use io::{standard_error, TimedOut};
 use from_str::FromStr;
 use kinds::Send;
 use option::{None, Some, Option};
@@ -34,6 +35,7 @@ use boxed::Box;
 use rt::rtio::{IoFactory, LocalIo, RtioSocket, RtioTcpListener};
 use rt::rtio::{RtioTcpAcceptor, RtioTcpStream};
 use rt::rtio;
+use time::Duration;
 
 /// A structure which represents a TCP stream between a local socket and a
 /// remote socket.
@@ -92,7 +94,7 @@ impl TcpStream {
     }
 
     /// Creates a TCP connection to a remote socket address, timing out after
-    /// the specified number of milliseconds.
+    /// 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
@@ -100,13 +102,20 @@ impl TcpStream {
     ///
     /// 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`.
     #[experimental = "the timeout argument may eventually change types"]
     pub fn connect_timeout(addr: SocketAddr,
-                           timeout_ms: u64) -> IoResult<TcpStream> {
+                           timeout: Duration) -> IoResult<TcpStream> {
+        if timeout <= Duration::milliseconds(0) {
+            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_ms)).map(TcpStream::new)
+            io.tcp_connect(addr, Some(timeout.num_milliseconds() as u64)).map(TcpStream::new)
         }).map_err(IoError::from_rtio_error)
     }
 
@@ -164,13 +173,14 @@ impl TcpStream {
     /// # #![allow(unused_must_use)]
     /// use std::io::timer;
     /// use std::io::TcpStream;
+    /// use std::time::Duration;
     ///
     /// let mut stream = TcpStream::connect("127.0.0.1", 34254).unwrap();
     /// let stream2 = stream.clone();
     ///
     /// spawn(proc() {
     ///     // close this stream after one second
-    ///     timer::sleep(1000);
+    ///     timer::sleep(Duration::seconds(1));
     ///     let mut stream = stream2;
     ///     stream.close_read();
     /// });
diff --git a/src/libstd/io/net/unix.rs b/src/libstd/io/net/unix.rs
index 5e7c4214977..d88af8dd30a 100644
--- a/src/libstd/io/net/unix.rs
+++ b/src/libstd/io/net/unix.rs
@@ -29,10 +29,12 @@ use prelude::*;
 use c_str::ToCStr;
 use clone::Clone;
 use io::{Listener, Acceptor, Reader, Writer, IoResult, IoError};
+use io::{standard_error, TimedOut};
 use kinds::Send;
 use boxed::Box;
 use rt::rtio::{IoFactory, LocalIo, RtioUnixListener};
 use rt::rtio::{RtioUnixAcceptor, RtioPipe};
+use time::Duration;
 
 /// A stream which communicates over a named pipe.
 pub struct UnixStream {
@@ -66,11 +68,18 @@ impl UnixStream {
     ///
     /// This function is similar to `connect`, except that if `timeout_ms`
     /// 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`.
     #[experimental = "the timeout argument is likely to change types"]
     pub fn connect_timeout<P: ToCStr>(path: &P,
-                                      timeout_ms: u64) -> IoResult<UnixStream> {
+                                      timeout: Duration) -> IoResult<UnixStream> {
+        if timeout <= Duration::milliseconds(0) {
+            return Err(standard_error(TimedOut));
+        }
+
         LocalIo::maybe_raise(|io| {
-            let s = io.unix_connect(&path.to_c_str(), Some(timeout_ms));
+            let s = io.unix_connect(&path.to_c_str(), Some(timeout.num_milliseconds() as u64));
             s.map(|p| UnixStream { obj: p })
         }).map_err(IoError::from_rtio_error)
     }
@@ -499,13 +508,25 @@ mod tests {
 
     iotest!(fn connect_timeout_error() {
         let addr = next_test_unix();
-        assert!(UnixStream::connect_timeout(&addr, 100).is_err());
+        assert!(UnixStream::connect_timeout(&addr, Duration::milliseconds(100)).is_err());
     })
 
     iotest!(fn connect_timeout_success() {
         let addr = next_test_unix();
         let _a = UnixListener::bind(&addr).unwrap().listen().unwrap();
-        assert!(UnixStream::connect_timeout(&addr, 100).is_ok());
+        assert!(UnixStream::connect_timeout(&addr, Duration::milliseconds(100)).is_ok());
+    })
+
+    iotest!(fn connect_timeout_zero() {
+        let addr = next_test_unix();
+        let _a = UnixListener::bind(&addr).unwrap().listen().unwrap();
+        assert!(UnixStream::connect_timeout(&addr, Duration::milliseconds(0)).is_err());
+    })
+
+    iotest!(fn connect_timeout_negative() {
+        let addr = next_test_unix();
+        let _a = UnixListener::bind(&addr).unwrap().listen().unwrap();
+        assert!(UnixStream::connect_timeout(&addr, Duration::milliseconds(-1)).is_err());
     })
 
     iotest!(fn close_readwrite_smoke() {
diff --git a/src/libstd/io/process.rs b/src/libstd/io/process.rs
index c82b4831341..3dd4343c5f4 100644
--- a/src/libstd/io/process.rs
+++ b/src/libstd/io/process.rs
@@ -976,7 +976,7 @@ mod tests {
                 assert!(!p.wait().unwrap().success());
                 return
             }
-            timer::sleep(100);
+            timer::sleep(Duration::milliseconds(100));
         }
         fail!("never saw the child go away");
     })
diff --git a/src/libstd/io/signal.rs b/src/libstd/io/signal.rs
index c126866e715..10739c70143 100644
--- a/src/libstd/io/signal.rs
+++ b/src/libstd/io/signal.rs
@@ -167,6 +167,7 @@ mod test_unix {
     use comm::Empty;
     use io::timer;
     use super::{Listener, Interrupt};
+    use time::Duration;
 
     fn sigint() {
         unsafe {
@@ -179,7 +180,7 @@ mod test_unix {
         let mut signal = Listener::new();
         signal.register(Interrupt).unwrap();
         sigint();
-        timer::sleep(10);
+        timer::sleep(Duration::milliseconds(10));
         match signal.rx.recv() {
             Interrupt => (),
             s => fail!("Expected Interrupt, got {:?}", s),
@@ -193,7 +194,7 @@ mod test_unix {
         s1.register(Interrupt).unwrap();
         s2.register(Interrupt).unwrap();
         sigint();
-        timer::sleep(10);
+        timer::sleep(Duration::milliseconds(10));
         match s1.rx.recv() {
             Interrupt => (),
             s => fail!("Expected Interrupt, got {:?}", s),
@@ -212,7 +213,7 @@ mod test_unix {
         s2.register(Interrupt).unwrap();
         s2.unregister(Interrupt);
         sigint();
-        timer::sleep(10);
+        timer::sleep(Duration::milliseconds(10));
         assert_eq!(s2.rx.try_recv(), Err(Empty));
     }
 }
diff --git a/src/libstd/io/test.rs b/src/libstd/io/test.rs
index 331cfa1a59e..079a9aef648 100644
--- a/src/libstd/io/test.rs
+++ b/src/libstd/io/test.rs
@@ -39,6 +39,7 @@ macro_rules! iotest (
             use io::process::*;
             use rt::running_on_valgrind;
             use str;
+            use time::Duration;
 
             fn f() $b
 
diff --git a/src/libstd/io/timer.rs b/src/libstd/io/timer.rs
index 1c9e428dcad..39c6c74e45e 100644
--- a/src/libstd/io/timer.rs
+++ b/src/libstd/io/timer.rs
@@ -17,7 +17,10 @@ and create receivers which will receive notifications after a period of time.
 
 */
 
+// FIXME: These functions take Durations but only pass ms to the backend impls.
+
 use comm::{Receiver, Sender, channel};
+use time::Duration;
 use io::{IoResult, IoError};
 use kinds::Send;
 use boxed::Box;
@@ -35,15 +38,16 @@ use rt::rtio::{IoFactory, LocalIo, RtioTimer, Callback};
 /// # fn main() {}
 /// # fn foo() {
 /// use std::io::Timer;
+/// use std::time::Duration;
 ///
 /// let mut timer = Timer::new().unwrap();
-/// timer.sleep(10); // block the task for awhile
+/// timer.sleep(Duration::milliseconds(10)); // block the task for awhile
 ///
-/// let timeout = timer.oneshot(10);
+/// let timeout = timer.oneshot(Duration::milliseconds(10));
 /// // do some work
 /// timeout.recv(); // wait for the timeout to expire
 ///
-/// let periodic = timer.periodic(10);
+/// let periodic = timer.periodic(Duration::milliseconds(10));
 /// loop {
 ///     periodic.recv();
 ///     // this loop is only executed once every 10ms
@@ -58,9 +62,10 @@ use rt::rtio::{IoFactory, LocalIo, RtioTimer, Callback};
 /// # fn main() {}
 /// # fn foo() {
 /// use std::io::timer;
+/// use std::time::Duration;
 ///
 /// // Put this task to sleep for 5 seconds
-/// timer::sleep(5000);
+/// timer::sleep(Duration::seconds(5));
 /// # }
 /// ```
 pub struct Timer {
@@ -69,12 +74,15 @@ pub struct Timer {
 
 struct TimerCallback { tx: Sender<()> }
 
-/// Sleep the current task for `msecs` milliseconds.
-pub fn sleep(msecs: u64) {
+/// Sleep the current task for the specified duration.
+///
+/// When provided a zero or negative `duration`, the function will
+/// return immediately.
+pub fn sleep(duration: Duration) {
     let timer = Timer::new();
     let mut timer = timer.ok().expect("timer::sleep: could not create a Timer");
 
-    timer.sleep(msecs)
+    timer.sleep(duration)
 }
 
 impl Timer {
@@ -87,16 +95,22 @@ impl Timer {
         }).map_err(IoError::from_rtio_error)
     }
 
-    /// Blocks the current task for `msecs` milliseconds.
+    /// Blocks the current task for the specified duration.
     ///
     /// Note that this function will cause any other receivers for this timer to
     /// be invalidated (the other end will be closed).
-    pub fn sleep(&mut self, msecs: u64) {
-        self.obj.sleep(msecs);
+    ///
+    /// When provided a zero or negative `duration`, the function will
+    /// return immediately.
+    pub fn sleep(&mut self, duration: Duration) {
+        // Short-circuit the timer backend for 0 duration
+        let ms = in_ms_u64(duration);
+        if ms == 0 { return }
+        self.obj.sleep(ms);
     }
 
     /// Creates a oneshot receiver which will have a notification sent when
-    /// `msecs` milliseconds has elapsed.
+    /// the specified duration has elapsed.
     ///
     /// This does *not* block the current task, but instead returns immediately.
     ///
@@ -111,9 +125,10 @@ impl Timer {
     ///
     /// ```rust
     /// use std::io::Timer;
+    /// use std::time::Duration;
     ///
     /// let mut timer = Timer::new().unwrap();
-    /// let ten_milliseconds = timer.oneshot(10);
+    /// let ten_milliseconds = timer.oneshot(Duration::milliseconds(10));
     ///
     /// for _ in range(0u, 100) { /* do work */ }
     ///
@@ -123,24 +138,33 @@ impl Timer {
     ///
     /// ```rust
     /// use std::io::Timer;
+    /// use std::time::Duration;
     ///
     /// // Incorrect, method chaining-style:
-    /// let mut five_ms = Timer::new().unwrap().oneshot(5);
+    /// let mut five_ms = Timer::new().unwrap().oneshot(Duration::milliseconds(5));
     /// // The timer object was destroyed, so this will always fail:
     /// // five_ms.recv()
     /// ```
-    pub fn oneshot(&mut self, msecs: u64) -> Receiver<()> {
+    ///
+    /// When provided a zero or negative `duration`, the message will
+    /// be sent immediately.
+    pub fn oneshot(&mut self, duration: Duration) -> Receiver<()> {
         let (tx, rx) = channel();
-        self.obj.oneshot(msecs, box TimerCallback { tx: tx });
+        // Short-circuit the timer backend for 0 duration
+        if in_ms_u64(duration) != 0 {
+            self.obj.oneshot(in_ms_u64(duration), box TimerCallback { tx: tx });
+        } else {
+            tx.send(());
+        }
         return rx
     }
 
     /// Creates a receiver which will have a continuous stream of notifications
-    /// being sent every `msecs` milliseconds.
+    /// being sent each time the specified duration has elapsed.
     ///
     /// This does *not* block the current task, but instead returns
     /// immediately. The first notification will not be received immediately,
-    /// but rather after `msec` milliseconds have passed.
+    /// but rather after the first duration.
     ///
     /// Note that this invalidates any previous receiver which has been created
     /// by this timer, and that the returned receiver will be invalidated once
@@ -153,9 +177,10 @@ impl Timer {
     ///
     /// ```rust
     /// use std::io::Timer;
+    /// use std::time::Duration;
     ///
     /// let mut timer = Timer::new().unwrap();
-    /// let ten_milliseconds = timer.periodic(10);
+    /// let ten_milliseconds = timer.periodic(Duration::milliseconds(10));
     ///
     /// for _ in range(0u, 100) { /* do work */ }
     ///
@@ -171,15 +196,24 @@ impl Timer {
     ///
     /// ```rust
     /// use std::io::Timer;
+    /// use std::time::Duration;
     ///
     /// // Incorrect, method chaining-style.
-    /// let mut five_ms = Timer::new().unwrap().periodic(5);
+    /// let mut five_ms = Timer::new().unwrap().periodic(Duration::milliseconds(5));
     /// // The timer object was destroyed, so this will always fail:
     /// // five_ms.recv()
     /// ```
-    pub fn periodic(&mut self, msecs: u64) -> Receiver<()> {
+    ///
+    /// When provided a zero or negative `duration`, the messages will
+    /// be sent without delay.
+    pub fn periodic(&mut self, duration: Duration) -> Receiver<()> {
+        let ms = in_ms_u64(duration);
+        // FIXME: The backend implementations don't ever send a message
+        // if given a 0 ms duration. Temporarily using 1ms. It's
+        // not clear what use a 0ms period is anyway...
+        let ms = if ms == 0 { 1 } else { ms };
         let (tx, rx) = channel();
-        self.obj.period(msecs, box TimerCallback { tx: tx });
+        self.obj.period(ms, box TimerCallback { tx: tx });
         return rx
     }
 }
@@ -190,42 +224,48 @@ impl Callback for TimerCallback {
     }
 }
 
+fn in_ms_u64(d: Duration) -> u64 {
+    let ms = d.num_milliseconds();
+    if ms < 0 { return 0 };
+    return ms as u64;
+}
+
 #[cfg(test)]
 mod test {
     iotest!(fn test_io_timer_sleep_simple() {
         let mut timer = Timer::new().unwrap();
-        timer.sleep(1);
+        timer.sleep(Duration::milliseconds(1));
     })
 
     iotest!(fn test_io_timer_sleep_oneshot() {
         let mut timer = Timer::new().unwrap();
-        timer.oneshot(1).recv();
+        timer.oneshot(Duration::milliseconds(1)).recv();
     })
 
     iotest!(fn test_io_timer_sleep_oneshot_forget() {
         let mut timer = Timer::new().unwrap();
-        timer.oneshot(100000000000);
+        timer.oneshot(Duration::milliseconds(100000000));
     })
 
     iotest!(fn oneshot_twice() {
         let mut timer = Timer::new().unwrap();
-        let rx1 = timer.oneshot(10000);
-        let rx = timer.oneshot(1);
+        let rx1 = timer.oneshot(Duration::milliseconds(10000));
+        let rx = timer.oneshot(Duration::milliseconds(1));
         rx.recv();
         assert_eq!(rx1.recv_opt(), Err(()));
     })
 
     iotest!(fn test_io_timer_oneshot_then_sleep() {
         let mut timer = Timer::new().unwrap();
-        let rx = timer.oneshot(100000000000);
-        timer.sleep(1); // this should invalidate rx
+        let rx = timer.oneshot(Duration::milliseconds(100000000));
+        timer.sleep(Duration::milliseconds(1)); // this should invalidate rx
 
         assert_eq!(rx.recv_opt(), Err(()));
     })
 
     iotest!(fn test_io_timer_sleep_periodic() {
         let mut timer = Timer::new().unwrap();
-        let rx = timer.periodic(1);
+        let rx = timer.periodic(Duration::milliseconds(1));
         rx.recv();
         rx.recv();
         rx.recv();
@@ -233,60 +273,60 @@ mod test {
 
     iotest!(fn test_io_timer_sleep_periodic_forget() {
         let mut timer = Timer::new().unwrap();
-        timer.periodic(100000000000);
+        timer.periodic(Duration::milliseconds(100000000));
     })
 
     iotest!(fn test_io_timer_sleep_standalone() {
-        sleep(1)
+        sleep(Duration::milliseconds(1))
     })
 
     iotest!(fn oneshot() {
         let mut timer = Timer::new().unwrap();
 
-        let rx = timer.oneshot(1);
+        let rx = timer.oneshot(Duration::milliseconds(1));
         rx.recv();
         assert!(rx.recv_opt().is_err());
 
-        let rx = timer.oneshot(1);
+        let rx = timer.oneshot(Duration::milliseconds(1));
         rx.recv();
         assert!(rx.recv_opt().is_err());
     })
 
     iotest!(fn override() {
         let mut timer = Timer::new().unwrap();
-        let orx = timer.oneshot(100);
-        let prx = timer.periodic(100);
-        timer.sleep(1);
+        let orx = timer.oneshot(Duration::milliseconds(100));
+        let prx = timer.periodic(Duration::milliseconds(100));
+        timer.sleep(Duration::milliseconds(1));
         assert_eq!(orx.recv_opt(), Err(()));
         assert_eq!(prx.recv_opt(), Err(()));
-        timer.oneshot(1).recv();
+        timer.oneshot(Duration::milliseconds(1)).recv();
     })
 
     iotest!(fn period() {
         let mut timer = Timer::new().unwrap();
-        let rx = timer.periodic(1);
+        let rx = timer.periodic(Duration::milliseconds(1));
         rx.recv();
         rx.recv();
-        let rx2 = timer.periodic(1);
+        let rx2 = timer.periodic(Duration::milliseconds(1));
         rx2.recv();
         rx2.recv();
     })
 
     iotest!(fn sleep() {
         let mut timer = Timer::new().unwrap();
-        timer.sleep(1);
-        timer.sleep(1);
+        timer.sleep(Duration::milliseconds(1));
+        timer.sleep(Duration::milliseconds(1));
     })
 
     iotest!(fn oneshot_fail() {
         let mut timer = Timer::new().unwrap();
-        let _rx = timer.oneshot(1);
+        let _rx = timer.oneshot(Duration::milliseconds(1));
         fail!();
     } #[should_fail])
 
     iotest!(fn period_fail() {
         let mut timer = Timer::new().unwrap();
-        let _rx = timer.periodic(1);
+        let _rx = timer.periodic(Duration::milliseconds(1));
         fail!();
     } #[should_fail])
 
@@ -298,7 +338,7 @@ mod test {
     iotest!(fn closing_channel_during_drop_doesnt_kill_everything() {
         // see issue #10375
         let mut timer = Timer::new().unwrap();
-        let timer_rx = timer.periodic(1000);
+        let timer_rx = timer.periodic(Duration::milliseconds(1000));
 
         spawn(proc() {
             let _ = timer_rx.recv_opt();
@@ -311,31 +351,31 @@ mod test {
     iotest!(fn reset_doesnt_switch_tasks() {
         // similar test to the one above.
         let mut timer = Timer::new().unwrap();
-        let timer_rx = timer.periodic(1000);
+        let timer_rx = timer.periodic(Duration::milliseconds(1000));
 
         spawn(proc() {
             let _ = timer_rx.recv_opt();
         });
 
-        timer.oneshot(1);
+        timer.oneshot(Duration::milliseconds(1));
     })
 
     iotest!(fn reset_doesnt_switch_tasks2() {
         // similar test to the one above.
         let mut timer = Timer::new().unwrap();
-        let timer_rx = timer.periodic(1000);
+        let timer_rx = timer.periodic(Duration::milliseconds(1000));
 
         spawn(proc() {
             let _ = timer_rx.recv_opt();
         });
 
-        timer.sleep(1);
+        timer.sleep(Duration::milliseconds(1));
     })
 
     iotest!(fn sender_goes_away_oneshot() {
         let rx = {
             let mut timer = Timer::new().unwrap();
-            timer.oneshot(1000)
+            timer.oneshot(Duration::milliseconds(1000))
         };
         assert_eq!(rx.recv_opt(), Err(()));
     })
@@ -343,26 +383,67 @@ mod test {
     iotest!(fn sender_goes_away_period() {
         let rx = {
             let mut timer = Timer::new().unwrap();
-            timer.periodic(1000)
+            timer.periodic(Duration::milliseconds(1000))
         };
         assert_eq!(rx.recv_opt(), Err(()));
     })
 
     iotest!(fn receiver_goes_away_oneshot() {
         let mut timer1 = Timer::new().unwrap();
-        timer1.oneshot(1);
+        timer1.oneshot(Duration::milliseconds(1));
         let mut timer2 = Timer::new().unwrap();
         // while sleeping, the previous timer should fire and not have its
         // callback do something terrible.
-        timer2.sleep(2);
+        timer2.sleep(Duration::milliseconds(2));
     })
 
     iotest!(fn receiver_goes_away_period() {
         let mut timer1 = Timer::new().unwrap();
-        timer1.periodic(1);
+        timer1.periodic(Duration::milliseconds(1));
         let mut timer2 = Timer::new().unwrap();
         // while sleeping, the previous timer should fire and not have its
         // callback do something terrible.
-        timer2.sleep(2);
+        timer2.sleep(Duration::milliseconds(2));
+    })
+
+    iotest!(fn sleep_zero() {
+        let mut timer = Timer::new().unwrap();
+        timer.sleep(Duration::milliseconds(0));
+    })
+
+    iotest!(fn sleep_negative() {
+        let mut timer = Timer::new().unwrap();
+        timer.sleep(Duration::milliseconds(-1000000));
+    })
+
+    iotest!(fn oneshot_zero() {
+        let mut timer = Timer::new().unwrap();
+        let rx = timer.oneshot(Duration::milliseconds(0));
+        rx.recv();
+    })
+
+    iotest!(fn oneshot_negative() {
+        let mut timer = Timer::new().unwrap();
+        let rx = timer.oneshot(Duration::milliseconds(-1000000));
+        rx.recv();
+    })
+
+    iotest!(fn periodic_zero() {
+        let mut timer = Timer::new().unwrap();
+        let rx = timer.periodic(Duration::milliseconds(0));
+        rx.recv();
+        rx.recv();
+        rx.recv();
+        rx.recv();
     })
+
+    iotest!(fn periodic_negative() {
+        let mut timer = Timer::new().unwrap();
+        let rx = timer.periodic(Duration::milliseconds(-1000000));
+        rx.recv();
+        rx.recv();
+        rx.recv();
+        rx.recv();
+    })
+
 }
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index 20fc7efeb57..103cd574e73 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -233,6 +233,8 @@ pub mod ascii;
 #[cfg(not(test))]
 pub mod gc;
 
+pub mod time;
+
 /* Common traits */
 
 pub mod from_str;
diff --git a/src/libstd/task.rs b/src/libstd/task.rs
index 19ad81a0483..9cace9c80ef 100644
--- a/src/libstd/task.rs
+++ b/src/libstd/task.rs
@@ -664,10 +664,11 @@ mod test {
 #[test]
 fn task_abort_no_kill_runtime() {
     use std::io::timer;
+    use time::Duration;
     use mem;
 
     let tb = TaskBuilder::new();
     let rx = tb.try_future(proc() {});
     mem::drop(rx);
-    timer::sleep(1000);
+    timer::sleep(Duration::milliseconds(1000));
 }
diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs
new file mode 100644
index 00000000000..545d1f2aab4
--- /dev/null
+++ b/src/libstd/time/duration.rs
@@ -0,0 +1,634 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Temporal quantification
+
+#![experimental]
+
+use {fmt, i32};
+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 seconds.
+static NANOS_PER_SEC: i32 = 1_000_000_000;
+/// The number of (non-leap) seconds in days.
+static SECS_PER_DAY: i32 = 86400;
+
+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(PartialEq, Eq, PartialOrd, Ord)]
+pub struct Duration {
+    days: i32,
+    secs: u32,  // Always < SECS_PER_DAY
+    nanos: u32, // Always < NANOS_PR_SECOND
+}
+
+/// The minimum possible `Duration`.
+pub static MIN: Duration = Duration { days: MIN_DAYS, secs: 0, 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 };
+
+impl Duration {
+    /// Makes a new `Duration` with given number of weeks.
+    /// Equivalent to `Duration::new(weeks * 7, 0, 0)` 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)
+    }
+
+    /// Makes a new `Duration` with given number of days.
+    /// Equivalent to `Duration::new(days, 0, 0)`.
+    #[inline]
+    pub fn days(days: i32) -> Duration {
+        Duration { days: days, secs: 0, nanos: 0 }
+    }
+
+    /// Makes a new `Duration` with given number of hours.
+    /// Equivalent to `Duration::new(0, hours * 3600, 0)` with overflow checks.
+    #[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) }
+    }
+
+    /// Makes a new `Duration` with given number of minutes.
+    /// Equivalent to `Duration::new(0, mins * 60, 0)` with overflow checks.
+    #[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) }
+    }
+
+    /// 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) }
+    }
+
+    /// 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) }
+    }
+
+    /// 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) }
+    }
+
+    /// 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)
+    }
+
+    /// Returns the total number of whole weeks in the duration.
+    #[inline]
+    pub fn num_weeks(&self) -> i32 {
+        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
+        }
+    }
+
+    /// Returns the total number of whole hours in the duration.
+    #[inline]
+    pub fn num_hours(&self) -> i64 {
+        self.num_seconds() / 3600
+    }
+
+    /// Returns the total number of whole minutes in the duration.
+    #[inline]
+    pub fn num_minutes(&self) -> i64 {
+        self.num_seconds() / 60
+    }
+
+    /// 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 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)
+        }
+        if self.days < 0 {-millis(self.to_negated_tuple_64())} else {millis(self.to_tuple_64())}
+    }
+
+    /// Returns the total number of whole microseconds in the duration,
+    /// or `None` on the overflow (exceeding 2^63 microseconds in either directions).
+    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())
+        }
+    }
+
+    /// Returns the total number of whole nanoseconds in the duration,
+    /// or `None` on the overflow (exceeding 2^63 nanoseconds in either directions).
+    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())
+        }
+    }
+}
+
+impl num::Bounded for Duration {
+    #[inline] fn min_value() -> Duration { MIN }
+    #[inline] fn max_value() -> Duration { MAX }
+}
+
+impl num::Zero for Duration {
+    #[inline]
+    fn zero() -> Duration {
+        Duration { days: 0, secs: 0, nanos: 0 }
+    }
+
+    #[inline]
+    fn is_zero(&self) -> bool {
+        self.days == 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
+    }
+}
+
+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;
+            secs += 1;
+        }
+        if secs >= SECS_PER_DAY as u32 {
+            secs -= SECS_PER_DAY as u32;
+            days += 1;
+        }
+        Duration { days: days, 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 nanos = self.nanos + rhs.nanos;
+        if nanos >= NANOS_PER_SEC as u32 {
+            nanos -= NANOS_PER_SEC as u32;
+            secs += 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 })
+    }
+}
+
+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;
+        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 }
+    }
+}
+
+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;
+        if nanos < 0 {
+            nanos += NANOS_PER_SEC;
+            secs -= 1;
+        }
+        if secs < 0 {
+            secs += SECS_PER_DAY;
+            days = try_opt!(days.checked_sub(&1));
+        }
+        Some(Duration { days: days, secs: secs as u32, nanos: nanos as u32 })
+    }
+}
+
+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 }
+    }
+}
+
+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 }
+    }
+}
+
+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;
+
+        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));
+        }
+        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));
+            } else {
+                try!(write!(f, "T{}.{:09}S", self.secs, self.nanos));
+            }
+        }
+        Ok(())
+    }
+}
+
+// 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))
+}
+
+#[inline]
+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 {
+    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) {
+    (this / other, this % other)
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{Duration, MIN_DAYS, MAX_DAYS, MIN, MAX};
+    use {i32, i64};
+    use num::{Zero, CheckedAdd, CheckedSub};
+    use option::{Some, None};
+    use to_string::ToString;
+
+    #[test]
+    fn test_duration() {
+        let d: Duration = Zero::zero();
+        assert_eq!(d, Zero::zero());
+        assert!(Duration::seconds(1) != Zero::zero());
+        assert_eq!(Duration::seconds(1) + Duration::seconds(2), Duration::seconds(3));
+        assert_eq!(Duration::seconds(86399) + Duration::seconds(4),
+                   Duration::days(1) + Duration::seconds(3));
+        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(2) + Duration::seconds(86399) + Duration::nanoseconds(1234567890),
+                   Duration::days(3) + Duration::nanoseconds(234567890));
+        assert_eq!(-Duration::days(3), Duration::days(-3));
+        assert_eq!(-(Duration::days(3) + Duration::seconds(70)),
+                   Duration::days(-4) + Duration::seconds(86400-70));
+    }
+
+    #[test]
+    fn test_duration_num_days() {
+        let d: Duration = Zero::zero();
+        assert_eq!(d.num_days(), 0);
+        assert_eq!(Duration::days(1).num_days(), 1);
+        assert_eq!(Duration::days(-1).num_days(), -1);
+        assert_eq!(Duration::seconds(86399).num_days(), 0);
+        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);
+    }
+
+    #[test]
+    fn test_duration_num_seconds() {
+        let d: Duration = Zero::zero();
+        assert_eq!(d.num_seconds(), 0);
+        assert_eq!(Duration::seconds(1).num_seconds(), 1);
+        assert_eq!(Duration::seconds(-1).num_seconds(), -1);
+        assert_eq!(Duration::milliseconds(999).num_seconds(), 0);
+        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);
+    }
+
+    #[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);
+    }
+
+    #[test]
+    fn test_duration_num_microseconds() {
+        let d: Duration = Zero::zero();
+        assert_eq!(d.num_microseconds(), Some(0));
+        assert_eq!(Duration::microseconds(1).num_microseconds(), Some(1));
+        assert_eq!(Duration::microseconds(-1).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::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!(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(),
+                   Some(i64::MAX / MICROS_PER_DAY * MICROS_PER_DAY));
+        assert_eq!(Duration::days((i64::MIN / MICROS_PER_DAY) as i32).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);
+    }
+
+    #[test]
+    fn test_duration_num_nanoseconds() {
+        let d: Duration = Zero::zero();
+        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!(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(),
+                   Some(i64::MAX / NANOS_PER_DAY * NANOS_PER_DAY));
+        assert_eq!(Duration::days((i64::MIN / NANOS_PER_DAY) as i32).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);
+    }
+
+    #[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::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());
+    }
+
+    #[test]
+    fn test_duration_mul() {
+        let d: Duration = Zero::zero();
+        assert_eq!(d * i32::MAX, d);
+        assert_eq!(d * i32::MIN, d);
+        assert_eq!(Duration::nanoseconds(1) * 0, Zero::zero());
+        assert_eq!(Duration::nanoseconds(1) * 1, Duration::nanoseconds(1));
+        assert_eq!(Duration::nanoseconds(1) * 1_000_000_000, Duration::seconds(1));
+        assert_eq!(Duration::nanoseconds(1) * -1_000_000_000, -Duration::seconds(1));
+        assert_eq!(-Duration::nanoseconds(1) * 1_000_000_000, -Duration::seconds(1));
+        assert_eq!(Duration::nanoseconds(30) * 333_333_333,
+                   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));
+    }
+
+    #[test]
+    fn test_duration_div() {
+        let d: Duration = Zero::zero();
+        assert_eq!(d / i32::MAX, d);
+        assert_eq!(d / i32::MIN, d);
+        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));
+    }
+
+    #[test]
+    fn test_duration_fmt() {
+        let d: Duration = Zero::zero();
+        assert_eq!(d.to_string(), "PT0S".to_string());
+        assert_eq!(Duration::days(42).to_string(), "P42D".to_string());
+        assert_eq!(Duration::days(-42).to_string(), "P-42D".to_string());
+        assert_eq!(Duration::seconds(42).to_string(), "PT42S".to_string());
+        assert_eq!(Duration::milliseconds(42).to_string(), "PT0.042S".to_string());
+        assert_eq!(Duration::microseconds(42).to_string(), "PT0.000042S".to_string());
+        assert_eq!(Duration::nanoseconds(42).to_string(), "PT0.000000042S".to_string());
+        assert_eq!((Duration::days(7) + Duration::milliseconds(6543)).to_string(),
+                   "P7DT6.543S".to_string());
+
+        // the format specifier should have no effect on `Duration`
+        assert_eq!(format!("{:30}", Duration::days(1) + Duration::milliseconds(2345)),
+                   "P1DT2.345S".to_string());
+    }
+}
diff --git a/src/libstd/time/mod.rs b/src/libstd/time/mod.rs
new file mode 100644
index 00000000000..436fa5ebdea
--- /dev/null
+++ b/src/libstd/time/mod.rs
@@ -0,0 +1,15 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Temporal quantification.
+
+pub use self::duration::Duration;
+
+pub mod duration;
diff --git a/src/libsync/comm/mod.rs b/src/libsync/comm/mod.rs
index 45016b97566..e4df661b562 100644
--- a/src/libsync/comm/mod.rs
+++ b/src/libsync/comm/mod.rs
@@ -128,10 +128,11 @@
 //!
 //! ```no_run
 //! use std::io::timer::Timer;
+//! use std::time::Duration;
 //!
 //! let (tx, rx) = channel::<int>();
 //! let mut timer = Timer::new().unwrap();
-//! let timeout = timer.oneshot(10000);
+//! let timeout = timer.oneshot(Duration::seconds(10));
 //!
 //! loop {
 //!     select! {
@@ -150,12 +151,13 @@
 //!
 //! ```no_run
 //! use std::io::timer::Timer;
+//! use std::time::Duration;
 //!
 //! let (tx, rx) = channel::<int>();
 //! let mut timer = Timer::new().unwrap();
 //!
 //! loop {
-//!     let timeout = timer.oneshot(5000);
+//!     let timeout = timer.oneshot(Duration::seconds(5));
 //!
 //!     select! {
 //!         val = rx.recv() => println!("Received {}", val),
diff --git a/src/test/run-pass/core-run-destroy.rs b/src/test/run-pass/core-run-destroy.rs
index 34f1e681608..9cee83a8598 100644
--- a/src/test/run-pass/core-run-destroy.rs
+++ b/src/test/run-pass/core-run-destroy.rs
@@ -25,6 +25,7 @@ extern crate green;
 extern crate rustuv;
 
 use std::io::{Process, Command};
+use std::time::Duration;
 
 macro_rules! succeed( ($e:expr) => (
     match $e { Ok(..) => {}, Err(e) => fail!("failure: {}", e) }
@@ -115,7 +116,7 @@ pub fn test_destroy_actually_kills(force: bool) {
     // Don't let this test time out, this should be quick
     let (tx, rx1) = channel();
     let mut t = timer::Timer::new().unwrap();
-    let rx2 = t.oneshot(1000);
+    let rx2 = t.oneshot(Duration::milliseconds(1000));
     spawn(proc() {
         select! {
             () = rx2.recv() => unsafe { libc::exit(1) },
diff --git a/src/test/run-pass/issue-12684.rs b/src/test/run-pass/issue-12684.rs
index 37e675b52eb..e21338746be 100644
--- a/src/test/run-pass/issue-12684.rs
+++ b/src/test/run-pass/issue-12684.rs
@@ -13,6 +13,8 @@ extern crate native;
 extern crate green;
 extern crate rustuv;
 
+use std::time::Duration;
+
 #[start]
 fn start(argc: int, argv: *const *const u8) -> int {
     green::start(argc, argv, rustuv::event_loop, main)
@@ -24,6 +26,6 @@ fn main() {
 
 fn customtask() {
     let mut timer = std::io::timer::Timer::new().unwrap();
-    let periodic = timer.periodic(10);
+    let periodic = timer.periodic(Duration::milliseconds(10));
     periodic.recv();
 }
diff --git a/src/test/run-pass/issue-12699.rs b/src/test/run-pass/issue-12699.rs
index c24128f97e3..6b6e770bc99 100644
--- a/src/test/run-pass/issue-12699.rs
+++ b/src/test/run-pass/issue-12699.rs
@@ -12,6 +12,7 @@
 extern crate native;
 
 use std::io::timer;
+use std::time::Duration;
 
 #[start]
 fn start(argc: int, argv: *const *const u8) -> int {
@@ -19,5 +20,5 @@ fn start(argc: int, argv: *const *const u8) -> int {
 }
 
 fn main() {
-    timer::sleep(250);
+    timer::sleep(Duration::milliseconds(250));
 }
diff --git a/src/test/run-pass/issue-9396.rs b/src/test/run-pass/issue-9396.rs
index 9f08f1db410..c16319a16f2 100644
--- a/src/test/run-pass/issue-9396.rs
+++ b/src/test/run-pass/issue-9396.rs
@@ -10,12 +10,13 @@
 
 use std::comm;
 use std::io::timer::Timer;
+use std::time::Duration;
 
 pub fn main() {
     let (tx, rx) = channel();
     spawn(proc (){
         let mut timer = Timer::new().unwrap();
-        timer.sleep(10);
+        timer.sleep(Duration::milliseconds(10));
         tx.send(());
     });
     loop {
diff --git a/src/test/run-pass/tcp-connect-timeouts.rs b/src/test/run-pass/tcp-connect-timeouts.rs
index 6f6fff15814..c1d93033ab6 100644
--- a/src/test/run-pass/tcp-connect-timeouts.rs
+++ b/src/test/run-pass/tcp-connect-timeouts.rs
@@ -38,6 +38,7 @@ macro_rules! iotest (
             use std::io::net::tcp::*;
             use std::io::test::*;
             use std::io;
+            use std::time::Duration;
 
             fn f() $b
 
@@ -72,7 +73,7 @@ iotest!(fn eventual_timeout() {
 
     let mut v = Vec::new();
     for _ in range(0u, 10000) {
-        match TcpStream::connect_timeout(addr, 100) {
+        match TcpStream::connect_timeout(addr, Duration::milliseconds(100)) {
             Ok(e) => v.push(e),
             Err(ref e) if e.kind == io::TimedOut => return,
             Err(e) => fail!("other error: {}", e),
@@ -87,11 +88,22 @@ iotest!(fn timeout_success() {
     let port = addr.port;
     let _l = TcpListener::bind(host.as_slice(), port).unwrap().listen();
 
-    assert!(TcpStream::connect_timeout(addr, 1000).is_ok());
+    assert!(TcpStream::connect_timeout(addr, Duration::milliseconds(1000)).is_ok());
 })
 
 iotest!(fn timeout_error() {
     let addr = next_test_ip4();
 
-    assert!(TcpStream::connect_timeout(addr, 1000).is_err());
+    assert!(TcpStream::connect_timeout(addr, Duration::milliseconds(1000)).is_err());
 })
+
+    iotest!(fn connect_timeout_zero() {
+        let addr = next_test_ip4();
+        assert!(TcpStream::connect_timeout(addr, Duration::milliseconds(0)).is_err());
+    })
+
+    iotest!(fn connect_timeout_negative() {
+        let addr = next_test_ip4();
+        assert!(TcpStream::connect_timeout(addr, Duration::milliseconds(-1)).is_err());
+    })
+
diff --git a/src/test/run-pass/tcp-stress.rs b/src/test/run-pass/tcp-stress.rs
index f52a3455e41..864d005f373 100644
--- a/src/test/run-pass/tcp-stress.rs
+++ b/src/test/run-pass/tcp-stress.rs
@@ -23,6 +23,7 @@ extern crate debug;
 use std::io::net::tcp::{TcpListener, TcpStream};
 use std::io::{Acceptor, Listener};
 use std::task::TaskBuilder;
+use std::time::Duration;
 
 #[start]
 fn start(argc: int, argv: *const *const u8) -> int {
@@ -33,7 +34,7 @@ fn main() {
     // This test has a chance to time out, try to not let it time out
     spawn(proc() {
         use std::io::timer;
-        timer::sleep(30 * 1000);
+        timer::sleep(Duration::milliseconds(30 * 1000));
         println!("timed out!");
         unsafe { libc::exit(1) }
     });