diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index a9c7673d4ddfb..318609f44fda6 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 b9144047df5c0..e041547082885 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 0b0c22ed88799..5c91c48c55d5b 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 { + timeout: Duration) -> IoResult { + 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 5e7c421497772..d88af8dd30aa3 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(path: &P, - timeout_ms: u64) -> IoResult { + timeout: Duration) -> IoResult { + 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 c82b4831341ef..3dd4343c5f4fc 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 c126866e7159a..10739c70143cd 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 331cfa1a59ef9..079a9aef648aa 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 1c9e428dcad82..39c6c74e45eef 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 20fc7efeb574f..103cd574e7381 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 19ad81a04834d..9cace9c80ef5a 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 0000000000000..545d1f2aab44f --- /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 or the MIT license +// , 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 { + fn micros((days, secs, nanos): (i64, u32, u32)) -> Option { + 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 { + fn nanos((days, secs, nanos): (i64, u32, u32)) -> Option { + 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 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 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 { + 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 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 { + 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 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 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 0000000000000..436fa5ebdea72 --- /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 or the MIT license +// , 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 45016b97566ce..e4df661b56201 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::(); //! 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::(); //! 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 34f1e681608a3..9cee83a8598c7 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 37e675b52ebd8..e21338746be2c 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 c24128f97e372..6b6e770bc99a4 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 9f08f1db41057..c16319a16f282 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 6f6fff15814d5..c1d93033ab6fe 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 f52a3455e4157..864d005f37344 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) } });