From 410e97df2ab9d70476694c005e9cd60238ddc394 Mon Sep 17 00:00:00 2001 From: Joey Ezechiels Date: Fri, 2 Nov 2018 20:21:51 +0100 Subject: [PATCH 1/6] Absorb the time crate I've discussed this with one of the maintainers (Alex Chrichton), and with his blessing I've purged the dependency on the deprecated time crate in favor of folding its code into chrono. This enables further refactoring. --- Cargo.toml | 17 +- src/lib.rs | 29 +- src/naive/date.rs | 35 +- src/naive/datetime.rs | 70 +-- src/naive/time.rs | 63 +- src/time/display.rs | 260 +++++++++ src/time/duration.rs | 648 +++++++++++++++++++++ src/time/mod.rs | 1263 +++++++++++++++++++++++++++++++++++++++++ src/time/parse.rs | 393 +++++++++++++ src/time/sys.rs | 973 +++++++++++++++++++++++++++++++ 10 files changed, 3643 insertions(+), 108 deletions(-) create mode 100644 src/time/display.rs create mode 100644 src/time/duration.rs create mode 100644 src/time/mod.rs create mode 100644 src/time/parse.rs create mode 100644 src/time/sys.rs diff --git a/Cargo.toml b/Cargo.toml index 96936a8265..43af047c1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,20 +24,33 @@ name = "chrono" [features] default = ["clock"] -clock = ["time"] +clock = [] [dependencies] -time = { version = "0.1.39", optional = true } num-integer = { version = "0.1.36", default-features = false } num-traits = { version = "0.2", default-features = false } rustc-serialize = { version = "0.3.20", optional = true } serde = { version = "1", optional = true } + +[target.'cfg(unix)'.dependencies] +libc = "0.2.1" + +[target.'cfg(target_os = "redox")'.dependencies] +redox_syscall = "0.1" +libc = "0.2.1" + +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3.0", features = ["std", "minwinbase", "minwindef", "ntdef", "profileapi", "sysinfoapi", "timezoneapi"] } +libc = "0.2.1" + [dev-dependencies] serde_json = { version = "1" } serde_derive = { version = "1" } bincode = { version = "0.8.0" } num-iter = { version = "0.1.35", default-features = false } +log = "0.4" +winapi = { version = "0.3.0", features = ["std", "processthreadsapi", "winbase"] } [package.metadata.docs.rs] all-features = true diff --git a/src/lib.rs b/src/lib.rs index 1f149ed5dc..4889530d8e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -158,9 +158,8 @@ //! The following illustrates most supported operations to the date and time: //! //! ```rust -//! # extern crate chrono; extern crate time; fn main() { -//! use chrono::prelude::*; -//! use time::Duration; +//! # extern crate chrono; fn main() { +//! use chrono::{prelude::*, Duration}; //! //! # /* we intentionally fake the datetime... //! // assume this returned `2014-11-28T21:45:59.324310806+09:00`: @@ -405,8 +404,20 @@ trivially_copy_pass_by_ref, ))] -#[cfg(feature="clock")] -extern crate time as oldtime; + +// These are required by the `time` module: +#[cfg(all(feature="clock", target_os = "redox"))] +extern crate syscall; +#[cfg(all(feature="clock", unix))] +extern crate libc; +#[cfg(all(feature="clock", windows))] +extern crate winapi; +#[cfg(feature = "rustc-serialize")] +extern crate rustc_serialize; +#[cfg(test)] +#[macro_use] +extern crate log; + extern crate num_integer; extern crate num_traits; #[cfg(feature = "rustc-serialize")] @@ -414,6 +425,14 @@ extern crate rustc_serialize; #[cfg(feature = "serde")] extern crate serde as serdelib; +#[cfg(feature="clock")] +mod time; + +#[cfg(feature="clock")] +use time as oldtime; +#[cfg(feature="clock")] +pub use time::{PreciseTime, SteadyTime}; + // this reexport is to aid the transition and should not be in the prelude! pub use oldtime::Duration; diff --git a/src/naive/date.rs b/src/naive/date.rs index aad98bc605..8611339d7d 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -788,10 +788,9 @@ impl NaiveDate { /// # Example /// /// ~~~~ - /// # extern crate chrono; extern crate time; fn main() { - /// use chrono::NaiveDate; + /// # extern crate chrono; fn main() { + /// use chrono::{NaiveDate, Duration}; /// use chrono::naive::MAX_DATE; - /// use time::Duration; /// /// let d = NaiveDate::from_ymd(2015, 9, 5); /// assert_eq!(d.checked_add_signed(Duration::days(40)), @@ -824,10 +823,9 @@ impl NaiveDate { /// # Example /// /// ~~~~ - /// # extern crate chrono; extern crate time; fn main() { - /// use chrono::NaiveDate; + /// # extern crate chrono; fn main() { + /// use chrono::{NaiveDate, Duration}; /// use chrono::naive::MIN_DATE; - /// use time::Duration; /// /// let d = NaiveDate::from_ymd(2015, 9, 5); /// assert_eq!(d.checked_sub_signed(Duration::days(40)), @@ -862,9 +860,8 @@ impl NaiveDate { /// # Example /// /// ~~~~ - /// # extern crate chrono; extern crate time; fn main() { - /// use chrono::NaiveDate; - /// use time::Duration; + /// # extern crate chrono; fn main() { + /// use chrono::{NaiveDate, Duration}; /// /// let from_ymd = NaiveDate::from_ymd; /// let since = NaiveDate::signed_duration_since; @@ -1311,9 +1308,8 @@ impl Datelike for NaiveDate { /// # Example /// /// ~~~~ -/// # extern crate chrono; extern crate time; fn main() { -/// use chrono::NaiveDate; -/// use time::Duration; +/// # extern crate chrono; fn main() { +/// use chrono::{NaiveDate, Duration}; /// /// let from_ymd = NaiveDate::from_ymd; /// @@ -1353,9 +1349,8 @@ impl AddAssign for NaiveDate { /// # Example /// /// ~~~~ -/// # extern crate chrono; extern crate time; fn main() { -/// use chrono::NaiveDate; -/// use time::Duration; +/// # extern crate chrono; fn main() { +/// use chrono::{NaiveDate, Duration}; /// /// let from_ymd = NaiveDate::from_ymd; /// @@ -1387,7 +1382,7 @@ impl SubAssign for NaiveDate { /// Subtracts another `NaiveDate` from the current date. /// Returns a `Duration` of integral numbers. -/// +/// /// This does not overflow or underflow at all, /// as all possible output fits in the range of `Duration`. /// @@ -1397,9 +1392,8 @@ impl SubAssign for NaiveDate { /// # Example /// /// ~~~~ -/// # extern crate chrono; extern crate time; fn main() { -/// use chrono::NaiveDate; -/// use time::Duration; +/// # extern crate chrono; fn main() { +/// use chrono::{NaiveDate, Duration}; /// /// let from_ymd = NaiveDate::from_ymd; /// @@ -1629,7 +1623,7 @@ mod serde { impl<'de> de::Visitor<'de> for NaiveDateVisitor { type Value = NaiveDate; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "a formatted date string") } @@ -2118,4 +2112,3 @@ mod tests { "2009,09,01,00,53"); } } - diff --git a/src/naive/datetime.rs b/src/naive/datetime.rs index d228a54f55..7611c9e694 100644 --- a/src/naive/datetime.rs +++ b/src/naive/datetime.rs @@ -402,9 +402,8 @@ impl NaiveDateTime { /// # Example /// /// ~~~~ - /// # extern crate chrono; extern crate time; fn main() { - /// use chrono::NaiveDate; - /// use time::Duration; + /// # extern crate chrono; fn main() { + /// use chrono::{NaiveDate, Duration}; /// /// let from_ymd = NaiveDate::from_ymd; /// @@ -430,9 +429,8 @@ impl NaiveDateTime { /// Overflow returns `None`. /// /// ~~~~ - /// # extern crate chrono; extern crate time; fn main() { - /// # use chrono::NaiveDate; - /// # use time::Duration; + /// # extern crate chrono; fn main() { + /// # use chrono::{NaiveDate, Duration}; /// # let hms = |h, m, s| NaiveDate::from_ymd(2016, 7, 8).and_hms(h, m, s); /// assert_eq!(hms(3, 5, 7).checked_add_signed(Duration::days(1_000_000_000)), None); /// # } @@ -442,9 +440,8 @@ impl NaiveDateTime { /// but the addition assumes that it is the only leap second happened. /// /// ~~~~ - /// # extern crate chrono; extern crate time; fn main() { - /// # use chrono::NaiveDate; - /// # use time::Duration; + /// # extern crate chrono; fn main() { + /// # use chrono::{NaiveDate, Duration}; /// # let from_ymd = NaiveDate::from_ymd; /// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli(h, m, s, milli); /// let leap = hmsm(3, 5, 59, 1_300); @@ -488,9 +485,8 @@ impl NaiveDateTime { /// # Example /// /// ~~~~ - /// # extern crate chrono; extern crate time; fn main() { - /// use chrono::NaiveDate; - /// use time::Duration; + /// # extern crate chrono; fn main() { + /// use chrono::{NaiveDate, Duration}; /// /// let from_ymd = NaiveDate::from_ymd; /// @@ -516,9 +512,8 @@ impl NaiveDateTime { /// Overflow returns `None`. /// /// ~~~~ - /// # extern crate chrono; extern crate time; fn main() { - /// # use chrono::NaiveDate; - /// # use time::Duration; + /// # extern crate chrono; fn main() { + /// # use chrono::{NaiveDate, Duration}; /// # let hms = |h, m, s| NaiveDate::from_ymd(2016, 7, 8).and_hms(h, m, s); /// assert_eq!(hms(3, 5, 7).checked_sub_signed(Duration::days(1_000_000_000)), None); /// # } @@ -528,9 +523,8 @@ impl NaiveDateTime { /// but the subtraction assumes that it is the only leap second happened. /// /// ~~~~ - /// # extern crate chrono; extern crate time; fn main() { - /// # use chrono::NaiveDate; - /// # use time::Duration; + /// # extern crate chrono; fn main() { + /// # use chrono::{NaiveDate, Duration}; /// # let from_ymd = NaiveDate::from_ymd; /// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli(h, m, s, milli); /// let leap = hmsm(3, 5, 59, 1_300); @@ -570,9 +564,8 @@ impl NaiveDateTime { /// # Example /// /// ~~~~ - /// # extern crate chrono; extern crate time; fn main() { - /// use chrono::NaiveDate; - /// use time::Duration; + /// # extern crate chrono; fn main() { + /// use chrono::{NaiveDate, Duration}; /// /// let from_ymd = NaiveDate::from_ymd; /// @@ -591,9 +584,8 @@ impl NaiveDateTime { /// there were no other leap seconds happened. /// /// ~~~~ - /// # extern crate chrono; extern crate time; fn main() { - /// # use chrono::NaiveDate; - /// # use time::Duration; + /// # extern crate chrono; fn main() { + /// # use chrono::{NaiveDate, Duration}; /// # let from_ymd = NaiveDate::from_ymd; /// let leap = from_ymd(2015, 6, 30).and_hms_milli(23, 59, 59, 1_500); /// assert_eq!(leap.signed_duration_since(from_ymd(2015, 6, 30).and_hms(23, 0, 0)), @@ -1188,9 +1180,8 @@ impl hash::Hash for NaiveDateTime { /// # Example /// /// ~~~~ -/// # extern crate chrono; extern crate time; fn main() { -/// use chrono::NaiveDate; -/// use time::Duration; +/// # extern crate chrono; fn main() { +/// use chrono::{NaiveDate, Duration}; /// /// let from_ymd = NaiveDate::from_ymd; /// @@ -1214,9 +1205,8 @@ impl hash::Hash for NaiveDateTime { /// but the addition assumes that it is the only leap second happened. /// /// ~~~~ -/// # extern crate chrono; extern crate time; fn main() { -/// # use chrono::NaiveDate; -/// # use time::Duration; +/// # extern crate chrono; fn main() { +/// # use chrono::{NaiveDate, Duration}; /// # let from_ymd = NaiveDate::from_ymd; /// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli(h, m, s, milli); /// let leap = hmsm(3, 5, 59, 1_300); @@ -1260,9 +1250,8 @@ impl AddAssign for NaiveDateTime { /// # Example /// /// ~~~~ -/// # extern crate chrono; extern crate time; fn main() { -/// use chrono::NaiveDate; -/// use time::Duration; +/// # extern crate chrono; fn main() { +/// use chrono::{NaiveDate, Duration}; /// /// let from_ymd = NaiveDate::from_ymd; /// @@ -1286,9 +1275,8 @@ impl AddAssign for NaiveDateTime { /// but the subtraction assumes that it is the only leap second happened. /// /// ~~~~ -/// # extern crate chrono; extern crate time; fn main() { -/// # use chrono::NaiveDate; -/// # use time::Duration; +/// # extern crate chrono; fn main() { +/// # use chrono::{NaiveDate, Duration}; /// # let from_ymd = NaiveDate::from_ymd; /// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli(h, m, s, milli); /// let leap = hmsm(3, 5, 59, 1_300); @@ -1331,9 +1319,8 @@ impl SubAssign for NaiveDateTime { /// # Example /// /// ~~~~ -/// # extern crate chrono; extern crate time; fn main() { -/// use chrono::NaiveDate; -/// use time::Duration; +/// # extern crate chrono; fn main() { +/// use chrono::{NaiveDate, Duration}; /// /// let from_ymd = NaiveDate::from_ymd; /// @@ -1351,9 +1338,8 @@ impl SubAssign for NaiveDateTime { /// there were no other leap seconds happened. /// /// ~~~~ -/// # extern crate chrono; extern crate time; fn main() { -/// # use chrono::NaiveDate; -/// # use time::Duration; +/// # extern crate chrono; fn main() { +/// # use chrono::{NaiveDate, Duration}; /// # let from_ymd = NaiveDate::from_ymd; /// let leap = from_ymd(2015, 6, 30).and_hms_milli(23, 59, 59, 1_500); /// assert_eq!(leap - from_ymd(2015, 6, 30).and_hms(23, 0, 0), diff --git a/src/naive/time.rs b/src/naive/time.rs index 440c8a7ac0..b44b7250af 100644 --- a/src/naive/time.rs +++ b/src/naive/time.rs @@ -504,9 +504,8 @@ impl NaiveTime { /// # Example /// /// ~~~~ - /// # extern crate chrono; extern crate time; fn main() { - /// use chrono::NaiveTime; - /// use time::Duration; + /// # extern crate chrono; fn main() { + /// use chrono::{NaiveTime, Duration}; /// /// let from_hms = NaiveTime::from_hms; /// @@ -590,9 +589,8 @@ impl NaiveTime { /// # Example /// /// ~~~~ - /// # extern crate chrono; extern crate time; fn main() { - /// use chrono::NaiveTime; - /// use time::Duration; + /// # extern crate chrono; fn main() { + /// use chrono::{NaiveTime, Duration}; /// /// let from_hms = NaiveTime::from_hms; /// @@ -623,9 +621,8 @@ impl NaiveTime { /// # Example /// /// ~~~~ - /// # extern crate chrono; extern crate time; fn main() { - /// use chrono::NaiveTime; - /// use time::Duration; + /// # extern crate chrono; fn main() { + /// use chrono::{NaiveTime, Duration}; /// /// let from_hmsm = NaiveTime::from_hms_milli; /// let since = NaiveTime::signed_duration_since; @@ -653,9 +650,8 @@ impl NaiveTime { /// there were no other leap seconds happened. /// /// ~~~~ - /// # extern crate chrono; extern crate time; fn main() { - /// # use chrono::NaiveTime; - /// # use time::Duration; + /// # extern crate chrono; fn main() { + /// # use chrono::{NaiveTime, Duration}; /// # let from_hmsm = NaiveTime::from_hms_milli; /// # let since = NaiveTime::signed_duration_since; /// assert_eq!(since(from_hmsm(3, 0, 59, 1_000), from_hmsm(3, 0, 59, 0)), @@ -1000,9 +996,8 @@ impl hash::Hash for NaiveTime { /// # Example /// /// ~~~~ -/// # extern crate chrono; extern crate time; fn main() { -/// use chrono::NaiveTime; -/// use time::Duration; +/// # extern crate chrono; fn main() { +/// use chrono::{NaiveTime, Duration}; /// /// let from_hmsm = NaiveTime::from_hms_milli; /// @@ -1020,9 +1015,8 @@ impl hash::Hash for NaiveTime { /// The addition wraps around. /// /// ~~~~ -/// # extern crate chrono; extern crate time; fn main() { -/// # use chrono::NaiveTime; -/// # use time::Duration; +/// # extern crate chrono; fn main() { +/// # use chrono::{NaiveTime, Duration}; /// # let from_hmsm = NaiveTime::from_hms_milli; /// assert_eq!(from_hmsm(3, 5, 7, 0) + Duration::seconds(22*60*60), from_hmsm(1, 5, 7, 0)); /// assert_eq!(from_hmsm(3, 5, 7, 0) + Duration::seconds(-8*60*60), from_hmsm(19, 5, 7, 0)); @@ -1033,9 +1027,8 @@ impl hash::Hash for NaiveTime { /// Leap seconds are handled, but the addition assumes that it is the only leap second happened. /// /// ~~~~ -/// # extern crate chrono; extern crate time; fn main() { -/// # use chrono::NaiveTime; -/// # use time::Duration; +/// # extern crate chrono; fn main() { +/// # use chrono::{NaiveTime, Duration}; /// # let from_hmsm = NaiveTime::from_hms_milli; /// let leap = from_hmsm(3, 5, 59, 1_300); /// assert_eq!(leap + Duration::zero(), from_hmsm(3, 5, 59, 1_300)); @@ -1075,9 +1068,8 @@ impl AddAssign for NaiveTime { /// # Example /// /// ~~~~ -/// # extern crate chrono; extern crate time; fn main() { -/// use chrono::NaiveTime; -/// use time::Duration; +/// # extern crate chrono; fn main() { +/// use chrono::{NaiveTime, Duration}; /// /// let from_hmsm = NaiveTime::from_hms_milli; /// @@ -1093,9 +1085,8 @@ impl AddAssign for NaiveTime { /// The subtraction wraps around. /// /// ~~~~ -/// # extern crate chrono; extern crate time; fn main() { -/// # use chrono::NaiveTime; -/// # use time::Duration; +/// # extern crate chrono; fn main() { +/// # use chrono::{NaiveTime, Duration}; /// # let from_hmsm = NaiveTime::from_hms_milli; /// assert_eq!(from_hmsm(3, 5, 7, 0) - Duration::seconds(8*60*60), from_hmsm(19, 5, 7, 0)); /// assert_eq!(from_hmsm(3, 5, 7, 0) - Duration::days(800), from_hmsm(3, 5, 7, 0)); @@ -1105,9 +1096,8 @@ impl AddAssign for NaiveTime { /// Leap seconds are handled, but the subtraction assumes that it is the only leap second happened. /// /// ~~~~ -/// # extern crate chrono; extern crate time; fn main() { -/// # use chrono::NaiveTime; -/// # use time::Duration; +/// # extern crate chrono; fn main() { +/// # use chrono::{NaiveTime, Duration}; /// # let from_hmsm = NaiveTime::from_hms_milli; /// let leap = from_hmsm(3, 5, 59, 1_300); /// assert_eq!(leap - Duration::zero(), from_hmsm(3, 5, 59, 1_300)); @@ -1149,9 +1139,8 @@ impl SubAssign for NaiveTime { /// # Example /// /// ~~~~ -/// # extern crate chrono; extern crate time; fn main() { -/// use chrono::NaiveTime; -/// use time::Duration; +/// # extern crate chrono; fn main() { +/// use chrono::{NaiveTime, Duration}; /// /// let from_hmsm = NaiveTime::from_hms_milli; /// @@ -1171,9 +1160,8 @@ impl SubAssign for NaiveTime { /// there were no other leap seconds happened. /// /// ~~~~ -/// # extern crate chrono; extern crate time; fn main() { -/// # use chrono::NaiveTime; -/// # use time::Duration; +/// # extern crate chrono; fn main() { +/// # use chrono::{NaiveTime, Duration}; /// # let from_hmsm = NaiveTime::from_hms_milli; /// assert_eq!(from_hmsm(3, 0, 59, 1_000) - from_hmsm(3, 0, 59, 0), Duration::seconds(1)); /// assert_eq!(from_hmsm(3, 0, 59, 1_500) - from_hmsm(3, 0, 59, 0), @@ -1431,7 +1419,7 @@ mod serde { impl<'de> de::Visitor<'de> for NaiveTimeVisitor { type Value = NaiveTime; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "a formatted time string") } @@ -1730,4 +1718,3 @@ mod tests { "23:59:60"); } } - diff --git a/src/time/display.rs b/src/time/display.rs new file mode 100644 index 0000000000..27372d22e7 --- /dev/null +++ b/src/time/display.rs @@ -0,0 +1,260 @@ +use std::fmt::{self, Write}; + +use super::{TmFmt, Tm, Fmt}; + +impl<'a> fmt::Display for TmFmt<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match self.format { + Fmt::Str(ref s) => { + let mut chars = s.chars(); + while let Some(ch) = chars.next() { + if ch == '%' { + // we've already validated that % always precedes + // another char + try!(parse_type(fmt, chars.next().unwrap(), self.tm)); + } else { + try!(fmt.write_char(ch)); + } + } + + Ok(()) + } + Fmt::Ctime => self.tm.to_local().asctime().fmt(fmt), + Fmt::Rfc3339 => { + if self.tm.tm_utcoff == 0 { + TmFmt { + tm: self.tm, + format: Fmt::Str("%Y-%m-%dT%H:%M:%SZ"), + }.fmt(fmt) + } else { + let s = TmFmt { + tm: self.tm, + format: Fmt::Str("%Y-%m-%dT%H:%M:%S"), + }; + let sign = if self.tm.tm_utcoff > 0 { '+' } else { '-' }; + let mut m = abs(self.tm.tm_utcoff) / 60; + let h = m / 60; + m -= h * 60; + write!(fmt, "{}{}{:02}:{:02}", s, sign, h, m) + } + } + } + } +} + +fn is_leap_year(year: i32) -> bool { + (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)) +} + +fn days_in_year(year: i32) -> i32 { + if is_leap_year(year) { 366 } + else { 365 } +} + +fn iso_week_days(yday: i32, wday: i32) -> i32 { + /* The number of days from the first day of the first ISO week of this + * year to the year day YDAY with week day WDAY. + * ISO weeks start on Monday. The first ISO week has the year's first + * Thursday. + * YDAY may be as small as yday_minimum. + */ + let iso_week_start_wday: i32 = 1; /* Monday */ + let iso_week1_wday: i32 = 4; /* Thursday */ + let yday_minimum: i32 = 366; + /* Add enough to the first operand of % to make it nonnegative. */ + let big_enough_multiple_of_7: i32 = (yday_minimum / 7 + 2) * 7; + + yday - (yday - wday + iso_week1_wday + big_enough_multiple_of_7) % 7 + + iso_week1_wday - iso_week_start_wday +} + +fn iso_week(fmt: &mut fmt::Formatter, ch:char, tm: &Tm) -> fmt::Result { + let mut year = tm.tm_year + 1900; + let mut days = iso_week_days(tm.tm_yday, tm.tm_wday); + + if days < 0 { + /* This ISO week belongs to the previous year. */ + year -= 1; + days = iso_week_days(tm.tm_yday + (days_in_year(year)), tm.tm_wday); + } else { + let d = iso_week_days(tm.tm_yday - (days_in_year(year)), + tm.tm_wday); + if 0 <= d { + /* This ISO week belongs to the next year. */ + year += 1; + days = d; + } + } + + match ch { + 'G' => write!(fmt, "{}", year), + 'g' => write!(fmt, "{:02}", (year % 100 + 100) % 100), + 'V' => write!(fmt, "{:02}", days / 7 + 1), + _ => Ok(()) + } +} + +fn parse_type(fmt: &mut fmt::Formatter, ch: char, tm: &Tm) -> fmt::Result { + match ch { + 'A' => fmt.write_str(match tm.tm_wday { + 0 => "Sunday", + 1 => "Monday", + 2 => "Tuesday", + 3 => "Wednesday", + 4 => "Thursday", + 5 => "Friday", + 6 => "Saturday", + _ => unreachable!(), + }), + 'a' => fmt.write_str(match tm.tm_wday { + 0 => "Sun", + 1 => "Mon", + 2 => "Tue", + 3 => "Wed", + 4 => "Thu", + 5 => "Fri", + 6 => "Sat", + _ => unreachable!(), + }), + 'B' => fmt.write_str(match tm.tm_mon { + 0 => "January", + 1 => "February", + 2 => "March", + 3 => "April", + 4 => "May", + 5 => "June", + 6 => "July", + 7 => "August", + 8 => "September", + 9 => "October", + 10 => "November", + 11 => "December", + _ => unreachable!(), + }), + 'b' | 'h' => fmt.write_str(match tm.tm_mon { + 0 => "Jan", + 1 => "Feb", + 2 => "Mar", + 3 => "Apr", + 4 => "May", + 5 => "Jun", + 6 => "Jul", + 7 => "Aug", + 8 => "Sep", + 9 => "Oct", + 10 => "Nov", + 11 => "Dec", + _ => unreachable!(), + }), + 'C' => write!(fmt, "{:02}", (tm.tm_year + 1900) / 100), + 'c' => { + try!(parse_type(fmt, 'a', tm)); + try!(fmt.write_str(" ")); + try!(parse_type(fmt, 'b', tm)); + try!(fmt.write_str(" ")); + try!(parse_type(fmt, 'e', tm)); + try!(fmt.write_str(" ")); + try!(parse_type(fmt, 'T', tm)); + try!(fmt.write_str(" ")); + parse_type(fmt, 'Y', tm) + } + 'D' | 'x' => { + try!(parse_type(fmt, 'm', tm)); + try!(fmt.write_str("/")); + try!(parse_type(fmt, 'd', tm)); + try!(fmt.write_str("/")); + parse_type(fmt, 'y', tm) + } + 'd' => write!(fmt, "{:02}", tm.tm_mday), + 'e' => write!(fmt, "{:2}", tm.tm_mday), + 'f' => write!(fmt, "{:09}", tm.tm_nsec), + 'F' => { + try!(parse_type(fmt, 'Y', tm)); + try!(fmt.write_str("-")); + try!(parse_type(fmt, 'm', tm)); + try!(fmt.write_str("-")); + parse_type(fmt, 'd', tm) + } + 'G' => iso_week(fmt, 'G', tm), + 'g' => iso_week(fmt, 'g', tm), + 'H' => write!(fmt, "{:02}", tm.tm_hour), + 'I' => { + let mut h = tm.tm_hour; + if h == 0 { h = 12 } + if h > 12 { h -= 12 } + write!(fmt, "{:02}", h) + } + 'j' => write!(fmt, "{:03}", tm.tm_yday + 1), + 'k' => write!(fmt, "{:2}", tm.tm_hour), + 'l' => { + let mut h = tm.tm_hour; + if h == 0 { h = 12 } + if h > 12 { h -= 12 } + write!(fmt, "{:2}", h) + } + 'M' => write!(fmt, "{:02}", tm.tm_min), + 'm' => write!(fmt, "{:02}", tm.tm_mon + 1), + 'n' => fmt.write_str("\n"), + 'P' => fmt.write_str(if tm.tm_hour < 12 { "am" } else { "pm" }), + 'p' => fmt.write_str(if (tm.tm_hour) < 12 { "AM" } else { "PM" }), + 'R' => { + try!(parse_type(fmt, 'H', tm)); + try!(fmt.write_str(":")); + parse_type(fmt, 'M', tm) + } + 'r' => { + try!(parse_type(fmt, 'I', tm)); + try!(fmt.write_str(":")); + try!(parse_type(fmt, 'M', tm)); + try!(fmt.write_str(":")); + try!(parse_type(fmt, 'S', tm)); + try!(fmt.write_str(" ")); + parse_type(fmt, 'p', tm) + } + 'S' => write!(fmt, "{:02}", tm.tm_sec), + 's' => write!(fmt, "{}", tm.to_timespec().sec), + 'T' | 'X' => { + try!(parse_type(fmt, 'H', tm)); + try!(fmt.write_str(":")); + try!(parse_type(fmt, 'M', tm)); + try!(fmt.write_str(":")); + parse_type(fmt, 'S', tm) + } + 't' => fmt.write_str("\t"), + 'U' => write!(fmt, "{:02}", (tm.tm_yday - tm.tm_wday + 7) / 7), + 'u' => { + let i = tm.tm_wday; + write!(fmt, "{}", (if i == 0 { 7 } else { i })) + } + 'V' => iso_week(fmt, 'V', tm), + 'v' => { + try!(parse_type(fmt, 'e', tm)); + try!(fmt.write_str("-")); + try!(parse_type(fmt, 'b', tm)); + try!(fmt.write_str("-")); + parse_type(fmt, 'Y', tm) + } + 'W' => { + write!(fmt, "{:02}", (tm.tm_yday - (tm.tm_wday - 1 + 7) % 7 + 7) / 7) + } + 'w' => write!(fmt, "{}", tm.tm_wday), + 'Y' => write!(fmt, "{}", tm.tm_year + 1900), + 'y' => write!(fmt, "{:02}", (tm.tm_year + 1900) % 100), + // FIXME (#2350): support locale + 'Z' => fmt.write_str(if tm.tm_utcoff == 0 { "UTC"} else { "" }), + 'z' => { + let sign = if tm.tm_utcoff > 0 { '+' } else { '-' }; + let mut m = abs(tm.tm_utcoff) / 60; + let h = m / 60; + m -= h * 60; + write!(fmt, "{}{:02}{:02}", sign, h, m) + } + '+' => write!(fmt, "{}", tm.rfc3339()), + '%' => fmt.write_str("%"), + _ => unreachable!(), + } +} + +fn abs(i: i32) -> i32 { + if i < 0 {-i} else {i} +} diff --git a/src/time/duration.rs b/src/time/duration.rs new file mode 100644 index 0000000000..419af0fc7e --- /dev/null +++ b/src/time/duration.rs @@ -0,0 +1,648 @@ +// 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 + +use std::{fmt, i64}; +use std::error::Error; +use std::ops::{Add, Sub, Mul, Div, Neg, FnOnce}; +use std::time::Duration as StdDuration; + +/// The number of nanoseconds in a microsecond. +const NANOS_PER_MICRO: i32 = 1000; +/// The number of nanoseconds in a millisecond. +const NANOS_PER_MILLI: i32 = 1000_000; +/// The number of nanoseconds in seconds. +const NANOS_PER_SEC: i32 = 1_000_000_000; +/// The number of microseconds per second. +const MICROS_PER_SEC: i64 = 1000_000; +/// The number of milliseconds per second. +const MILLIS_PER_SEC: i64 = 1000; +/// The number of seconds in a minute. +const SECS_PER_MINUTE: i64 = 60; +/// The number of seconds in an hour. +const SECS_PER_HOUR: i64 = 3600; +/// The number of (non-leap) seconds in days. +const SECS_PER_DAY: i64 = 86400; +/// The number of (non-leap) seconds in a week. +const SECS_PER_WEEK: i64 = 604800; + +macro_rules! try_opt { + ($e:expr) => (match $e { Some(v) => v, None => return None }) +} + + +/// ISO 8601 time duration with nanosecond precision. +/// This also allows for the negative duration; see individual methods for details. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct Duration { + secs: i64, + nanos: i32, // Always 0 <= nanos < NANOS_PER_SEC +} + +/// The minimum possible `Duration`: `i64::MIN` milliseconds. +pub const MIN: Duration = Duration { + secs: i64::MIN / MILLIS_PER_SEC - 1, + nanos: NANOS_PER_SEC + (i64::MIN % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI +}; + +/// The maximum possible `Duration`: `i64::MAX` milliseconds. +pub const MAX: Duration = Duration { + secs: i64::MAX / MILLIS_PER_SEC, + nanos: (i64::MAX % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI +}; + +impl Duration { + /// Makes a new `Duration` with given number of weeks. + /// Equivalent to `Duration::seconds(weeks * 7 * 24 * 60 * 60)` with overflow checks. + /// Panics when the duration is out of bounds. + #[inline] + pub fn weeks(weeks: i64) -> Duration { + let secs = weeks.checked_mul(SECS_PER_WEEK).expect("Duration::weeks out of bounds"); + Duration::seconds(secs) + } + + /// Makes a new `Duration` with given number of days. + /// Equivalent to `Duration::seconds(days * 24 * 60 * 60)` with overflow checks. + /// Panics when the duration is out of bounds. + #[inline] + pub fn days(days: i64) -> Duration { + let secs = days.checked_mul(SECS_PER_DAY).expect("Duration::days out of bounds"); + Duration::seconds(secs) + } + + /// Makes a new `Duration` with given number of hours. + /// Equivalent to `Duration::seconds(hours * 60 * 60)` with overflow checks. + /// Panics when the duration is out of bounds. + #[inline] + pub fn hours(hours: i64) -> Duration { + let secs = hours.checked_mul(SECS_PER_HOUR).expect("Duration::hours ouf of bounds"); + Duration::seconds(secs) + } + + /// Makes a new `Duration` with given number of minutes. + /// Equivalent to `Duration::seconds(minutes * 60)` with overflow checks. + /// Panics when the duration is out of bounds. + #[inline] + pub fn minutes(minutes: i64) -> Duration { + let secs = minutes.checked_mul(SECS_PER_MINUTE).expect("Duration::minutes out of bounds"); + Duration::seconds(secs) + } + + /// Makes a new `Duration` with given number of seconds. + /// Panics when the duration is more than `i64::MAX` milliseconds + /// or less than `i64::MIN` milliseconds. + #[inline] + pub fn seconds(seconds: i64) -> Duration { + let d = Duration { secs: seconds, nanos: 0 }; + if d < MIN || d > MAX { + panic!("Duration::seconds out of bounds"); + } + d + } + + /// Makes a new `Duration` with given number of milliseconds. + #[inline] + pub fn milliseconds(milliseconds: i64) -> Duration { + let (secs, millis) = div_mod_floor_64(milliseconds, MILLIS_PER_SEC); + let nanos = millis as i32 * NANOS_PER_MILLI; + Duration { secs: secs, nanos: nanos } + } + + /// Makes a new `Duration` with given number of microseconds. + #[inline] + pub fn microseconds(microseconds: i64) -> Duration { + let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); + let nanos = micros as i32 * NANOS_PER_MICRO; + Duration { secs: secs, nanos: nanos } + } + + /// Makes a new `Duration` with given number of nanoseconds. + #[inline] + pub fn nanoseconds(nanos: i64) -> Duration { + let (secs, nanos) = div_mod_floor_64(nanos, NANOS_PER_SEC as i64); + Duration { secs: secs, nanos: nanos as i32 } + } + + /// Runs a closure, returning the duration of time it took to run the + /// closure. + pub fn span(f: F) -> Duration where F: FnOnce() { + let before = super::precise_time_ns(); + f(); + Duration::nanoseconds((super::precise_time_ns() - before) as i64) + } + + /// Returns the total number of whole weeks in the duration. + #[inline] + pub fn num_weeks(&self) -> i64 { + self.num_days() / 7 + } + + /// Returns the total number of whole days in the duration. + pub fn num_days(&self) -> i64 { + self.num_seconds() / SECS_PER_DAY + } + + /// Returns the total number of whole hours in the duration. + #[inline] + pub fn num_hours(&self) -> i64 { + self.num_seconds() / SECS_PER_HOUR + } + + /// Returns the total number of whole minutes in the duration. + #[inline] + pub fn num_minutes(&self) -> i64 { + self.num_seconds() / SECS_PER_MINUTE + } + + /// Returns the total number of whole seconds in the duration. + pub fn num_seconds(&self) -> i64 { + // If secs is negative, nanos should be subtracted from the duration. + if self.secs < 0 && self.nanos > 0 { + self.secs + 1 + } else { + self.secs + } + } + + /// Returns the number of nanoseconds such that + /// `nanos_mod_sec() + num_seconds() * NANOS_PER_SEC` is the total number of + /// nanoseconds in the duration. + fn nanos_mod_sec(&self) -> i32 { + if self.secs < 0 && self.nanos > 0 { + self.nanos - NANOS_PER_SEC + } else { + self.nanos + } + } + + /// Returns the total number of whole milliseconds in the duration, + pub fn num_milliseconds(&self) -> i64 { + // A proper Duration will not overflow, because MIN and MAX are defined + // such that the range is exactly i64 milliseconds. + let secs_part = self.num_seconds() * MILLIS_PER_SEC; + let nanos_part = self.nanos_mod_sec() / NANOS_PER_MILLI; + secs_part + nanos_part as i64 + } + + /// Returns the total number of whole microseconds in the duration, + /// or `None` on overflow (exceeding 263 microseconds in either direction). + pub fn num_microseconds(&self) -> Option { + let secs_part = try_opt!(self.num_seconds().checked_mul(MICROS_PER_SEC)); + let nanos_part = self.nanos_mod_sec() / NANOS_PER_MICRO; + secs_part.checked_add(nanos_part as i64) + } + + /// Returns the total number of whole nanoseconds in the duration, + /// or `None` on overflow (exceeding 263 nanoseconds in either direction). + pub fn num_nanoseconds(&self) -> Option { + let secs_part = try_opt!(self.num_seconds().checked_mul(NANOS_PER_SEC as i64)); + let nanos_part = self.nanos_mod_sec(); + secs_part.checked_add(nanos_part as i64) + } + + /// Add two durations, returning `None` if overflow occurred. + pub fn checked_add(&self, rhs: &Duration) -> Option { + let mut secs = try_opt!(self.secs.checked_add(rhs.secs)); + let mut nanos = self.nanos + rhs.nanos; + if nanos >= NANOS_PER_SEC { + nanos -= NANOS_PER_SEC; + secs = try_opt!(secs.checked_add(1)); + } + let d = Duration { secs: secs, nanos: nanos }; + // Even if d is within the bounds of i64 seconds, + // it might still overflow i64 milliseconds. + if d < MIN || d > MAX { None } else { Some(d) } + } + + /// Subtract two durations, returning `None` if overflow occurred. + pub fn checked_sub(&self, rhs: &Duration) -> Option { + let mut secs = try_opt!(self.secs.checked_sub(rhs.secs)); + let mut nanos = self.nanos - rhs.nanos; + if nanos < 0 { + nanos += NANOS_PER_SEC; + secs = try_opt!(secs.checked_sub(1)); + } + let d = Duration { secs: secs, nanos: nanos }; + // Even if d is within the bounds of i64 seconds, + // it might still overflow i64 milliseconds. + if d < MIN || d > MAX { None } else { Some(d) } + } + + /// The minimum possible `Duration`: `i64::MIN` milliseconds. + #[inline] + pub fn min_value() -> Duration { MIN } + + /// The maximum possible `Duration`: `i64::MAX` milliseconds. + #[inline] + pub fn max_value() -> Duration { MAX } + + /// A duration where the stored seconds and nanoseconds are equal to zero. + #[inline] + pub fn zero() -> Duration { + Duration { secs: 0, nanos: 0 } + } + + /// Returns `true` if the duration equals `Duration::zero()`. + #[inline] + pub fn is_zero(&self) -> bool { + self.secs == 0 && self.nanos == 0 + } + + /// Creates a `time::Duration` object from `std::time::Duration` + /// + /// This function errors when original duration is larger than the maximum + /// value supported for this type. + pub fn from_std(duration: StdDuration) -> Result { + // We need to check secs as u64 before coercing to i64 + if duration.as_secs() > MAX.secs as u64 { + return Err(OutOfRangeError(())); + } + let d = Duration { + secs: duration.as_secs() as i64, + nanos: duration.subsec_nanos() as i32, + }; + if d > MAX { + return Err(OutOfRangeError(())); + } + Ok(d) + } + + /// Creates a `std::time::Duration` object from `time::Duration` + /// + /// This function errors when duration is less than zero. As standard + /// library implementation is limited to non-negative values. + pub fn to_std(&self) -> Result { + if self.secs < 0 { + return Err(OutOfRangeError(())); + } + Ok(StdDuration::new(self.secs as u64, self.nanos as u32)) + } +} + +impl Neg for Duration { + type Output = Duration; + + #[inline] + fn neg(self) -> Duration { + if self.nanos == 0 { + Duration { secs: -self.secs, nanos: 0 } + } else { + Duration { secs: -self.secs - 1, nanos: NANOS_PER_SEC - self.nanos } + } + } +} + +impl Add for Duration { + type Output = Duration; + + fn add(self, rhs: Duration) -> Duration { + let mut secs = self.secs + rhs.secs; + let mut nanos = self.nanos + rhs.nanos; + if nanos >= NANOS_PER_SEC { + nanos -= NANOS_PER_SEC; + secs += 1; + } + Duration { secs: secs, nanos: nanos } + } +} + +impl Sub for Duration { + type Output = Duration; + + fn sub(self, rhs: Duration) -> Duration { + let mut secs = self.secs - rhs.secs; + let mut nanos = self.nanos - rhs.nanos; + if nanos < 0 { + nanos += NANOS_PER_SEC; + secs -= 1; + } + Duration { secs: secs, nanos: nanos } + } +} + +impl Mul for Duration { + type Output = Duration; + + fn mul(self, rhs: i32) -> Duration { + // Multiply nanoseconds as i64, because it cannot overflow that way. + let total_nanos = self.nanos as i64 * rhs as i64; + let (extra_secs, nanos) = div_mod_floor_64(total_nanos, NANOS_PER_SEC as i64); + let secs = self.secs * rhs as i64 + extra_secs; + Duration { secs: secs, nanos: nanos as i32 } + } +} + +impl Div for Duration { + type Output = Duration; + + fn div(self, rhs: i32) -> Duration { + let mut secs = self.secs / rhs as i64; + let carry = self.secs - secs * rhs as i64; + let extra_nanos = carry * NANOS_PER_SEC as i64 / rhs as i64; + let mut nanos = self.nanos / rhs + extra_nanos as i32; + if nanos >= NANOS_PER_SEC { + nanos -= NANOS_PER_SEC; + secs += 1; + } + if nanos < 0 { + nanos += NANOS_PER_SEC; + secs -= 1; + } + Duration { secs: secs, nanos: nanos } + } +} + +impl fmt::Display for Duration { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // technically speaking, negative duration is not valid ISO 8601, + // but we need to print it anyway. + let (abs, sign) = if self.secs < 0 { (-*self, "-") } else { (*self, "") }; + + let days = abs.secs / SECS_PER_DAY; + let secs = abs.secs - days * SECS_PER_DAY; + let hasdate = days != 0; + let hastime = (secs != 0 || abs.nanos != 0) || !hasdate; + + try!(write!(f, "{}P", sign)); + + if hasdate { + try!(write!(f, "{}D", days)); + } + if hastime { + if abs.nanos == 0 { + try!(write!(f, "T{}S", secs)); + } else if abs.nanos % NANOS_PER_MILLI == 0 { + try!(write!(f, "T{}.{:03}S", secs, abs.nanos / NANOS_PER_MILLI)); + } else if abs.nanos % NANOS_PER_MICRO == 0 { + try!(write!(f, "T{}.{:06}S", secs, abs.nanos / NANOS_PER_MICRO)); + } else { + try!(write!(f, "T{}.{:09}S", secs, abs.nanos)); + } + } + Ok(()) + } +} + +/// Represents error when converting `Duration` to/from a standard library +/// implementation +/// +/// The `std::time::Duration` supports a range from zero to `u64::MAX` +/// *seconds*, while this module supports signed range of up to +/// `i64::MAX` of *milliseconds*. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct OutOfRangeError(()); + +impl fmt::Display for OutOfRangeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.description()) + } +} + +impl Error for OutOfRangeError { + fn description(&self) -> &str { + "Source duration value is out of range for the target type" + } +} + +// Copied from libnum +#[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, MAX, OutOfRangeError}; + use std::{i32, i64}; + use std::time::Duration as StdDuration; + + #[test] + fn test_duration() { + assert!(Duration::seconds(1) != Duration::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() { + assert_eq!(Duration::zero().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 as i64).num_days(), i32::MAX as i64); + assert_eq!(Duration::days(i32::MIN as i64).num_days(), i32::MIN as i64); + } + + #[test] + fn test_duration_num_seconds() { + assert_eq!(Duration::zero().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); + } + + #[test] + fn test_duration_num_milliseconds() { + assert_eq!(Duration::zero().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(i64::MAX).num_milliseconds(), i64::MAX); + assert_eq!(Duration::milliseconds(i64::MIN).num_milliseconds(), i64::MIN); + assert_eq!(MAX.num_milliseconds(), i64::MAX); + assert_eq!(MIN.num_milliseconds(), i64::MIN); + } + + #[test] + fn test_duration_num_microseconds() { + assert_eq!(Duration::zero().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(i64::MAX).num_microseconds(), Some(i64::MAX)); + assert_eq!(Duration::microseconds(i64::MIN).num_microseconds(), Some(i64::MIN)); + assert_eq!(MAX.num_microseconds(), None); + assert_eq!(MIN.num_microseconds(), None); + + // overflow checks + const MICROS_PER_DAY: i64 = 86400_000_000; + assert_eq!(Duration::days(i64::MAX / MICROS_PER_DAY).num_microseconds(), + Some(i64::MAX / MICROS_PER_DAY * MICROS_PER_DAY)); + assert_eq!(Duration::days(i64::MIN / MICROS_PER_DAY).num_microseconds(), + Some(i64::MIN / MICROS_PER_DAY * MICROS_PER_DAY)); + assert_eq!(Duration::days(i64::MAX / MICROS_PER_DAY + 1).num_microseconds(), None); + assert_eq!(Duration::days(i64::MIN / MICROS_PER_DAY - 1).num_microseconds(), None); + } + + #[test] + fn test_duration_num_nanoseconds() { + assert_eq!(Duration::zero().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(i64::MAX).num_nanoseconds(), Some(i64::MAX)); + assert_eq!(Duration::nanoseconds(i64::MIN).num_nanoseconds(), Some(i64::MIN)); + assert_eq!(MAX.num_nanoseconds(), None); + assert_eq!(MIN.num_nanoseconds(), None); + + // overflow checks + const NANOS_PER_DAY: i64 = 86400_000_000_000; + assert_eq!(Duration::days(i64::MAX / NANOS_PER_DAY).num_nanoseconds(), + Some(i64::MAX / NANOS_PER_DAY * NANOS_PER_DAY)); + assert_eq!(Duration::days(i64::MIN / NANOS_PER_DAY).num_nanoseconds(), + Some(i64::MIN / NANOS_PER_DAY * NANOS_PER_DAY)); + assert_eq!(Duration::days(i64::MAX / NANOS_PER_DAY + 1).num_nanoseconds(), None); + assert_eq!(Duration::days(i64::MIN / NANOS_PER_DAY - 1).num_nanoseconds(), None); + } + + #[test] + fn test_duration_checked_ops() { + assert_eq!(Duration::milliseconds(i64::MAX - 1).checked_add(&Duration::microseconds(999)), + Some(Duration::milliseconds(i64::MAX - 2) + Duration::microseconds(1999))); + assert!(Duration::milliseconds(i64::MAX).checked_add(&Duration::microseconds(1000)) + .is_none()); + + assert_eq!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(0)), + Some(Duration::milliseconds(i64::MIN))); + assert!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(1)) + .is_none()); + } + + #[test] + fn test_duration_mul() { + assert_eq!(Duration::zero() * i32::MAX, Duration::zero()); + assert_eq!(Duration::zero() * i32::MIN, Duration::zero()); + assert_eq!(Duration::nanoseconds(1) * 0, Duration::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)); + assert_eq!(Duration::milliseconds(1500) * -2, Duration::seconds(-3)); + assert_eq!(Duration::milliseconds(-1500) * 2, Duration::seconds(-3)); + } + + #[test] + fn test_duration_div() { + assert_eq!(Duration::zero() / i32::MAX, Duration::zero()); + assert_eq!(Duration::zero() / i32::MIN, Duration::zero()); + assert_eq!(Duration::nanoseconds(123_456_789) / 1, Duration::nanoseconds(123_456_789)); + assert_eq!(Duration::nanoseconds(123_456_789) / -1, -Duration::nanoseconds(123_456_789)); + assert_eq!(-Duration::nanoseconds(123_456_789) / -1, Duration::nanoseconds(123_456_789)); + assert_eq!(-Duration::nanoseconds(123_456_789) / 1, -Duration::nanoseconds(123_456_789)); + assert_eq!(Duration::seconds(1) / 3, Duration::nanoseconds(333_333_333)); + assert_eq!(Duration::seconds(4) / 3, Duration::nanoseconds(1_333_333_333)); + assert_eq!(Duration::seconds(-1) / 2, Duration::milliseconds(-500)); + assert_eq!(Duration::seconds(1) / -2, Duration::milliseconds(-500)); + assert_eq!(Duration::seconds(-1) / -2, Duration::milliseconds(500)); + assert_eq!(Duration::seconds(-4) / 3, Duration::nanoseconds(-1_333_333_333)); + assert_eq!(Duration::seconds(-4) / -3, Duration::nanoseconds(1_333_333_333)); + } + + #[test] + fn test_duration_fmt() { + assert_eq!(Duration::zero().to_string(), "PT0S"); + assert_eq!(Duration::days(42).to_string(), "P42D"); + assert_eq!(Duration::days(-42).to_string(), "-P42D"); + assert_eq!(Duration::seconds(42).to_string(), "PT42S"); + assert_eq!(Duration::milliseconds(42).to_string(), "PT0.042S"); + assert_eq!(Duration::microseconds(42).to_string(), "PT0.000042S"); + assert_eq!(Duration::nanoseconds(42).to_string(), "PT0.000000042S"); + assert_eq!((Duration::days(7) + Duration::milliseconds(6543)).to_string(), + "P7DT6.543S"); + assert_eq!(Duration::seconds(-86401).to_string(), "-P1DT1S"); + assert_eq!(Duration::nanoseconds(-1).to_string(), "-PT0.000000001S"); + + // the format specifier should have no effect on `Duration` + assert_eq!(format!("{:30}", Duration::days(1) + Duration::milliseconds(2345)), + "P1DT2.345S"); + } + + #[test] + fn test_to_std() { + assert_eq!(Duration::seconds(1).to_std(), Ok(StdDuration::new(1, 0))); + assert_eq!(Duration::seconds(86401).to_std(), Ok(StdDuration::new(86401, 0))); + assert_eq!(Duration::milliseconds(123).to_std(), Ok(StdDuration::new(0, 123000000))); + assert_eq!(Duration::milliseconds(123765).to_std(), Ok(StdDuration::new(123, 765000000))); + assert_eq!(Duration::nanoseconds(777).to_std(), Ok(StdDuration::new(0, 777))); + assert_eq!(MAX.to_std(), Ok(StdDuration::new(9223372036854775, 807000000))); + assert_eq!(Duration::seconds(-1).to_std(), + Err(OutOfRangeError(()))); + assert_eq!(Duration::milliseconds(-1).to_std(), + Err(OutOfRangeError(()))); + } + + #[test] + fn test_from_std() { + assert_eq!(Ok(Duration::seconds(1)), + Duration::from_std(StdDuration::new(1, 0))); + assert_eq!(Ok(Duration::seconds(86401)), + Duration::from_std(StdDuration::new(86401, 0))); + assert_eq!(Ok(Duration::milliseconds(123)), + Duration::from_std(StdDuration::new(0, 123000000))); + assert_eq!(Ok(Duration::milliseconds(123765)), + Duration::from_std(StdDuration::new(123, 765000000))); + assert_eq!(Ok(Duration::nanoseconds(777)), + Duration::from_std(StdDuration::new(0, 777))); + assert_eq!(Ok(MAX), + Duration::from_std(StdDuration::new(9223372036854775, 807000000))); + assert_eq!(Duration::from_std(StdDuration::new(9223372036854776, 0)), + Err(OutOfRangeError(()))); + assert_eq!(Duration::from_std(StdDuration::new(9223372036854775, 807000001)), + Err(OutOfRangeError(()))); + } +} diff --git a/src/time/mod.rs b/src/time/mod.rs new file mode 100644 index 0000000000..c550efb2b1 --- /dev/null +++ b/src/time/mod.rs @@ -0,0 +1,1263 @@ +// Copyright 2012-2013 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. + +//! Simple time handling. +//! This crate uses the same syntax for format strings as the +//! [`strftime()`](http://man7.org/linux/man-pages/man3/strftime.3.html) +//! function from the C standard library. + +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://www.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/time/")] +#![allow(trivial_numeric_casts)] +// #![cfg_attr(test, deny(warnings))] + +mod display; +mod duration; +mod parse; +mod sys; + +use std::cmp::Ordering; +use std::error::Error; +use std::fmt; +use std::ops::{Add, Sub}; + +pub use self::duration::{Duration, OutOfRangeError}; + +use self::ParseError::{InvalidDay, InvalidDayOfMonth, InvalidDayOfWeek, + InvalidDayOfYear, InvalidFormatSpecifier, InvalidHour, + InvalidMinute, InvalidMonth, InvalidSecond, InvalidTime, + InvalidYear, InvalidZoneOffset, InvalidSecondsSinceEpoch, + MissingFormatConverter, UnexpectedCharacter}; + +pub use self::parse::strptime; + +pub static NSEC_PER_SEC: i32 = 1_000_000_000; + +/// A record specifying a time value in seconds and nanoseconds, where +/// nanoseconds represent the offset from the given second. +/// +/// For example a timespec of 1.2 seconds after the beginning of the epoch would +/// be represented as {sec: 1, nsec: 200000000}. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))] +pub struct Timespec { pub sec: i64, pub nsec: i32 } +/* + * Timespec assumes that pre-epoch Timespecs have negative sec and positive + * nsec fields. Darwin's and Linux's struct timespec functions handle pre- + * epoch timestamps using a "two steps back, one step forward" representation, + * though the man pages do not actually document this. For example, the time + * -1.2 seconds before the epoch is represented by `Timespec { sec: -2_i64, + * nsec: 800_000_000 }`. + */ +impl Timespec { + pub fn new(sec: i64, nsec: i32) -> Timespec { + assert!(nsec >= 0 && nsec < NSEC_PER_SEC); + Timespec { sec: sec, nsec: nsec } + } +} + +impl Add for Timespec { + type Output = Timespec; + + fn add(self, other: Duration) -> Timespec { + let d_sec = other.num_seconds(); + // It is safe to unwrap the nanoseconds, because there cannot be + // more than one second left, which fits in i64 and in i32. + let d_nsec = (other - Duration::seconds(d_sec)) + .num_nanoseconds().unwrap() as i32; + let mut sec = self.sec + d_sec; + let mut nsec = self.nsec + d_nsec; + if nsec >= NSEC_PER_SEC { + nsec -= NSEC_PER_SEC; + sec += 1; + } else if nsec < 0 { + nsec += NSEC_PER_SEC; + sec -= 1; + } + Timespec::new(sec, nsec) + } +} + +impl Sub for Timespec { + type Output = Timespec; + + fn sub(self, other: Duration) -> Timespec { + let d_sec = other.num_seconds(); + // It is safe to unwrap the nanoseconds, because there cannot be + // more than one second left, which fits in i64 and in i32. + let d_nsec = (other - Duration::seconds(d_sec)) + .num_nanoseconds().unwrap() as i32; + let mut sec = self.sec - d_sec; + let mut nsec = self.nsec - d_nsec; + if nsec >= NSEC_PER_SEC { + nsec -= NSEC_PER_SEC; + sec += 1; + } else if nsec < 0 { + nsec += NSEC_PER_SEC; + sec -= 1; + } + Timespec::new(sec, nsec) + } +} + +impl Sub for Timespec { + type Output = Duration; + + fn sub(self, other: Timespec) -> Duration { + let sec = self.sec - other.sec; + let nsec = self.nsec - other.nsec; + Duration::seconds(sec) + Duration::nanoseconds(nsec as i64) + } +} + +/** + * Returns the current time as a `timespec` containing the seconds and + * nanoseconds since 1970-01-01T00:00:00Z. + */ +pub fn get_time() -> Timespec { + let (sec, nsec) = sys::get_time(); + Timespec::new(sec, nsec) +} + + +/** + * Returns the current value of a high-resolution performance counter + * in nanoseconds since an unspecified epoch. + */ +#[inline] +pub fn precise_time_ns() -> u64 { + sys::get_precise_ns() +} + + +/** + * Returns the current value of a high-resolution performance counter + * in seconds since an unspecified epoch. + */ +#[allow(unused)] +pub fn precise_time_s() -> f64 { + return (precise_time_ns() as f64) / 1000000000.; +} + +/// An opaque structure representing a moment in time. +/// +/// The only operation that can be performed on a `PreciseTime` is the +/// calculation of the `Duration` of time that lies between them. +/// +/// # Examples +/// +/// Repeatedly call a function for 1 second: +/// +/// ```rust +/// use chrono::{Duration, PreciseTime}; +/// # fn do_some_work() {} +/// +/// let start = PreciseTime::now(); +/// +/// while start.to(PreciseTime::now()) < Duration::seconds(1) { +/// do_some_work(); +/// } +/// ``` +#[derive(Debug, Copy, Clone)] +pub struct PreciseTime(u64); + +impl PreciseTime { + /// Returns a `PreciseTime` representing the current moment in time. + pub fn now() -> PreciseTime { + PreciseTime(precise_time_ns()) + } + + /// Returns a `Duration` representing the span of time from the value of + /// `self` to the value of `later`. + /// + /// # Notes + /// + /// If `later` represents a time before `self`, the result of this method + /// is unspecified. + /// + /// If `later` represents a time more than 293 years after `self`, the + /// result of this method is unspecified. + #[inline] + pub fn to(&self, later: PreciseTime) -> Duration { + // NB: even if later is less than self due to overflow, this will work + // since the subtraction will underflow properly as well. + // + // We could deal with the overflow when casting to an i64, but all that + // gets us is the ability to handle intervals of up to 584 years, which + // seems not very useful :) + Duration::nanoseconds((later.0 - self.0) as i64) + } +} + +/// A structure representing a moment in time. +/// +/// `SteadyTime`s are generated by a "steady" clock, that is, a clock which +/// never experiences discontinuous jumps and for which time always flows at +/// the same rate. +/// +/// # Examples +/// +/// Repeatedly call a function for 1 second: +/// +/// ```rust +/// # use chrono::{Duration, SteadyTime}; +/// # fn do_some_work() {} +/// let start = SteadyTime::now(); +/// +/// while SteadyTime::now() - start < Duration::seconds(1) { +/// do_some_work(); +/// } +/// ``` +#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug)] +pub struct SteadyTime(sys::SteadyTime); + +impl SteadyTime { + /// Returns a `SteadyTime` representing the current moment in time. + pub fn now() -> SteadyTime { + SteadyTime(sys::SteadyTime::now()) + } +} + +impl fmt::Display for SteadyTime { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + // TODO: needs a display customization + fmt::Debug::fmt(self, fmt) + } +} + +impl Sub for SteadyTime { + type Output = Duration; + + fn sub(self, other: SteadyTime) -> Duration { + self.0 - other.0 + } +} + +impl Sub for SteadyTime { + type Output = SteadyTime; + + fn sub(self, other: Duration) -> SteadyTime { + SteadyTime(self.0 - other) + } +} + +impl Add for SteadyTime { + type Output = SteadyTime; + + fn add(self, other: Duration) -> SteadyTime { + SteadyTime(self.0 + other) + } +} + +#[cfg(not(windows))] +#[allow(unused)] +pub fn tzset() { + extern { fn tzset(); } + unsafe { tzset() } +} + + +#[cfg(windows)] +pub fn tzset() {} + +/// Holds a calendar date and time broken down into its components (year, month, +/// day, and so on), also called a broken-down time value. +// FIXME: use c_int instead of i32? +#[repr(C)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))] +pub struct Tm { + /// Seconds after the minute - [0, 60] + pub tm_sec: i32, + + /// Minutes after the hour - [0, 59] + pub tm_min: i32, + + /// Hours after midnight - [0, 23] + pub tm_hour: i32, + + /// Day of the month - [1, 31] + pub tm_mday: i32, + + /// Months since January - [0, 11] + pub tm_mon: i32, + + /// Years since 1900 + pub tm_year: i32, + + /// Days since Sunday - [0, 6]. 0 = Sunday, 1 = Monday, ..., 6 = Saturday. + pub tm_wday: i32, + + /// Days since January 1 - [0, 365] + pub tm_yday: i32, + + /// Daylight Saving Time flag. + /// + /// This value is positive if Daylight Saving Time is in effect, zero if + /// Daylight Saving Time is not in effect, and negative if this information + /// is not available. + pub tm_isdst: i32, + + /// Identifies the time zone that was used to compute this broken-down time + /// value, including any adjustment for Daylight Saving Time. This is the + /// number of seconds east of UTC. For example, for U.S. Pacific Daylight + /// Time, the value is `-7*60*60 = -25200`. + pub tm_utcoff: i32, + + /// Nanoseconds after the second - [0, 109 - 1] + pub tm_nsec: i32, +} + +impl Add for Tm { + type Output = Tm; + + /// The resulting Tm is in UTC. + // FIXME: The resulting Tm should have the same timezone as `self`; + // however, we need a function such as `at_tm(clock: Timespec, offset: i32)` + // for this. + fn add(self, other: Duration) -> Tm { + at_utc(self.to_timespec() + other) + } +} + +impl Sub for Tm { + type Output = Tm; + + /// The resulting Tm is in UTC. + // FIXME: The resulting Tm should have the same timezone as `self`; + // however, we need a function such as `at_tm(clock: Timespec, offset: i32)` + // for this. + fn sub(self, other: Duration) -> Tm { + at_utc(self.to_timespec() - other) + } +} + +impl Sub for Tm { + type Output = Duration; + + fn sub(self, other: Tm) -> Duration { + self.to_timespec() - other.to_timespec() + } +} + +impl PartialOrd for Tm { + fn partial_cmp(&self, other: &Tm) -> Option { + self.to_timespec().partial_cmp(&other.to_timespec()) + } +} + +impl Ord for Tm { + fn cmp(&self, other: &Tm) -> Ordering { + self.to_timespec().cmp(&other.to_timespec()) + } +} + +pub fn empty_tm() -> Tm { + Tm { + tm_sec: 0, + tm_min: 0, + tm_hour: 0, + tm_mday: 0, + tm_mon: 0, + tm_year: 0, + tm_wday: 0, + tm_yday: 0, + tm_isdst: 0, + tm_utcoff: 0, + tm_nsec: 0, + } +} + +/// Returns the specified time in UTC +pub fn at_utc(clock: Timespec) -> Tm { + let Timespec { sec, nsec } = clock; + let mut tm = empty_tm(); + sys::time_to_utc_tm(sec, &mut tm); + tm.tm_nsec = nsec; + tm +} + +/// Returns the current time in UTC +#[allow(unused)] +pub fn now_utc() -> Tm { + at_utc(get_time()) +} + +/// Returns the specified time in the local timezone +pub fn at(clock: Timespec) -> Tm { + let Timespec { sec, nsec } = clock; + let mut tm = empty_tm(); + sys::time_to_local_tm(sec, &mut tm); + tm.tm_nsec = nsec; + tm +} + +/// Returns the current time in the local timezone +pub fn now() -> Tm { + at(get_time()) +} + +impl Tm { + /// Convert time to the seconds from January 1, 1970 + pub fn to_timespec(&self) -> Timespec { + let sec = match self.tm_utcoff { + 0 => sys::utc_tm_to_time(self), + _ => sys::local_tm_to_time(self) + }; + + Timespec::new(sec, self.tm_nsec) + } + + /// Convert time to the local timezone + pub fn to_local(&self) -> Tm { + at(self.to_timespec()) + } + + /// Convert time to the UTC + #[allow(unused)] + pub fn to_utc(&self) -> Tm { + match self.tm_utcoff { + 0 => *self, + _ => at_utc(self.to_timespec()) + } + } + + /** + * Returns a TmFmt that outputs according to the `asctime` format in ISO + * C, in the local timezone. + * + * Example: "Thu Jan 1 00:00:00 1970" + */ + #[allow(unused)] + pub fn ctime(&self) -> TmFmt { + TmFmt { + tm: self, + format: Fmt::Ctime, + } + } + + /** + * Returns a TmFmt that outputs according to the `asctime` format in ISO + * C. + * + * Example: "Thu Jan 1 00:00:00 1970" + */ + pub fn asctime(&self) -> TmFmt { + TmFmt { + tm: self, + format: Fmt::Str("%c"), + } + } + + /// Formats the time according to the format string. + #[allow(unused)] + pub fn strftime<'a>(&'a self, format: &'a str) -> Result, ParseError> { + validate_format(TmFmt { + tm: self, + format: Fmt::Str(format), + }) + } + + /** + * Returns a TmFmt that outputs according to RFC 822. + * + * local: "Thu, 22 Mar 2012 07:53:18 PST" + * utc: "Thu, 22 Mar 2012 14:53:18 GMT" + */ + #[allow(unused)] + pub fn rfc822(&self) -> TmFmt { + let fmt = if self.tm_utcoff == 0 { + "%a, %d %b %Y %T GMT" + } else { + "%a, %d %b %Y %T %Z" + }; + TmFmt { + tm: self, + format: Fmt::Str(fmt), + } + } + + /** + * Returns a TmFmt that outputs according to RFC 822 with Zulu time. + * + * local: "Thu, 22 Mar 2012 07:53:18 -0700" + * utc: "Thu, 22 Mar 2012 14:53:18 -0000" + */ + #[allow(unused)] + pub fn rfc822z(&self) -> TmFmt { + TmFmt { + tm: self, + format: Fmt::Str("%a, %d %b %Y %T %z"), + } + } + + /** + * Returns a TmFmt that outputs according to RFC 3339. RFC 3339 is + * compatible with ISO 8601. + * + * local: "2012-02-22T07:53:18-07:00" + * utc: "2012-02-22T14:53:18Z" + */ + pub fn rfc3339<'a>(&'a self) -> TmFmt { + TmFmt { + tm: self, + format: Fmt::Rfc3339, + } + } +} + +#[derive(Copy, PartialEq, Debug, Clone)] +pub enum ParseError { + InvalidSecond, + InvalidMinute, + InvalidHour, + InvalidDay, + InvalidMonth, + InvalidYear, + InvalidDayOfWeek, + InvalidDayOfMonth, + InvalidDayOfYear, + InvalidZoneOffset, + InvalidTime, + InvalidSecondsSinceEpoch, + MissingFormatConverter, + InvalidFormatSpecifier(char), + UnexpectedCharacter(char, char), +} + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + InvalidFormatSpecifier(ch) => { + write!(f, "{}: %{}", self.description(), ch) + } + UnexpectedCharacter(a, b) => { + write!(f, "expected: `{}`, found: `{}`", a, b) + } + _ => write!(f, "{}", self.description()) + } + } +} + +impl Error for ParseError { + fn description(&self) -> &str { + match *self { + InvalidSecond => "Invalid second.", + InvalidMinute => "Invalid minute.", + InvalidHour => "Invalid hour.", + InvalidDay => "Invalid day.", + InvalidMonth => "Invalid month.", + InvalidYear => "Invalid year.", + InvalidDayOfWeek => "Invalid day of the week.", + InvalidDayOfMonth => "Invalid day of the month.", + InvalidDayOfYear => "Invalid day of the year.", + InvalidZoneOffset => "Invalid zone offset.", + InvalidTime => "Invalid time.", + InvalidSecondsSinceEpoch => "Invalid seconds since epoch.", + MissingFormatConverter => "missing format converter after `%`", + InvalidFormatSpecifier(..) => "invalid format specifier", + UnexpectedCharacter(..) => "Unexpected character.", + } + } +} + +/// A wrapper around a `Tm` and format string that implements Display. +#[derive(Debug)] +pub struct TmFmt<'a> { + tm: &'a Tm, + format: Fmt<'a> +} + +#[derive(Debug)] +enum Fmt<'a> { + Str(&'a str), + Rfc3339, + Ctime, +} + +fn validate_format<'a>(fmt: TmFmt<'a>) -> Result, ParseError> { + + match (fmt.tm.tm_wday, fmt.tm.tm_mon) { + (0...6, 0...11) => (), + (_wday, 0...11) => return Err(InvalidDayOfWeek), + (0...6, _mon) => return Err(InvalidMonth), + _ => return Err(InvalidDay) + } + match fmt.format { + Fmt::Str(ref s) => { + let mut chars = s.chars(); + loop { + match chars.next() { + Some('%') => { + match chars.next() { + Some('A') | Some('a') | Some('B') | Some('b') | + Some('C') | Some('c') | Some('D') | Some('d') | + Some('e') | Some('F') | Some('f') | Some('G') | + Some('g') | Some('H') | Some('h') | Some('I') | + Some('j') | Some('k') | Some('l') | Some('M') | + Some('m') | Some('n') | Some('P') | Some('p') | + Some('R') | Some('r') | Some('S') | Some('s') | + Some('T') | Some('t') | Some('U') | Some('u') | + Some('V') | Some('v') | Some('W') | Some('w') | + Some('X') | Some('x') | Some('Y') | Some('y') | + Some('Z') | Some('z') | Some('+') | Some('%') => (), + + Some(c) => return Err(InvalidFormatSpecifier(c)), + None => return Err(MissingFormatConverter), + } + }, + None => break, + _ => () + } + } + }, + _ => () + } + Ok(fmt) +} + +/// Formats the time according to the format string. +#[allow(unused)] +pub fn strftime(format: &str, tm: &Tm) -> Result { + tm.strftime(format).map(|fmt| fmt.to_string()) +} + +#[cfg(test)] +mod tests { + use crate::time::sys; + + use super::{Timespec, get_time, precise_time_ns, precise_time_s, + at_utc, at, strptime, PreciseTime, SteadyTime, ParseError, Duration}; + use super::ParseError::{InvalidTime, InvalidYear, MissingFormatConverter, + InvalidFormatSpecifier}; + + use std::sync::{Once, ONCE_INIT, Mutex, MutexGuard, LockResult}; + use std::mem; + + struct TzReset { + _tzreset: sys::TzReset, + _lock: LockResult>, + } + + fn set_time_zone_la_or_london(london: bool) -> TzReset { + // Lock manages current timezone because some tests require LA some + // London + static mut LOCK: *mut Mutex<()> = 0 as *mut _; + static INIT: Once = ONCE_INIT; + + unsafe { + INIT.call_once(|| { + LOCK = mem::transmute(Box::new(Mutex::new(()))); + }); + + let timezone_lock = (*LOCK).lock(); + let reset_func = if london { + sys::set_london_with_dst_time_zone() + } else { + sys::set_los_angeles_time_zone() + }; + TzReset { + _lock: timezone_lock, + _tzreset: reset_func, + } + } + } + + fn set_time_zone() -> TzReset { + set_time_zone_la_or_london(false) + } + + fn set_time_zone_london_dst() -> TzReset { + set_time_zone_la_or_london(true) + } + + #[test] + fn test_get_time() { + static SOME_RECENT_DATE: i64 = 1325376000i64; // 2012-01-01T00:00:00Z + static SOME_FUTURE_DATE: i64 = 1577836800i64; // 2020-01-01T00:00:00Z + + let tv1 = get_time(); + debug!("tv1={} sec + {} nsec", tv1.sec, tv1.nsec); + + assert!(tv1.sec > SOME_RECENT_DATE); + assert!(tv1.nsec < 1000000000i32); + + let tv2 = get_time(); + debug!("tv2={} sec + {} nsec", tv2.sec, tv2.nsec); + + assert!(tv2.sec >= tv1.sec); + assert!(tv2.sec < SOME_FUTURE_DATE); + assert!(tv2.nsec < 1000000000i32); + if tv2.sec == tv1.sec { + assert!(tv2.nsec >= tv1.nsec); + } + } + + #[test] + fn test_precise_time() { + let s0 = precise_time_s(); + debug!("s0={} sec", s0); + assert!(s0 > 0.); + + let ns0 = precise_time_ns(); + let ns1 = precise_time_ns(); + debug!("ns0={} ns", ns0); + debug!("ns1={} ns", ns1); + assert!(ns1 >= ns0); + + let ns2 = precise_time_ns(); + debug!("ns2={} ns", ns2); + assert!(ns2 >= ns1); + } + + #[test] + fn test_precise_time_to() { + let t0 = PreciseTime(1000); + let t1 = PreciseTime(1023); + assert_eq!(Duration::nanoseconds(23), t0.to(t1)); + } + + #[test] + fn test_at_utc() { + let _reset = set_time_zone(); + + let time = Timespec::new(1234567890, 54321); + let utc = at_utc(time); + + assert_eq!(utc.tm_sec, 30); + assert_eq!(utc.tm_min, 31); + assert_eq!(utc.tm_hour, 23); + assert_eq!(utc.tm_mday, 13); + assert_eq!(utc.tm_mon, 1); + assert_eq!(utc.tm_year, 109); + assert_eq!(utc.tm_wday, 5); + assert_eq!(utc.tm_yday, 43); + assert_eq!(utc.tm_isdst, 0); + assert_eq!(utc.tm_utcoff, 0); + assert_eq!(utc.tm_nsec, 54321); + } + + #[test] + fn test_at() { + let _reset = set_time_zone(); + + let time = Timespec::new(1234567890, 54321); + let local = at(time); + + debug!("time_at: {:?}", local); + + assert_eq!(local.tm_sec, 30); + assert_eq!(local.tm_min, 31); + assert_eq!(local.tm_hour, 15); + assert_eq!(local.tm_mday, 13); + assert_eq!(local.tm_mon, 1); + assert_eq!(local.tm_year, 109); + assert_eq!(local.tm_wday, 5); + assert_eq!(local.tm_yday, 43); + assert_eq!(local.tm_isdst, 0); + assert_eq!(local.tm_utcoff, -28800); + assert_eq!(local.tm_nsec, 54321); + } + + #[test] + fn test_to_timespec() { + let _reset = set_time_zone(); + + let time = Timespec::new(1234567890, 54321); + let utc = at_utc(time); + + assert_eq!(utc.to_timespec(), time); + assert_eq!(utc.to_local().to_timespec(), time); + } + + #[test] + fn test_conversions() { + let _reset = set_time_zone(); + + let time = Timespec::new(1234567890, 54321); + let utc = at_utc(time); + let local = at(time); + + assert!(local.to_local() == local); + assert!(local.to_utc() == utc); + assert!(local.to_utc().to_local() == local); + assert!(utc.to_utc() == utc); + assert!(utc.to_local() == local); + assert!(utc.to_local().to_utc() == utc); + } + + #[test] + fn test_strptime() { + let _reset = set_time_zone(); + + match strptime("", "") { + Ok(ref tm) => { + assert!(tm.tm_sec == 0); + assert!(tm.tm_min == 0); + assert!(tm.tm_hour == 0); + assert!(tm.tm_mday == 0); + assert!(tm.tm_mon == 0); + assert!(tm.tm_year == 0); + assert!(tm.tm_wday == 0); + assert!(tm.tm_isdst == 0); + assert!(tm.tm_utcoff == 0); + assert!(tm.tm_nsec == 0); + } + Err(_) => () + } + + let format = "%a %b %e %T.%f %Y"; + assert_eq!(strptime("", format), Err(ParseError::InvalidDay)); + assert_eq!(strptime("Fri Feb 13 15:31:30", format), + Err(InvalidTime)); + + match strptime("Fri Feb 13 15:31:30.01234 2009", format) { + Err(e) => panic!("{}", e), + Ok(ref tm) => { + assert_eq!(tm.tm_sec, 30); + assert_eq!(tm.tm_min, 31); + assert_eq!(tm.tm_hour, 15); + assert_eq!(tm.tm_mday, 13); + assert_eq!(tm.tm_mon, 1); + assert_eq!(tm.tm_year, 109); + assert_eq!(tm.tm_wday, 5); + assert_eq!(tm.tm_yday, 0); + assert_eq!(tm.tm_isdst, 0); + assert_eq!(tm.tm_utcoff, 0); + assert_eq!(tm.tm_nsec, 12340000); + } + } + + fn test(s: &str, format: &str) -> bool { + match strptime(s, format) { + Ok(tm) => { + tm.strftime(format).unwrap().to_string() == s.to_string() + }, + Err(e) => panic!("{:?}, s={:?}, format={:?}", e, s, format) + } + } + + fn test_oneway(s : &str, format : &str) -> bool { + match strptime(s, format) { + Ok(_) => { + // oneway tests are used when reformatting the parsed Tm + // back into a string can generate a different string + // from the original (i.e. leading zeroes) + true + }, + Err(e) => panic!("{:?}, s={:?}, format={:?}", e, s, format) + } + } + + let days = [ + "Sunday".to_string(), + "Monday".to_string(), + "Tuesday".to_string(), + "Wednesday".to_string(), + "Thursday".to_string(), + "Friday".to_string(), + "Saturday".to_string() + ]; + for day in days.iter() { + assert!(test(&day, "%A")); + } + + let days = [ + "Sun".to_string(), + "Mon".to_string(), + "Tue".to_string(), + "Wed".to_string(), + "Thu".to_string(), + "Fri".to_string(), + "Sat".to_string() + ]; + for day in days.iter() { + assert!(test(&day, "%a")); + } + + let months = [ + "January".to_string(), + "February".to_string(), + "March".to_string(), + "April".to_string(), + "May".to_string(), + "June".to_string(), + "July".to_string(), + "August".to_string(), + "September".to_string(), + "October".to_string(), + "November".to_string(), + "December".to_string() + ]; + for day in months.iter() { + assert!(test(&day, "%B")); + } + + let months = [ + "Jan".to_string(), + "Feb".to_string(), + "Mar".to_string(), + "Apr".to_string(), + "May".to_string(), + "Jun".to_string(), + "Jul".to_string(), + "Aug".to_string(), + "Sep".to_string(), + "Oct".to_string(), + "Nov".to_string(), + "Dec".to_string() + ]; + for day in months.iter() { + assert!(test(&day, "%b")); + } + + assert!(test("19", "%C")); + assert!(test("Fri Feb 3 23:31:30 2009", "%c")); + assert!(test("Fri Feb 13 23:31:30 2009", "%c")); + assert!(test("02/13/09", "%D")); + assert!(test("03", "%d")); + assert!(test("13", "%d")); + assert!(test(" 3", "%e")); + assert!(test("13", "%e")); + assert!(test("2009-02-13", "%F")); + assert!(test("03", "%H")); + assert!(test("13", "%H")); + assert!(test("03", "%I")); // FIXME (#2350): flesh out + assert!(test("11", "%I")); // FIXME (#2350): flesh out + assert!(test("044", "%j")); + assert!(test(" 3", "%k")); + assert!(test("13", "%k")); + assert!(test(" 1", "%l")); + assert!(test("11", "%l")); + assert!(test("03", "%M")); + assert!(test("13", "%M")); + assert!(test("\n", "%n")); + assert!(test("am", "%P")); + assert!(test("pm", "%P")); + assert!(test("AM", "%p")); + assert!(test("PM", "%p")); + assert!(test("23:31", "%R")); + assert!(test("11:31:30 AM", "%r")); + assert!(test("11:31:30 PM", "%r")); + assert!(test("03", "%S")); + assert!(test("13", "%S")); + assert!(test("15:31:30", "%T")); + assert!(test("\t", "%t")); + assert!(test("1", "%u")); + assert!(test("7", "%u")); + assert!(test("13-Feb-2009", "%v")); + assert!(test("0", "%w")); + assert!(test("6", "%w")); + assert!(test("2009", "%Y")); + assert!(test("09", "%y")); + + assert!(test_oneway("3", "%d")); + assert!(test_oneway("3", "%H")); + assert!(test_oneway("3", "%e")); + assert!(test_oneway("3", "%M")); + assert!(test_oneway("3", "%S")); + + assert!(strptime("-0000", "%z").unwrap().tm_utcoff == 0); + assert!(strptime("-00:00", "%z").unwrap().tm_utcoff == 0); + assert!(strptime("Z", "%z").unwrap().tm_utcoff == 0); + assert_eq!(-28800, strptime("-0800", "%z").unwrap().tm_utcoff); + assert_eq!(-28800, strptime("-08:00", "%z").unwrap().tm_utcoff); + assert_eq!(28800, strptime("+0800", "%z").unwrap().tm_utcoff); + assert_eq!(28800, strptime("+08:00", "%z").unwrap().tm_utcoff); + assert_eq!(5400, strptime("+0130", "%z").unwrap().tm_utcoff); + assert_eq!(5400, strptime("+01:30", "%z").unwrap().tm_utcoff); + assert!(test("%", "%%")); + + // Test for #7256 + assert_eq!(strptime("360", "%Y-%m-%d"), Err(InvalidYear)); + + // Test for epoch seconds parsing + { + assert!(test("1428035610", "%s")); + let tm = strptime("1428035610", "%s").unwrap(); + assert_eq!(tm.tm_utcoff, 0); + assert_eq!(tm.tm_isdst, 0); + assert_eq!(tm.tm_yday, 92); + assert_eq!(tm.tm_wday, 5); + assert_eq!(tm.tm_year, 115); + assert_eq!(tm.tm_mon, 3); + assert_eq!(tm.tm_mday, 3); + assert_eq!(tm.tm_hour, 4); + } + } + + #[test] + fn test_asctime() { + let _reset = set_time_zone(); + + let time = Timespec::new(1234567890, 54321); + let utc = at_utc(time); + let local = at(time); + + debug!("test_ctime: {} {}", utc.asctime(), local.asctime()); + + assert_eq!(utc.asctime().to_string(), "Fri Feb 13 23:31:30 2009".to_string()); + assert_eq!(local.asctime().to_string(), "Fri Feb 13 15:31:30 2009".to_string()); + } + + #[test] + fn test_ctime() { + let _reset = set_time_zone(); + + let time = Timespec::new(1234567890, 54321); + let utc = at_utc(time); + let local = at(time); + + debug!("test_ctime: {} {}", utc.ctime(), local.ctime()); + + assert_eq!(utc.ctime().to_string(), "Fri Feb 13 15:31:30 2009".to_string()); + assert_eq!(local.ctime().to_string(), "Fri Feb 13 15:31:30 2009".to_string()); + } + + #[test] + fn test_strftime() { + let _reset = set_time_zone(); + + let time = Timespec::new(1234567890, 54321); + let utc = at_utc(time); + let local = at(time); + + assert_eq!(local.strftime("").unwrap().to_string(), "".to_string()); + assert_eq!(local.strftime("%A").unwrap().to_string(), "Friday".to_string()); + assert_eq!(local.strftime("%a").unwrap().to_string(), "Fri".to_string()); + assert_eq!(local.strftime("%B").unwrap().to_string(), "February".to_string()); + assert_eq!(local.strftime("%b").unwrap().to_string(), "Feb".to_string()); + assert_eq!(local.strftime("%C").unwrap().to_string(), "20".to_string()); + assert_eq!(local.strftime("%c").unwrap().to_string(), + "Fri Feb 13 15:31:30 2009".to_string()); + assert_eq!(local.strftime("%D").unwrap().to_string(), "02/13/09".to_string()); + assert_eq!(local.strftime("%d").unwrap().to_string(), "13".to_string()); + assert_eq!(local.strftime("%e").unwrap().to_string(), "13".to_string()); + assert_eq!(local.strftime("%F").unwrap().to_string(), "2009-02-13".to_string()); + assert_eq!(local.strftime("%f").unwrap().to_string(), "000054321".to_string()); + assert_eq!(local.strftime("%G").unwrap().to_string(), "2009".to_string()); + assert_eq!(local.strftime("%g").unwrap().to_string(), "09".to_string()); + assert_eq!(local.strftime("%H").unwrap().to_string(), "15".to_string()); + assert_eq!(local.strftime("%h").unwrap().to_string(), "Feb".to_string()); + assert_eq!(local.strftime("%I").unwrap().to_string(), "03".to_string()); + assert_eq!(local.strftime("%j").unwrap().to_string(), "044".to_string()); + assert_eq!(local.strftime("%k").unwrap().to_string(), "15".to_string()); + assert_eq!(local.strftime("%l").unwrap().to_string(), " 3".to_string()); + assert_eq!(local.strftime("%M").unwrap().to_string(), "31".to_string()); + assert_eq!(local.strftime("%m").unwrap().to_string(), "02".to_string()); + assert_eq!(local.strftime("%n").unwrap().to_string(), "\n".to_string()); + assert_eq!(local.strftime("%P").unwrap().to_string(), "pm".to_string()); + assert_eq!(local.strftime("%p").unwrap().to_string(), "PM".to_string()); + assert_eq!(local.strftime("%R").unwrap().to_string(), "15:31".to_string()); + assert_eq!(local.strftime("%r").unwrap().to_string(), "03:31:30 PM".to_string()); + assert_eq!(local.strftime("%S").unwrap().to_string(), "30".to_string()); + assert_eq!(local.strftime("%s").unwrap().to_string(), "1234567890".to_string()); + assert_eq!(local.strftime("%T").unwrap().to_string(), "15:31:30".to_string()); + assert_eq!(local.strftime("%t").unwrap().to_string(), "\t".to_string()); + assert_eq!(local.strftime("%U").unwrap().to_string(), "06".to_string()); + assert_eq!(local.strftime("%u").unwrap().to_string(), "5".to_string()); + assert_eq!(local.strftime("%V").unwrap().to_string(), "07".to_string()); + assert_eq!(local.strftime("%v").unwrap().to_string(), "13-Feb-2009".to_string()); + assert_eq!(local.strftime("%W").unwrap().to_string(), "06".to_string()); + assert_eq!(local.strftime("%w").unwrap().to_string(), "5".to_string()); + // FIXME (#2350): support locale + assert_eq!(local.strftime("%X").unwrap().to_string(), "15:31:30".to_string()); + // FIXME (#2350): support locale + assert_eq!(local.strftime("%x").unwrap().to_string(), "02/13/09".to_string()); + assert_eq!(local.strftime("%Y").unwrap().to_string(), "2009".to_string()); + assert_eq!(local.strftime("%y").unwrap().to_string(), "09".to_string()); + // FIXME (#2350): support locale + assert_eq!(local.strftime("%Z").unwrap().to_string(), "".to_string()); + assert_eq!(local.strftime("%z").unwrap().to_string(), "-0800".to_string()); + assert_eq!(local.strftime("%+").unwrap().to_string(), + "2009-02-13T15:31:30-08:00".to_string()); + assert_eq!(local.strftime("%%").unwrap().to_string(), "%".to_string()); + + let invalid_specifiers = ["%E", "%J", "%K", "%L", "%N", "%O", "%o", "%Q", "%q"]; + for &sp in invalid_specifiers.iter() { + assert_eq!(local.strftime(sp).unwrap_err(), + InvalidFormatSpecifier(sp[1..].chars().next().unwrap())); + } + assert_eq!(local.strftime("%").unwrap_err(), MissingFormatConverter); + assert_eq!(local.strftime("%A %").unwrap_err(), MissingFormatConverter); + + assert_eq!(local.asctime().to_string(), "Fri Feb 13 15:31:30 2009".to_string()); + assert_eq!(local.ctime().to_string(), "Fri Feb 13 15:31:30 2009".to_string()); + assert_eq!(local.rfc822z().to_string(), "Fri, 13 Feb 2009 15:31:30 -0800".to_string()); + assert_eq!(local.rfc3339().to_string(), "2009-02-13T15:31:30-08:00".to_string()); + + assert_eq!(utc.asctime().to_string(), "Fri Feb 13 23:31:30 2009".to_string()); + assert_eq!(utc.ctime().to_string(), "Fri Feb 13 15:31:30 2009".to_string()); + assert_eq!(utc.rfc822().to_string(), "Fri, 13 Feb 2009 23:31:30 GMT".to_string()); + assert_eq!(utc.rfc822z().to_string(), "Fri, 13 Feb 2009 23:31:30 -0000".to_string()); + assert_eq!(utc.rfc3339().to_string(), "2009-02-13T23:31:30Z".to_string()); + } + + #[test] + fn test_timespec_eq_ord() { + let a = &Timespec::new(-2, 1); + let b = &Timespec::new(-1, 2); + let c = &Timespec::new(1, 2); + let d = &Timespec::new(2, 1); + let e = &Timespec::new(2, 1); + + assert!(d.eq(e)); + assert!(c.ne(e)); + + assert!(a.lt(b)); + assert!(b.lt(c)); + assert!(c.lt(d)); + + assert!(a.le(b)); + assert!(b.le(c)); + assert!(c.le(d)); + assert!(d.le(e)); + assert!(e.le(d)); + + assert!(b.ge(a)); + assert!(c.ge(b)); + assert!(d.ge(c)); + assert!(e.ge(d)); + assert!(d.ge(e)); + + assert!(b.gt(a)); + assert!(c.gt(b)); + assert!(d.gt(c)); + } + + #[test] + #[allow(deprecated)] + fn test_timespec_hash() { + use std::hash::{Hash, Hasher}; + + let c = &Timespec::new(3, 2); + let d = &Timespec::new(2, 1); + let e = &Timespec::new(2, 1); + + let mut hasher = ::std::hash::SipHasher::new(); + + let d_hash:u64 = { + d.hash(&mut hasher); + hasher.finish() + }; + + hasher = ::std::hash::SipHasher::new(); + + let e_hash:u64 = { + e.hash(&mut hasher); + hasher.finish() + }; + + hasher = ::std::hash::SipHasher::new(); + + let c_hash:u64 = { + c.hash(&mut hasher); + hasher.finish() + }; + + assert_eq!(d_hash, e_hash); + assert!(c_hash != e_hash); + } + + #[test] + fn test_timespec_add() { + let a = Timespec::new(1, 2); + let b = Duration::seconds(2) + Duration::nanoseconds(3); + let c = a + b; + assert_eq!(c.sec, 3); + assert_eq!(c.nsec, 5); + + let p = Timespec::new(1, super::NSEC_PER_SEC - 2); + let q = Duration::seconds(2) + Duration::nanoseconds(2); + let r = p + q; + assert_eq!(r.sec, 4); + assert_eq!(r.nsec, 0); + + let u = Timespec::new(1, super::NSEC_PER_SEC - 2); + let v = Duration::seconds(2) + Duration::nanoseconds(3); + let w = u + v; + assert_eq!(w.sec, 4); + assert_eq!(w.nsec, 1); + + let k = Timespec::new(1, 0); + let l = Duration::nanoseconds(-1); + let m = k + l; + assert_eq!(m.sec, 0); + assert_eq!(m.nsec, 999_999_999); + } + + #[test] + fn test_timespec_sub() { + let a = Timespec::new(2, 3); + let b = Timespec::new(1, 2); + let c = a - b; + assert_eq!(c.num_nanoseconds(), Some(super::NSEC_PER_SEC as i64 + 1)); + + let p = Timespec::new(2, 0); + let q = Timespec::new(1, 2); + let r = p - q; + assert_eq!(r.num_nanoseconds(), Some(super::NSEC_PER_SEC as i64 - 2)); + + let u = Timespec::new(1, 2); + let v = Timespec::new(2, 3); + let w = u - v; + assert_eq!(w.num_nanoseconds(), Some(-super::NSEC_PER_SEC as i64 - 1)); + } + + #[test] + fn test_time_sub() { + let a = crate::time::now(); + let b = at(a.to_timespec() + Duration::seconds(5)); + let c = b - a; + assert_eq!(c.num_nanoseconds(), Some(super::NSEC_PER_SEC as i64 * 5)); + } + + #[test] + fn test_steadytime_sub() { + let a = SteadyTime::now(); + let b = a + Duration::seconds(1); + assert_eq!(b - a, Duration::seconds(1)); + assert_eq!(a - b, Duration::seconds(-1)); + } + + #[test] + fn test_date_before_1970() { + let early = strptime("1901-01-06", "%F").unwrap(); + let late = strptime("2000-01-01", "%F").unwrap(); + assert!(early < late); + } + + #[test] + fn test_dst() { + let _reset = set_time_zone_london_dst(); + let utc_in_feb = strptime("2015-02-01Z", "%F%z").unwrap(); + let utc_in_jun = strptime("2015-06-01Z", "%F%z").unwrap(); + let utc_in_nov = strptime("2015-11-01Z", "%F%z").unwrap(); + let local_in_feb = utc_in_feb.to_local(); + let local_in_jun = utc_in_jun.to_local(); + let local_in_nov = utc_in_nov.to_local(); + + assert_eq!(local_in_feb.tm_mon, 1); + assert_eq!(local_in_feb.tm_hour, 0); + assert_eq!(local_in_feb.tm_utcoff, 0); + assert_eq!(local_in_feb.tm_isdst, 0); + + assert_eq!(local_in_jun.tm_mon, 5); + assert_eq!(local_in_jun.tm_hour, 1); + assert_eq!(local_in_jun.tm_utcoff, 3600); + assert_eq!(local_in_jun.tm_isdst, 1); + + assert_eq!(local_in_nov.tm_mon, 10); + assert_eq!(local_in_nov.tm_hour, 0); + assert_eq!(local_in_nov.tm_utcoff, 0); + assert_eq!(local_in_nov.tm_isdst, 0) + } +} diff --git a/src/time/parse.rs b/src/time/parse.rs new file mode 100644 index 0000000000..9c302946a4 --- /dev/null +++ b/src/time/parse.rs @@ -0,0 +1,393 @@ +use super::{Timespec, Tm, at_utc, ParseError, NSEC_PER_SEC}; + +/// Parses the time from the string according to the format string. +#[allow(unused)] // re-exported by the parent `time` module +pub fn strptime(mut s: &str, format: &str) -> Result { + let mut tm = Tm { + tm_sec: 0, + tm_min: 0, + tm_hour: 0, + tm_mday: 0, + tm_mon: 0, + tm_year: 0, + tm_wday: 0, + tm_yday: 0, + tm_isdst: 0, + tm_utcoff: 0, + tm_nsec: 0, + }; + let mut chars = format.chars(); + while let Some(ch) = chars.next() { + if ch == '%' { + if let Some(ch) = chars.next() { + try!(parse_type(&mut s, ch, &mut tm)); + } + } else { + try!(parse_char(&mut s, ch)); + } + } + Ok(tm) +} + +fn parse_type(s: &mut &str, ch: char, tm: &mut Tm) -> Result<(), ParseError> { + match ch { + 'A' => match match_strs(s, &[("Sunday", 0), + ("Monday", 1), + ("Tuesday", 2), + ("Wednesday", 3), + ("Thursday", 4), + ("Friday", 5), + ("Saturday", 6)]) { + Some(v) => { tm.tm_wday = v; Ok(()) } + None => Err(ParseError::InvalidDay) + }, + 'a' => match match_strs(s, &[("Sun", 0), + ("Mon", 1), + ("Tue", 2), + ("Wed", 3), + ("Thu", 4), + ("Fri", 5), + ("Sat", 6)]) { + Some(v) => { tm.tm_wday = v; Ok(()) } + None => Err(ParseError::InvalidDay) + }, + 'B' => match match_strs(s, &[("January", 0), + ("February", 1), + ("March", 2), + ("April", 3), + ("May", 4), + ("June", 5), + ("July", 6), + ("August", 7), + ("September", 8), + ("October", 9), + ("November", 10), + ("December", 11)]) { + Some(v) => { tm.tm_mon = v; Ok(()) } + None => Err(ParseError::InvalidMonth) + }, + 'b' | 'h' => match match_strs(s, &[("Jan", 0), + ("Feb", 1), + ("Mar", 2), + ("Apr", 3), + ("May", 4), + ("Jun", 5), + ("Jul", 6), + ("Aug", 7), + ("Sep", 8), + ("Oct", 9), + ("Nov", 10), + ("Dec", 11)]) { + Some(v) => { tm.tm_mon = v; Ok(()) } + None => Err(ParseError::InvalidMonth) + }, + 'C' => match match_digits_in_range(s, 1, 2, false, 0, 99) { + Some(v) => { tm.tm_year += (v * 100) - 1900; Ok(()) } + None => Err(ParseError::InvalidYear) + }, + 'c' => { + parse_type(s, 'a', tm) + .and_then(|()| parse_char(s, ' ')) + .and_then(|()| parse_type(s, 'b', tm)) + .and_then(|()| parse_char(s, ' ')) + .and_then(|()| parse_type(s, 'e', tm)) + .and_then(|()| parse_char(s, ' ')) + .and_then(|()| parse_type(s, 'T', tm)) + .and_then(|()| parse_char(s, ' ')) + .and_then(|()| parse_type(s, 'Y', tm)) + } + 'D' | 'x' => { + parse_type(s, 'm', tm) + .and_then(|()| parse_char(s, '/')) + .and_then(|()| parse_type(s, 'd', tm)) + .and_then(|()| parse_char(s, '/')) + .and_then(|()| parse_type(s, 'y', tm)) + } + 'd' => match match_digits_in_range(s, 1, 2, false, 1, 31) { + Some(v) => { tm.tm_mday = v; Ok(()) } + None => Err(ParseError::InvalidDayOfMonth) + }, + 'e' => match match_digits_in_range(s, 1, 2, true, 1, 31) { + Some(v) => { tm.tm_mday = v; Ok(()) } + None => Err(ParseError::InvalidDayOfMonth) + }, + 'f' => { + tm.tm_nsec = match_fractional_seconds(s); + Ok(()) + } + 'F' => { + parse_type(s, 'Y', tm) + .and_then(|()| parse_char(s, '-')) + .and_then(|()| parse_type(s, 'm', tm)) + .and_then(|()| parse_char(s, '-')) + .and_then(|()| parse_type(s, 'd', tm)) + } + 'H' => { + match match_digits_in_range(s, 1, 2, false, 0, 23) { + Some(v) => { tm.tm_hour = v; Ok(()) } + None => Err(ParseError::InvalidHour) + } + } + 'I' => { + match match_digits_in_range(s, 1, 2, false, 1, 12) { + Some(v) => { tm.tm_hour = if v == 12 { 0 } else { v }; Ok(()) } + None => Err(ParseError::InvalidHour) + } + } + 'j' => { + match match_digits_in_range(s, 1, 3, false, 1, 366) { + Some(v) => { tm.tm_yday = v - 1; Ok(()) } + None => Err(ParseError::InvalidDayOfYear) + } + } + 'k' => { + match match_digits_in_range(s, 1, 2, true, 0, 23) { + Some(v) => { tm.tm_hour = v; Ok(()) } + None => Err(ParseError::InvalidHour) + } + } + 'l' => { + match match_digits_in_range(s, 1, 2, true, 1, 12) { + Some(v) => { tm.tm_hour = if v == 12 { 0 } else { v }; Ok(()) } + None => Err(ParseError::InvalidHour) + } + } + 'M' => { + match match_digits_in_range(s, 1, 2, false, 0, 59) { + Some(v) => { tm.tm_min = v; Ok(()) } + None => Err(ParseError::InvalidMinute) + } + } + 'm' => { + match match_digits_in_range(s, 1, 2, false, 1, 12) { + Some(v) => { tm.tm_mon = v - 1; Ok(()) } + None => Err(ParseError::InvalidMonth) + } + } + 'n' => parse_char(s, '\n'), + 'P' => match match_strs(s, &[("am", 0), ("pm", 12)]) { + Some(v) => { tm.tm_hour += v; Ok(()) } + None => Err(ParseError::InvalidHour) + }, + 'p' => match match_strs(s, &[("AM", 0), ("PM", 12)]) { + Some(v) => { tm.tm_hour += v; Ok(()) } + None => Err(ParseError::InvalidHour) + }, + 'R' => { + parse_type(s, 'H', tm) + .and_then(|()| parse_char(s, ':')) + .and_then(|()| parse_type(s, 'M', tm)) + } + 'r' => { + parse_type(s, 'I', tm) + .and_then(|()| parse_char(s, ':')) + .and_then(|()| parse_type(s, 'M', tm)) + .and_then(|()| parse_char(s, ':')) + .and_then(|()| parse_type(s, 'S', tm)) + .and_then(|()| parse_char(s, ' ')) + .and_then(|()| parse_type(s, 'p', tm)) + } + 's' => { + match match_digits_i64(s, 1, 18, false) { + Some(v) => { + *tm = at_utc(Timespec::new(v, 0)); + Ok(()) + }, + None => Err(ParseError::InvalidSecondsSinceEpoch) + } + } + 'S' => { + match match_digits_in_range(s, 1, 2, false, 0, 60) { + Some(v) => { tm.tm_sec = v; Ok(()) } + None => Err(ParseError::InvalidSecond) + } + } + //'s' {} + 'T' | 'X' => { + parse_type(s, 'H', tm) + .and_then(|()| parse_char(s, ':')) + .and_then(|()| parse_type(s, 'M', tm)) + .and_then(|()| parse_char(s, ':')) + .and_then(|()| parse_type(s, 'S', tm)) + } + 't' => parse_char(s, '\t'), + 'u' => { + match match_digits_in_range(s, 1, 1, false, 1, 7) { + Some(v) => { tm.tm_wday = if v == 7 { 0 } else { v }; Ok(()) } + None => Err(ParseError::InvalidDayOfWeek) + } + } + 'v' => { + parse_type(s, 'e', tm) + .and_then(|()| parse_char(s, '-')) + .and_then(|()| parse_type(s, 'b', tm)) + .and_then(|()| parse_char(s, '-')) + .and_then(|()| parse_type(s, 'Y', tm)) + } + //'W' {} + 'w' => { + match match_digits_in_range(s, 1, 1, false, 0, 6) { + Some(v) => { tm.tm_wday = v; Ok(()) } + None => Err(ParseError::InvalidDayOfWeek) + } + } + 'Y' => { + match match_digits(s, 4, 4, false) { + Some(v) => { tm.tm_year = v - 1900; Ok(()) } + None => Err(ParseError::InvalidYear) + } + } + 'y' => { + match match_digits_in_range(s, 1, 2, false, 0, 99) { + Some(v) => { tm.tm_year = v; Ok(()) } + None => Err(ParseError::InvalidYear) + } + } + 'Z' => { + if match_str(s, "UTC") || match_str(s, "GMT") { + tm.tm_utcoff = 0; + Ok(()) + } else { + // It's odd, but to maintain compatibility with c's + // strptime we ignore the timezone. + for (i, ch) in s.char_indices() { + if ch == ' ' { + *s = &s[i..]; + return Ok(()) + } + } + *s = ""; + Ok(()) + } + } + 'z' => { + if parse_char(s, 'Z').is_ok() { + tm.tm_utcoff = 0; + Ok(()) + } else { + let sign = if parse_char(s, '+').is_ok() {1} + else if parse_char(s, '-').is_ok() {-1} + else { return Err(ParseError::InvalidZoneOffset) }; + + let hours; + let minutes; + + match match_digits(s, 2, 2, false) { + Some(h) => hours = h, + None => return Err(ParseError::InvalidZoneOffset) + } + + // consume the colon if its present, + // just ignore it otherwise + let _ = parse_char(s, ':'); + + match match_digits(s, 2, 2, false) { + Some(m) => minutes = m, + None => return Err(ParseError::InvalidZoneOffset) + } + + tm.tm_utcoff = sign * (hours * 60 * 60 + minutes * 60); + Ok(()) + } + } + '%' => parse_char(s, '%'), + ch => Err(ParseError::InvalidFormatSpecifier(ch)) + } +} + + +fn match_str(s: &mut &str, needle: &str) -> bool { + if s.starts_with(needle) { + *s = &s[needle.len()..]; + true + } else { + false + } +} + +fn match_strs(ss: &mut &str, strs: &[(&str, i32)]) -> Option { + for &(needle, value) in strs.iter() { + if match_str(ss, needle) { + return Some(value) + } + } + None +} + +fn match_digits(ss: &mut &str, min_digits : usize, max_digits: usize, ws: bool) -> Option { + match match_digits_i64(ss, min_digits, max_digits, ws) { + Some(v) => Some(v as i32), + None => None + } +} + +fn match_digits_i64(ss: &mut &str, min_digits : usize, max_digits: usize, ws: bool) -> Option { + let mut value : i64 = 0; + let mut n = 0; + if ws { + let s2 = ss.trim_left_matches(" "); + n = ss.len() - s2.len(); + if n > max_digits { return None } + } + let chars = ss[n..].char_indices(); + for (_, ch) in chars.take(max_digits - n) { + match ch { + '0' ... '9' => value = value * 10 + (ch as i64 - '0' as i64), + _ => break, + } + n += 1; + } + + if n >= min_digits && n <= max_digits { + *ss = &ss[n..]; + Some(value) + } else { + None + } +} + +fn match_fractional_seconds(ss: &mut &str) -> i32 { + let mut value = 0; + let mut multiplier = NSEC_PER_SEC / 10; + + let mut chars = ss.char_indices(); + let orig = *ss; + for (i, ch) in &mut chars { + *ss = &orig[i..]; + match ch { + '0' ... '9' => { + // This will drop digits after the nanoseconds place + let digit = ch as i32 - '0' as i32; + value += digit * multiplier; + multiplier /= 10; + } + _ => break + } + } + + value +} + +fn match_digits_in_range(ss: &mut &str, + min_digits : usize, max_digits : usize, + ws: bool, min: i32, max: i32) -> Option { + let before = *ss; + match match_digits(ss, min_digits, max_digits, ws) { + Some(val) if val >= min && val <= max => Some(val), + _ => { *ss = before; None } + } +} + +fn parse_char(s: &mut &str, c: char) -> Result<(), ParseError> { + match s.char_indices().next() { + Some((i, c2)) => { + if c == c2 { + *s = &s[i + c2.len_utf8()..]; + Ok(()) + } else { + Err(ParseError::UnexpectedCharacter(c, c2)) + } + } + None => Err(ParseError::InvalidTime), + } +} diff --git a/src/time/sys.rs b/src/time/sys.rs new file mode 100644 index 0000000000..fdf2319db7 --- /dev/null +++ b/src/time/sys.rs @@ -0,0 +1,973 @@ +#![allow(bad_style)] + +pub use self::inner::*; + +#[cfg(any( + all(target_arch = "wasm32", not(target_os = "emscripten")), + target_os = "redox", +))] +mod common { + use crate::time::Tm; + + pub fn time_to_tm(ts: i64, tm: &mut Tm) { + let leapyear = |year| -> bool { + year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) + }; + + static _ytab: [[i64; 12]; 2] = [ + [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ], + [ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ] + ]; + + let mut year = 1970; + + let dayclock = ts % 86400; + let mut dayno = ts / 86400; + + tm.tm_sec = (dayclock % 60) as i32; + tm.tm_min = ((dayclock % 3600) / 60) as i32; + tm.tm_hour = (dayclock / 3600) as i32; + tm.tm_wday = ((dayno + 4) % 7) as i32; + loop { + let yearsize = if leapyear(year) { + 366 + } else { + 365 + }; + if dayno >= yearsize { + dayno -= yearsize; + year += 1; + } else { + break; + } + } + tm.tm_year = (year - 1900) as i32; + tm.tm_yday = dayno as i32; + let mut mon = 0; + while dayno >= _ytab[if leapyear(year) { 1 } else { 0 }][mon] { + dayno -= _ytab[if leapyear(year) { 1 } else { 0 }][mon]; + mon += 1; + } + tm.tm_mon = mon as i32; + tm.tm_mday = dayno as i32 + 1; + tm.tm_isdst = 0; + } + + pub fn tm_to_time(tm: &Tm) -> i64 { + let mut y = tm.tm_year as i64 + 1900; + let mut m = tm.tm_mon as i64 + 1; + if m <= 2 { + y -= 1; + m += 12; + } + let d = tm.tm_mday as i64; + let h = tm.tm_hour as i64; + let mi = tm.tm_min as i64; + let s = tm.tm_sec as i64; + (365*y + y/4 - y/100 + y/400 + 3*(m+1)/5 + 30*m + d - 719561) + * 86400 + 3600 * h + 60 * mi + s + } +} + +#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] +mod inner { + use std::ops::{Add, Sub}; + use crate::time::Tm; + use Duration; + use super::common::{time_to_tm, tm_to_time}; + + #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] + pub struct SteadyTime; + + pub fn time_to_utc_tm(sec: i64, tm: &mut Tm) { + time_to_tm(sec, tm); + } + + pub fn time_to_local_tm(sec: i64, tm: &mut Tm) { + // FIXME: Add timezone logic + time_to_tm(sec, tm); + } + + pub fn utc_tm_to_time(tm: &Tm) -> i64 { + tm_to_time(tm) + } + + pub fn local_tm_to_time(tm: &Tm) -> i64 { + // FIXME: Add timezone logic + tm_to_time(tm) + } + + pub fn get_time() -> (i64, i32) { + unimplemented!() + } + + pub fn get_precise_ns() -> u64 { + unimplemented!() + } + + impl SteadyTime { + pub fn now() -> SteadyTime { + unimplemented!() + } + } + + impl Sub for SteadyTime { + type Output = Duration; + fn sub(self, _other: SteadyTime) -> Duration { + unimplemented!() + } + } + + impl Sub for SteadyTime { + type Output = SteadyTime; + fn sub(self, _other: Duration) -> SteadyTime { + unimplemented!() + } + } + + impl Add for SteadyTime { + type Output = SteadyTime; + fn add(self, _other: Duration) -> SteadyTime { + unimplemented!() + } + } +} + +#[cfg(target_os = "redox")] +mod inner { + use std::fmt; + use std::cmp::Ordering; + use std::ops::{Add, Sub}; + use syscall; + use super::common::{time_to_tm, tm_to_time}; + use crate::time::NSEC_PER_SEC; + + use Duration; + use Tm; + + pub fn time_to_utc_tm(sec: i64, tm: &mut Tm) { + time_to_tm(sec, tm); + } + + pub fn time_to_local_tm(sec: i64, tm: &mut Tm) { + // FIXME: Add timezone logic + time_to_tm(sec, tm); + } + + pub fn utc_tm_to_time(tm: &Tm) -> i64 { + tm_to_time(tm) + } + + pub fn local_tm_to_time(tm: &Tm) -> i64 { + // FIXME: Add timezone logic + tm_to_time(tm) + } + + pub fn get_time() -> (i64, i32) { + let mut tv = syscall::TimeSpec { tv_sec: 0, tv_nsec: 0 }; + syscall::clock_gettime(syscall::CLOCK_REALTIME, &mut tv).unwrap(); + (tv.tv_sec as i64, tv.tv_nsec as i32) + } + + pub fn get_precise_ns() -> u64 { + let mut ts = syscall::TimeSpec { tv_sec: 0, tv_nsec: 0 }; + syscall::clock_gettime(syscall::CLOCK_MONOTONIC, &mut ts).unwrap(); + (ts.tv_sec as u64) * 1000000000 + (ts.tv_nsec as u64) + } + + #[derive(Copy)] + pub struct SteadyTime { + t: syscall::TimeSpec, + } + + impl fmt::Debug for SteadyTime { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "SteadyTime {{ tv_sec: {:?}, tv_nsec: {:?} }}", + self.t.tv_sec, self.t.tv_nsec) + } + } + + impl Clone for SteadyTime { + fn clone(&self) -> SteadyTime { + SteadyTime { t: self.t } + } + } + + impl SteadyTime { + pub fn now() -> SteadyTime { + let mut t = SteadyTime { + t: syscall::TimeSpec { + tv_sec: 0, + tv_nsec: 0, + } + }; + syscall::clock_gettime(syscall::CLOCK_MONOTONIC, &mut t.t).unwrap(); + t + } + } + + impl Sub for SteadyTime { + type Output = Duration; + fn sub(self, other: SteadyTime) -> Duration { + if self.t.tv_nsec >= other.t.tv_nsec { + Duration::seconds(self.t.tv_sec as i64 - other.t.tv_sec as i64) + + Duration::nanoseconds(self.t.tv_nsec as i64 - other.t.tv_nsec as i64) + } else { + Duration::seconds(self.t.tv_sec as i64 - 1 - other.t.tv_sec as i64) + + Duration::nanoseconds(self.t.tv_nsec as i64 + NSEC_PER_SEC as i64 - + other.t.tv_nsec as i64) + } + } + } + + impl Sub for SteadyTime { + type Output = SteadyTime; + fn sub(self, other: Duration) -> SteadyTime { + self + -other + } + } + + impl Add for SteadyTime { + type Output = SteadyTime; + fn add(mut self, other: Duration) -> SteadyTime { + let seconds = other.num_seconds(); + let nanoseconds = other - Duration::seconds(seconds); + let nanoseconds = nanoseconds.num_nanoseconds().unwrap(); + self.t.tv_sec += seconds; + self.t.tv_nsec += nanoseconds as i32; + if self.t.tv_nsec >= NSEC_PER_SEC { + self.t.tv_nsec -= NSEC_PER_SEC; + self.t.tv_sec += 1; + } else if self.t.tv_nsec < 0 { + self.t.tv_sec -= 1; + self.t.tv_nsec += NSEC_PER_SEC; + } + self + } + } + + impl PartialOrd for SteadyTime { + fn partial_cmp(&self, other: &SteadyTime) -> Option { + Some(self.cmp(other)) + } + } + + impl Ord for SteadyTime { + fn cmp(&self, other: &SteadyTime) -> Ordering { + match self.t.tv_sec.cmp(&other.t.tv_sec) { + Ordering::Equal => self.t.tv_nsec.cmp(&other.t.tv_nsec), + ord => ord + } + } + } + + impl PartialEq for SteadyTime { + fn eq(&self, other: &SteadyTime) -> bool { + self.t.tv_sec == other.t.tv_sec && + self.t.tv_nsec == other.t.tv_nsec + } + } + + impl Eq for SteadyTime {} +} + +#[cfg(unix)] +mod inner { + use libc::{self, time_t}; + use std::mem; + use std::io; + use crate::time::Tm; + + #[cfg(any(target_os = "macos", target_os = "ios"))] + pub use self::mac::*; + #[cfg(all(not(target_os = "macos"), not(target_os = "ios")))] + pub use self::unix::*; + + #[cfg(target_os = "solaris")] + extern { + static timezone: time_t; + static altzone: time_t; + } + + fn rust_tm_to_tm(rust_tm: &Tm, tm: &mut libc::tm) { + tm.tm_sec = rust_tm.tm_sec; + tm.tm_min = rust_tm.tm_min; + tm.tm_hour = rust_tm.tm_hour; + tm.tm_mday = rust_tm.tm_mday; + tm.tm_mon = rust_tm.tm_mon; + tm.tm_year = rust_tm.tm_year; + tm.tm_wday = rust_tm.tm_wday; + tm.tm_yday = rust_tm.tm_yday; + tm.tm_isdst = rust_tm.tm_isdst; + } + + fn tm_to_rust_tm(tm: &libc::tm, utcoff: i32, rust_tm: &mut Tm) { + rust_tm.tm_sec = tm.tm_sec; + rust_tm.tm_min = tm.tm_min; + rust_tm.tm_hour = tm.tm_hour; + rust_tm.tm_mday = tm.tm_mday; + rust_tm.tm_mon = tm.tm_mon; + rust_tm.tm_year = tm.tm_year; + rust_tm.tm_wday = tm.tm_wday; + rust_tm.tm_yday = tm.tm_yday; + rust_tm.tm_isdst = tm.tm_isdst; + rust_tm.tm_utcoff = utcoff; + } + + #[cfg(any(target_os = "nacl", target_os = "solaris"))] + unsafe fn timegm(tm: *mut libc::tm) -> time_t { + use std::env::{set_var, var_os, remove_var}; + extern { + fn tzset(); + } + + let ret; + + let current_tz = var_os("TZ"); + set_var("TZ", "UTC"); + tzset(); + + ret = libc::mktime(tm); + + if let Some(tz) = current_tz { + set_var("TZ", tz); + } else { + remove_var("TZ"); + } + tzset(); + + ret + } + + pub fn time_to_utc_tm(sec: i64, tm: &mut Tm) { + unsafe { + let sec = sec as time_t; + let mut out = mem::zeroed(); + if libc::gmtime_r(&sec, &mut out).is_null() { + panic!("gmtime_r failed: {}", io::Error::last_os_error()); + } + tm_to_rust_tm(&out, 0, tm); + } + } + + pub fn time_to_local_tm(sec: i64, tm: &mut Tm) { + unsafe { + let sec = sec as time_t; + let mut out = mem::zeroed(); + if libc::localtime_r(&sec, &mut out).is_null() { + panic!("localtime_r failed: {}", io::Error::last_os_error()); + } + #[cfg(target_os = "solaris")] + let gmtoff = { + ::tzset(); + // < 0 means we don't know; assume we're not in DST. + if out.tm_isdst == 0 { + // timezone is seconds west of UTC, tm_gmtoff is seconds east + -timezone + } else if out.tm_isdst > 0 { + -altzone + } else { + -timezone + } + }; + #[cfg(not(target_os = "solaris"))] + let gmtoff = out.tm_gmtoff; + tm_to_rust_tm(&out, gmtoff as i32, tm); + } + } + + pub fn utc_tm_to_time(rust_tm: &Tm) -> i64 { + #[cfg(all(target_os = "android", target_pointer_width = "32"))] + use libc::timegm64 as timegm; + #[cfg(not(any(all(target_os = "android", target_pointer_width = "32"), target_os = "nacl", target_os = "solaris")))] + use libc::timegm; + + let mut tm = unsafe { mem::zeroed() }; + rust_tm_to_tm(rust_tm, &mut tm); + unsafe { timegm(&mut tm) as i64 } + } + + pub fn local_tm_to_time(rust_tm: &Tm) -> i64 { + let mut tm = unsafe { mem::zeroed() }; + rust_tm_to_tm(rust_tm, &mut tm); + unsafe { libc::mktime(&mut tm) as i64 } + } + + #[cfg(any(target_os = "macos", target_os = "ios"))] + mod mac { + use libc::{self, timeval, mach_timebase_info}; + use std::sync::{Once, ONCE_INIT}; + use std::ops::{Add, Sub}; + use Duration; + + fn info() -> &'static mach_timebase_info { + static mut INFO: mach_timebase_info = mach_timebase_info { + numer: 0, + denom: 0, + }; + static ONCE: Once = ONCE_INIT; + + unsafe { + ONCE.call_once(|| { + mach_timebase_info(&mut INFO); + }); + &INFO + } + } + + pub fn get_time() -> (i64, i32) { + use std::ptr; + let mut tv = timeval { tv_sec: 0, tv_usec: 0 }; + unsafe { libc::gettimeofday(&mut tv, ptr::null_mut()); } + (tv.tv_sec as i64, tv.tv_usec * 1000) + } + + #[inline] + pub fn get_precise_ns() -> u64 { + unsafe { + let time = libc::mach_absolute_time(); + let info = info(); + time * info.numer as u64 / info.denom as u64 + } + } + + #[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug)] + pub struct SteadyTime { t: u64 } + + impl SteadyTime { + pub fn now() -> SteadyTime { + SteadyTime { t: get_precise_ns() } + } + } + impl Sub for SteadyTime { + type Output = Duration; + fn sub(self, other: SteadyTime) -> Duration { + Duration::nanoseconds(self.t as i64 - other.t as i64) + } + } + impl Sub for SteadyTime { + type Output = SteadyTime; + fn sub(self, other: Duration) -> SteadyTime { + self + -other + } + } + impl Add for SteadyTime { + type Output = SteadyTime; + fn add(self, other: Duration) -> SteadyTime { + let delta = other.num_nanoseconds().unwrap(); + SteadyTime { + t: (self.t as i64 + delta) as u64 + } + } + } + } + + #[cfg(test)] + pub struct TzReset; + + #[cfg(test)] + pub fn set_los_angeles_time_zone() -> TzReset { + use std::env; + env::set_var("TZ", "America/Los_Angeles"); + crate::time::tzset(); + TzReset + } + + #[cfg(test)] + pub fn set_london_with_dst_time_zone() -> TzReset { + use std::env; + env::set_var("TZ", "Europe/London"); + crate::time::tzset(); + TzReset + } + + #[cfg(all(not(target_os = "macos"), not(target_os = "ios")))] + mod unix { + use std::fmt; + use std::cmp::Ordering; + use std::ops::{Add, Sub}; + use libc; + use crate::time::NSEC_PER_SEC; + + use Duration; + + pub fn get_time() -> (i64, i32) { + let mut tv = libc::timespec { tv_sec: 0, tv_nsec: 0 }; + unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, &mut tv); } + (tv.tv_sec as i64, tv.tv_nsec as i32) + } + + pub fn get_precise_ns() -> u64 { + let mut ts = libc::timespec { tv_sec: 0, tv_nsec: 0 }; + unsafe { + libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts); + } + (ts.tv_sec as u64) * 1000000000 + (ts.tv_nsec as u64) + } + + #[derive(Copy)] + pub struct SteadyTime { + t: libc::timespec, + } + + impl fmt::Debug for SteadyTime { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "SteadyTime {{ tv_sec: {:?}, tv_nsec: {:?} }}", + self.t.tv_sec, self.t.tv_nsec) + } + } + + impl Clone for SteadyTime { + fn clone(&self) -> SteadyTime { + SteadyTime { t: self.t } + } + } + + impl SteadyTime { + pub fn now() -> SteadyTime { + let mut t = SteadyTime { + t: libc::timespec { + tv_sec: 0, + tv_nsec: 0, + } + }; + unsafe { + assert_eq!(0, libc::clock_gettime(libc::CLOCK_MONOTONIC, + &mut t.t)); + } + t + } + } + + impl Sub for SteadyTime { + type Output = Duration; + fn sub(self, other: SteadyTime) -> Duration { + if self.t.tv_nsec >= other.t.tv_nsec { + Duration::seconds(self.t.tv_sec as i64 - other.t.tv_sec as i64) + + Duration::nanoseconds(self.t.tv_nsec as i64 - other.t.tv_nsec as i64) + } else { + Duration::seconds(self.t.tv_sec as i64 - 1 - other.t.tv_sec as i64) + + Duration::nanoseconds(self.t.tv_nsec as i64 + NSEC_PER_SEC as i64 - + other.t.tv_nsec as i64) + } + } + } + + impl Sub for SteadyTime { + type Output = SteadyTime; + fn sub(self, other: Duration) -> SteadyTime { + self + -other + } + } + + impl Add for SteadyTime { + type Output = SteadyTime; + fn add(mut self, other: Duration) -> SteadyTime { + let seconds = other.num_seconds(); + let nanoseconds = other - Duration::seconds(seconds); + let nanoseconds = nanoseconds.num_nanoseconds().unwrap(); + + #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))] + type nsec = i64; + #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))] + type nsec = libc::c_long; + + self.t.tv_sec += seconds as libc::time_t; + self.t.tv_nsec += nanoseconds as nsec; + if self.t.tv_nsec >= NSEC_PER_SEC as nsec { + self.t.tv_nsec -= NSEC_PER_SEC as nsec; + self.t.tv_sec += 1; + } else if self.t.tv_nsec < 0 { + self.t.tv_sec -= 1; + self.t.tv_nsec += NSEC_PER_SEC as nsec; + } + self + } + } + + impl PartialOrd for SteadyTime { + fn partial_cmp(&self, other: &SteadyTime) -> Option { + Some(self.cmp(other)) + } + } + + impl Ord for SteadyTime { + fn cmp(&self, other: &SteadyTime) -> Ordering { + match self.t.tv_sec.cmp(&other.t.tv_sec) { + Ordering::Equal => self.t.tv_nsec.cmp(&other.t.tv_nsec), + ord => ord + } + } + } + + impl PartialEq for SteadyTime { + fn eq(&self, other: &SteadyTime) -> bool { + self.t.tv_sec == other.t.tv_sec && + self.t.tv_nsec == other.t.tv_nsec + } + } + + impl Eq for SteadyTime {} + + } +} + +#[cfg(windows)] +#[allow(non_snake_case)] +mod inner { + use std::io; + use std::mem; + use std::sync::{Once, ONCE_INIT}; + use std::ops::{Add, Sub}; + use {Tm, Duration}; + + use winapi::um::winnt::*; + use winapi::shared::minwindef::*; + use winapi::um::minwinbase::SYSTEMTIME; + use winapi::um::profileapi::*; + use winapi::um::timezoneapi::*; + use winapi::um::sysinfoapi::GetSystemTimeAsFileTime; + + fn frequency() -> i64 { + static mut FREQUENCY: i64 = 0; + static ONCE: Once = ONCE_INIT; + + unsafe { + ONCE.call_once(|| { + let mut l = i64_to_large_integer(0); + QueryPerformanceFrequency(&mut l); + FREQUENCY = large_integer_to_i64(l); + }); + FREQUENCY + } + } + + fn i64_to_large_integer(i: i64) -> LARGE_INTEGER { + unsafe { + let mut large_integer: LARGE_INTEGER = mem::zeroed(); + *large_integer.QuadPart_mut() = i; + large_integer + } + } + + fn large_integer_to_i64(l: LARGE_INTEGER) -> i64 { + unsafe { + *l.QuadPart() + } + } + + const HECTONANOSECS_IN_SEC: i64 = 10_000_000; + const HECTONANOSEC_TO_UNIX_EPOCH: i64 = 11_644_473_600 * HECTONANOSECS_IN_SEC; + + fn time_to_file_time(sec: i64) -> FILETIME { + let t = (((sec * HECTONANOSECS_IN_SEC) + HECTONANOSEC_TO_UNIX_EPOCH)) as u64; + FILETIME { + dwLowDateTime: t as DWORD, + dwHighDateTime: (t >> 32) as DWORD + } + } + + fn file_time_as_u64(ft: &FILETIME) -> u64 { + ((ft.dwHighDateTime as u64) << 32) | (ft.dwLowDateTime as u64) + } + + fn file_time_to_nsec(ft: &FILETIME) -> i32 { + let t = file_time_as_u64(ft) as i64; + ((t % HECTONANOSECS_IN_SEC) * 100) as i32 + } + + fn file_time_to_unix_seconds(ft: &FILETIME) -> i64 { + let t = file_time_as_u64(ft) as i64; + ((t - HECTONANOSEC_TO_UNIX_EPOCH) / HECTONANOSECS_IN_SEC) as i64 + } + + fn system_time_to_file_time(sys: &SYSTEMTIME) -> FILETIME { + unsafe { + let mut ft = mem::zeroed(); + SystemTimeToFileTime(sys, &mut ft); + ft + } + } + + fn tm_to_system_time(tm: &Tm) -> SYSTEMTIME { + let mut sys: SYSTEMTIME = unsafe { mem::zeroed() }; + sys.wSecond = tm.tm_sec as WORD; + sys.wMinute = tm.tm_min as WORD; + sys.wHour = tm.tm_hour as WORD; + sys.wDay = tm.tm_mday as WORD; + sys.wDayOfWeek = tm.tm_wday as WORD; + sys.wMonth = (tm.tm_mon + 1) as WORD; + sys.wYear = (tm.tm_year + 1900) as WORD; + sys + } + + fn system_time_to_tm(sys: &SYSTEMTIME, tm: &mut Tm) { + tm.tm_sec = sys.wSecond as i32; + tm.tm_min = sys.wMinute as i32; + tm.tm_hour = sys.wHour as i32; + tm.tm_mday = sys.wDay as i32; + tm.tm_wday = sys.wDayOfWeek as i32; + tm.tm_mon = (sys.wMonth - 1) as i32; + tm.tm_year = (sys.wYear - 1900) as i32; + tm.tm_yday = yday(tm.tm_year, tm.tm_mon + 1, tm.tm_mday); + + fn yday(year: i32, month: i32, day: i32) -> i32 { + let leap = if month > 2 { + if year % 4 == 0 { 1 } else { 2 } + } else { + 0 + }; + let july = if month > 7 { 1 } else { 0 }; + + (month - 1) * 30 + month / 2 + (day - 1) - leap + july + } + } + + macro_rules! call { + ($name:ident($($arg:expr),*)) => { + if $name($($arg),*) == 0 { + panic!(concat!(stringify!($name), " failed with: {}"), + io::Error::last_os_error()); + } + } + } + + pub fn time_to_utc_tm(sec: i64, tm: &mut Tm) { + let mut out = unsafe { mem::zeroed() }; + let ft = time_to_file_time(sec); + unsafe { + call!(FileTimeToSystemTime(&ft, &mut out)); + } + system_time_to_tm(&out, tm); + tm.tm_utcoff = 0; + } + + pub fn time_to_local_tm(sec: i64, tm: &mut Tm) { + let ft = time_to_file_time(sec); + unsafe { + let mut utc = mem::zeroed(); + let mut local = mem::zeroed(); + call!(FileTimeToSystemTime(&ft, &mut utc)); + call!(SystemTimeToTzSpecificLocalTime(0 as *const _, + &mut utc, &mut local)); + system_time_to_tm(&local, tm); + + let local = system_time_to_file_time(&local); + let local_sec = file_time_to_unix_seconds(&local); + + let mut tz = mem::zeroed(); + GetTimeZoneInformation(&mut tz); + + // SystemTimeToTzSpecificLocalTime already applied the biases so + // check if it non standard + tm.tm_utcoff = (local_sec - sec) as i32; + tm.tm_isdst = if tm.tm_utcoff == -60 * (tz.Bias + tz.StandardBias) { + 0 + } else { + 1 + }; + } + } + + pub fn utc_tm_to_time(tm: &Tm) -> i64 { + unsafe { + let mut ft = mem::zeroed(); + let sys_time = tm_to_system_time(tm); + call!(SystemTimeToFileTime(&sys_time, &mut ft)); + file_time_to_unix_seconds(&ft) + } + } + + pub fn local_tm_to_time(tm: &Tm) -> i64 { + unsafe { + let mut ft = mem::zeroed(); + let mut utc = mem::zeroed(); + let mut sys_time = tm_to_system_time(tm); + call!(TzSpecificLocalTimeToSystemTime(0 as *mut _, + &mut sys_time, &mut utc)); + call!(SystemTimeToFileTime(&utc, &mut ft)); + file_time_to_unix_seconds(&ft) + } + } + + pub fn get_time() -> (i64, i32) { + unsafe { + let mut ft = mem::zeroed(); + GetSystemTimeAsFileTime(&mut ft); + (file_time_to_unix_seconds(&ft), file_time_to_nsec(&ft)) + } + } + + pub fn get_precise_ns() -> u64 { + let mut ticks = i64_to_large_integer(0); + unsafe { + assert!(QueryPerformanceCounter(&mut ticks) == 1); + } + mul_div_i64(large_integer_to_i64(ticks), 1000000000, frequency()) as u64 + + } + + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] + pub struct SteadyTime { + t: i64, + } + + impl SteadyTime { + pub fn now() -> SteadyTime { + let mut l = i64_to_large_integer(0); + unsafe { QueryPerformanceCounter(&mut l); } + SteadyTime { t : large_integer_to_i64(l) } + } + } + + impl Sub for SteadyTime { + type Output = Duration; + fn sub(self, other: SteadyTime) -> Duration { + let diff = self.t as i64 - other.t as i64; + Duration::nanoseconds(mul_div_i64(diff, 1000000000, + frequency())) + } + } + + impl Sub for SteadyTime { + type Output = SteadyTime; + fn sub(self, other: Duration) -> SteadyTime { + self + -other + } + } + + impl Add for SteadyTime { + type Output = SteadyTime; + fn add(mut self, other: Duration) -> SteadyTime { + self.t += (other.num_microseconds().unwrap() * frequency() / + 1_000_000) as i64; + self + } + } + + #[cfg(test)] + pub struct TzReset { + old: TIME_ZONE_INFORMATION, + } + + #[cfg(test)] + impl Drop for TzReset { + fn drop(&mut self) { + unsafe { + call!(SetTimeZoneInformation(&self.old)); + } + } + } + + #[cfg(test)] + pub fn set_los_angeles_time_zone() -> TzReset { + acquire_privileges(); + + unsafe { + let mut tz = mem::zeroed::(); + GetTimeZoneInformation(&mut tz); + let ret = TzReset { old: tz }; + tz.Bias = 60 * 8; + call!(SetTimeZoneInformation(&tz)); + return ret + } + } + + #[cfg(test)] + pub fn set_london_with_dst_time_zone() -> TzReset { + acquire_privileges(); + + unsafe { + let mut tz = mem::zeroed::(); + GetTimeZoneInformation(&mut tz); + let ret = TzReset { old: tz }; + // Since date set precisely this is 2015's dates + tz.Bias = 0; + tz.DaylightBias = -60; + tz.DaylightDate.wYear = 0; + tz.DaylightDate.wMonth = 3; + tz.DaylightDate.wDayOfWeek = 0; + tz.DaylightDate.wDay = 5; + tz.DaylightDate.wHour = 2; + tz.StandardBias = 0; + tz.StandardDate.wYear = 0; + tz.StandardDate.wMonth = 10; + tz.StandardDate.wDayOfWeek = 0; + tz.StandardDate.wDay = 5; + tz.StandardDate.wHour = 2; + call!(SetTimeZoneInformation(&tz)); + return ret + } + } + + // Ensures that this process has the necessary privileges to set a new time + // zone, and this is all transcribed from: + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724944%28v=vs.85%29.aspx + #[cfg(test)] + fn acquire_privileges() { + use std::sync::{ONCE_INIT, Once}; + use winapi::um::processthreadsapi::*; + use winapi::um::winbase::LookupPrivilegeValueA; + const SE_PRIVILEGE_ENABLED: DWORD = 2; + static INIT: Once = ONCE_INIT; + + // TODO: FIXME + extern "system" { + fn AdjustTokenPrivileges( + TokenHandle: HANDLE, DisableAllPrivileges: BOOL, NewState: PTOKEN_PRIVILEGES, + BufferLength: DWORD, PreviousState: PTOKEN_PRIVILEGES, ReturnLength: PDWORD, + ) -> BOOL; + } + + #[repr(C)] + struct TKP { + tkp: TOKEN_PRIVILEGES, + laa: LUID_AND_ATTRIBUTES, + } + + INIT.call_once(|| unsafe { + let mut hToken = 0 as *mut _; + call!(OpenProcessToken(GetCurrentProcess(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, + &mut hToken)); + + let mut tkp = mem::zeroed::(); + assert_eq!(tkp.tkp.Privileges.len(), 0); + let c = ::std::ffi::CString::new("SeTimeZonePrivilege").unwrap(); + call!(LookupPrivilegeValueA(0 as *const _, c.as_ptr(), + &mut tkp.laa.Luid)); + tkp.tkp.PrivilegeCount = 1; + tkp.laa.Attributes = SE_PRIVILEGE_ENABLED; + call!(AdjustTokenPrivileges(hToken, FALSE, &mut tkp.tkp, 0, + 0 as *mut _, 0 as *mut _)); + }); + } + + + + // Computes (value*numer)/denom without overflow, as long as both + // (numer*denom) and the overall result fit into i64 (which is the case + // for our time conversions). + fn mul_div_i64(value: i64, numer: i64, denom: i64) -> i64 { + let q = value / denom; + let r = value % denom; + // Decompose value as (value/denom*denom + value%denom), + // substitute into (value*numer)/denom and simplify. + // r < denom, so (denom*numer) is the upper bound of (r*numer) + q * numer + r * numer / denom + } + + #[test] + fn test_muldiv() { + assert_eq!(mul_div_i64( 1_000_000_000_001, 1_000_000_000, 1_000_000), + 1_000_000_000_001_000); + assert_eq!(mul_div_i64(-1_000_000_000_001, 1_000_000_000, 1_000_000), + -1_000_000_000_001_000); + assert_eq!(mul_div_i64(-1_000_000_000_001,-1_000_000_000, 1_000_000), + 1_000_000_000_001_000); + assert_eq!(mul_div_i64( 1_000_000_000_001, 1_000_000_000,-1_000_000), + -1_000_000_000_001_000); + assert_eq!(mul_div_i64( 1_000_000_000_001,-1_000_000_000,-1_000_000), + 1_000_000_000_001_000); + } +} From b43d6417a7b84b01132718ab8c600f96196b91ba Mon Sep 17 00:00:00 2001 From: Joey Ezechiels Date: Fri, 2 Nov 2018 20:35:49 +0100 Subject: [PATCH 2/6] Remove double `extern crate` statement --- src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4889530d8e..b2b8ee276c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -412,8 +412,6 @@ extern crate syscall; extern crate libc; #[cfg(all(feature="clock", windows))] extern crate winapi; -#[cfg(feature = "rustc-serialize")] -extern crate rustc_serialize; #[cfg(test)] #[macro_use] extern crate log; From ab3de5c0799e8612b1f3e2af81ee8ab66eb11dde Mon Sep 17 00:00:00 2001 From: Joey Ezechiels Date: Fri, 2 Nov 2018 20:45:54 +0100 Subject: [PATCH 3/6] Fix some errant doc tests in README.md The little buggers snuck through. --- README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8a37c7b775..f4587459cb 100644 --- a/README.md +++ b/README.md @@ -73,8 +73,8 @@ use chrono::prelude::*; ### Duration Chrono currently uses -the [`time::Duration`](https://docs.rs/time/0.1.40/time/struct.Duration.html) type -from the `time` crate to represent the magnitude of a time span. +the [`Duration`](https://docs.rs/chrono/struct.Duration.html) type +in the `time` module to represent the magnitude of a time span. Since this has the same name to the newer, standard type for duration, the reference will refer this type as `OldDuration`. Note that this is an "accurate" duration represented as seconds and @@ -98,9 +98,9 @@ type to represent a date and a time in a timezone. For more abstract moment-in-time tracking such as internal timekeeping that is unconcerned with timezones, consider -[`time::SystemTime`](https://doc.rust-lang.org/std/time/struct.SystemTime.html), +[`SystemTime`](https://doc.rust-lang.org/std/time/struct.SystemTime.html), which tracks your system clock, or -[`time::Instant`](https://doc.rust-lang.org/std/time/struct.Instant.html), which +[`Instant`](https://doc.rust-lang.org/std/time/struct.Instant.html), which is an opaque but monotonically-increasing representation of a moment in time. `DateTime` is timezone-aware and must be constructed from @@ -173,7 +173,7 @@ The following illustrates most supported operations to the date and time: ```rust use chrono::prelude::*; -use time::Duration; +use chrono::Duration; // assume this returned `2014-11-28T21:45:59.324310806+09:00`: let dt = Local::now(); @@ -388,4 +388,3 @@ and consequently `Utc.ymd(2014, 1, 30).with_month(2)` returns `None`. Advanced time zone handling is not yet supported. For now you can try the [Chrono-tz](https://github.com/chronotope/chrono-tz/) crate instead. - From ab60b40b37b5e98143dfae47861fc10853c6f1ea Mon Sep 17 00:00:00 2001 From: Joey Ezechiels Date: Fri, 2 Nov 2018 23:36:35 +0100 Subject: [PATCH 4/6] Fix CI test issues --- src/lib.rs | 14 +- src/oldtime.rs | 640 ------------------------------------------------ src/time/mod.rs | 5 +- src/time/sys.rs | 14 +- 4 files changed, 14 insertions(+), 659 deletions(-) delete mode 100644 src/oldtime.rs diff --git a/src/lib.rs b/src/lib.rs index b2b8ee276c..c5a1dd2327 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -408,7 +408,7 @@ // These are required by the `time` module: #[cfg(all(feature="clock", target_os = "redox"))] extern crate syscall; -#[cfg(all(feature="clock", unix))] +#[cfg(unix)] extern crate libc; #[cfg(all(feature="clock", windows))] extern crate winapi; @@ -423,16 +423,12 @@ extern crate rustc_serialize; #[cfg(feature = "serde")] extern crate serde as serdelib; -#[cfg(feature="clock")] -mod time; -#[cfg(feature="clock")] -use time as oldtime; -#[cfg(feature="clock")] -pub use time::{PreciseTime, SteadyTime}; +pub mod time; +pub use time::{Duration, PreciseTime, SteadyTime}; +pub use time as oldtime; // this reexport is to aid the transition and should not be in the prelude! -pub use oldtime::Duration; #[cfg(feature="clock")] #[doc(no_inline)] pub use offset::Local; @@ -464,8 +460,6 @@ macro_rules! try_opt { } mod div; -#[cfg(not(feature="clock"))] -mod oldtime; pub mod offset; pub mod naive { //! Date and time types which do not concern about the timezones. diff --git a/src/oldtime.rs b/src/oldtime.rs deleted file mode 100644 index 2bdc2f64ef..0000000000 --- a/src/oldtime.rs +++ /dev/null @@ -1,640 +0,0 @@ -// 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 - -use std::{fmt, i64}; -use std::error::Error; -use std::ops::{Add, Sub, Mul, Div, Neg}; -use std::time::Duration as StdDuration; - -/// The number of nanoseconds in a microsecond. -const NANOS_PER_MICRO: i32 = 1000; -/// The number of nanoseconds in a millisecond. -const NANOS_PER_MILLI: i32 = 1000_000; -/// The number of nanoseconds in seconds. -const NANOS_PER_SEC: i32 = 1_000_000_000; -/// The number of microseconds per second. -const MICROS_PER_SEC: i64 = 1000_000; -/// The number of milliseconds per second. -const MILLIS_PER_SEC: i64 = 1000; -/// The number of seconds in a minute. -const SECS_PER_MINUTE: i64 = 60; -/// The number of seconds in an hour. -const SECS_PER_HOUR: i64 = 3600; -/// The number of (non-leap) seconds in days. -const SECS_PER_DAY: i64 = 86400; -/// The number of (non-leap) seconds in a week. -const SECS_PER_WEEK: i64 = 604800; - -macro_rules! try_opt { - ($e:expr) => (match $e { Some(v) => v, None => return None }) -} - - -/// ISO 8601 time duration with nanosecond precision. -/// This also allows for the negative duration; see individual methods for details. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub struct Duration { - secs: i64, - nanos: i32, // Always 0 <= nanos < NANOS_PER_SEC -} - -/// The minimum possible `Duration`: `i64::MIN` milliseconds. -pub const MIN: Duration = Duration { - secs: i64::MIN / MILLIS_PER_SEC - 1, - nanos: NANOS_PER_SEC + (i64::MIN % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI -}; - -/// The maximum possible `Duration`: `i64::MAX` milliseconds. -pub const MAX: Duration = Duration { - secs: i64::MAX / MILLIS_PER_SEC, - nanos: (i64::MAX % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI -}; - -impl Duration { - /// Makes a new `Duration` with given number of weeks. - /// Equivalent to `Duration::seconds(weeks * 7 * 24 * 60 * 60)` with overflow checks. - /// Panics when the duration is out of bounds. - #[inline] - pub fn weeks(weeks: i64) -> Duration { - let secs = weeks.checked_mul(SECS_PER_WEEK).expect("Duration::weeks out of bounds"); - Duration::seconds(secs) - } - - /// Makes a new `Duration` with given number of days. - /// Equivalent to `Duration::seconds(days * 24 * 60 * 60)` with overflow checks. - /// Panics when the duration is out of bounds. - #[inline] - pub fn days(days: i64) -> Duration { - let secs = days.checked_mul(SECS_PER_DAY).expect("Duration::days out of bounds"); - Duration::seconds(secs) - } - - /// Makes a new `Duration` with given number of hours. - /// Equivalent to `Duration::seconds(hours * 60 * 60)` with overflow checks. - /// Panics when the duration is out of bounds. - #[inline] - pub fn hours(hours: i64) -> Duration { - let secs = hours.checked_mul(SECS_PER_HOUR).expect("Duration::hours ouf of bounds"); - Duration::seconds(secs) - } - - /// Makes a new `Duration` with given number of minutes. - /// Equivalent to `Duration::seconds(minutes * 60)` with overflow checks. - /// Panics when the duration is out of bounds. - #[inline] - pub fn minutes(minutes: i64) -> Duration { - let secs = minutes.checked_mul(SECS_PER_MINUTE).expect("Duration::minutes out of bounds"); - Duration::seconds(secs) - } - - /// Makes a new `Duration` with given number of seconds. - /// Panics when the duration is more than `i64::MAX` milliseconds - /// or less than `i64::MIN` milliseconds. - #[inline] - pub fn seconds(seconds: i64) -> Duration { - let d = Duration { secs: seconds, nanos: 0 }; - if d < MIN || d > MAX { - panic!("Duration::seconds out of bounds"); - } - d - } - - /// Makes a new `Duration` with given number of milliseconds. - #[inline] - pub fn milliseconds(milliseconds: i64) -> Duration { - let (secs, millis) = div_mod_floor_64(milliseconds, MILLIS_PER_SEC); - let nanos = millis as i32 * NANOS_PER_MILLI; - Duration { secs: secs, nanos: nanos } - } - - /// Makes a new `Duration` with given number of microseconds. - #[inline] - pub fn microseconds(microseconds: i64) -> Duration { - let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); - let nanos = micros as i32 * NANOS_PER_MICRO; - Duration { secs: secs, nanos: nanos } - } - - /// Makes a new `Duration` with given number of nanoseconds. - #[inline] - pub fn nanoseconds(nanos: i64) -> Duration { - let (secs, nanos) = div_mod_floor_64(nanos, NANOS_PER_SEC as i64); - Duration { secs: secs, nanos: nanos as i32 } - } - - /// Returns the total number of whole weeks in the duration. - #[inline] - pub fn num_weeks(&self) -> i64 { - self.num_days() / 7 - } - - /// Returns the total number of whole days in the duration. - pub fn num_days(&self) -> i64 { - self.num_seconds() / SECS_PER_DAY - } - - /// Returns the total number of whole hours in the duration. - #[inline] - pub fn num_hours(&self) -> i64 { - self.num_seconds() / SECS_PER_HOUR - } - - /// Returns the total number of whole minutes in the duration. - #[inline] - pub fn num_minutes(&self) -> i64 { - self.num_seconds() / SECS_PER_MINUTE - } - - /// Returns the total number of whole seconds in the duration. - pub fn num_seconds(&self) -> i64 { - // If secs is negative, nanos should be subtracted from the duration. - if self.secs < 0 && self.nanos > 0 { - self.secs + 1 - } else { - self.secs - } - } - - /// Returns the number of nanoseconds such that - /// `nanos_mod_sec() + num_seconds() * NANOS_PER_SEC` is the total number of - /// nanoseconds in the duration. - fn nanos_mod_sec(&self) -> i32 { - if self.secs < 0 && self.nanos > 0 { - self.nanos - NANOS_PER_SEC - } else { - self.nanos - } - } - - /// Returns the total number of whole milliseconds in the duration, - pub fn num_milliseconds(&self) -> i64 { - // A proper Duration will not overflow, because MIN and MAX are defined - // such that the range is exactly i64 milliseconds. - let secs_part = self.num_seconds() * MILLIS_PER_SEC; - let nanos_part = self.nanos_mod_sec() / NANOS_PER_MILLI; - secs_part + nanos_part as i64 - } - - /// Returns the total number of whole microseconds in the duration, - /// or `None` on overflow (exceeding 2^63 microseconds in either direction). - pub fn num_microseconds(&self) -> Option { - let secs_part = try_opt!(self.num_seconds().checked_mul(MICROS_PER_SEC)); - let nanos_part = self.nanos_mod_sec() / NANOS_PER_MICRO; - secs_part.checked_add(nanos_part as i64) - } - - /// Returns the total number of whole nanoseconds in the duration, - /// or `None` on overflow (exceeding 2^63 nanoseconds in either direction). - pub fn num_nanoseconds(&self) -> Option { - let secs_part = try_opt!(self.num_seconds().checked_mul(NANOS_PER_SEC as i64)); - let nanos_part = self.nanos_mod_sec(); - secs_part.checked_add(nanos_part as i64) - } - - /// Add two durations, returning `None` if overflow occurred. - pub fn checked_add(&self, rhs: &Duration) -> Option { - let mut secs = try_opt!(self.secs.checked_add(rhs.secs)); - let mut nanos = self.nanos + rhs.nanos; - if nanos >= NANOS_PER_SEC { - nanos -= NANOS_PER_SEC; - secs = try_opt!(secs.checked_add(1)); - } - let d = Duration { secs: secs, nanos: nanos }; - // Even if d is within the bounds of i64 seconds, - // it might still overflow i64 milliseconds. - if d < MIN || d > MAX { None } else { Some(d) } - } - - /// Subtract two durations, returning `None` if overflow occurred. - pub fn checked_sub(&self, rhs: &Duration) -> Option { - let mut secs = try_opt!(self.secs.checked_sub(rhs.secs)); - let mut nanos = self.nanos - rhs.nanos; - if nanos < 0 { - nanos += NANOS_PER_SEC; - secs = try_opt!(secs.checked_sub(1)); - } - let d = Duration { secs: secs, nanos: nanos }; - // Even if d is within the bounds of i64 seconds, - // it might still overflow i64 milliseconds. - if d < MIN || d > MAX { None } else { Some(d) } - } - - /// The minimum possible `Duration`: `i64::MIN` milliseconds. - #[inline] - pub fn min_value() -> Duration { MIN } - - /// The maximum possible `Duration`: `i64::MAX` milliseconds. - #[inline] - pub fn max_value() -> Duration { MAX } - - /// A duration where the stored seconds and nanoseconds are equal to zero. - #[inline] - pub fn zero() -> Duration { - Duration { secs: 0, nanos: 0 } - } - - /// Returns `true` if the duration equals `Duration::zero()`. - #[inline] - pub fn is_zero(&self) -> bool { - self.secs == 0 && self.nanos == 0 - } - - /// Creates a `time::Duration` object from `std::time::Duration` - /// - /// This function errors when original duration is larger than the maximum - /// value supported for this type. - pub fn from_std(duration: StdDuration) -> Result { - // We need to check secs as u64 before coercing to i64 - if duration.as_secs() > MAX.secs as u64 { - return Err(OutOfRangeError(())); - } - let d = Duration { - secs: duration.as_secs() as i64, - nanos: duration.subsec_nanos() as i32, - }; - if d > MAX { - return Err(OutOfRangeError(())); - } - Ok(d) - } - - /// Creates a `std::time::Duration` object from `time::Duration` - /// - /// This function errors when duration is less than zero. As standard - /// library implementation is limited to non-negative values. - pub fn to_std(&self) -> Result { - if self.secs < 0 { - return Err(OutOfRangeError(())); - } - Ok(StdDuration::new(self.secs as u64, self.nanos as u32)) - } -} - -impl Neg for Duration { - type Output = Duration; - - #[inline] - fn neg(self) -> Duration { - if self.nanos == 0 { - Duration { secs: -self.secs, nanos: 0 } - } else { - Duration { secs: -self.secs - 1, nanos: NANOS_PER_SEC - self.nanos } - } - } -} - -impl Add for Duration { - type Output = Duration; - - fn add(self, rhs: Duration) -> Duration { - let mut secs = self.secs + rhs.secs; - let mut nanos = self.nanos + rhs.nanos; - if nanos >= NANOS_PER_SEC { - nanos -= NANOS_PER_SEC; - secs += 1; - } - Duration { secs: secs, nanos: nanos } - } -} - -impl Sub for Duration { - type Output = Duration; - - fn sub(self, rhs: Duration) -> Duration { - let mut secs = self.secs - rhs.secs; - let mut nanos = self.nanos - rhs.nanos; - if nanos < 0 { - nanos += NANOS_PER_SEC; - secs -= 1; - } - Duration { secs: secs, nanos: nanos } - } -} - -impl Mul for Duration { - type Output = Duration; - - fn mul(self, rhs: i32) -> Duration { - // Multiply nanoseconds as i64, because it cannot overflow that way. - let total_nanos = self.nanos as i64 * rhs as i64; - let (extra_secs, nanos) = div_mod_floor_64(total_nanos, NANOS_PER_SEC as i64); - let secs = self.secs * rhs as i64 + extra_secs; - Duration { secs: secs, nanos: nanos as i32 } - } -} - -impl Div for Duration { - type Output = Duration; - - fn div(self, rhs: i32) -> Duration { - let mut secs = self.secs / rhs as i64; - let carry = self.secs - secs * rhs as i64; - let extra_nanos = carry * NANOS_PER_SEC as i64 / rhs as i64; - let mut nanos = self.nanos / rhs + extra_nanos as i32; - if nanos >= NANOS_PER_SEC { - nanos -= NANOS_PER_SEC; - secs += 1; - } - if nanos < 0 { - nanos += NANOS_PER_SEC; - secs -= 1; - } - Duration { secs: secs, nanos: nanos } - } -} - -impl fmt::Display for Duration { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // technically speaking, negative duration is not valid ISO 8601, - // but we need to print it anyway. - let (abs, sign) = if self.secs < 0 { (-*self, "-") } else { (*self, "") }; - - let days = abs.secs / SECS_PER_DAY; - let secs = abs.secs - days * SECS_PER_DAY; - let hasdate = days != 0; - let hastime = (secs != 0 || abs.nanos != 0) || !hasdate; - - try!(write!(f, "{}P", sign)); - - if hasdate { - try!(write!(f, "{}D", days)); - } - if hastime { - if abs.nanos == 0 { - try!(write!(f, "T{}S", secs)); - } else if abs.nanos % NANOS_PER_MILLI == 0 { - try!(write!(f, "T{}.{:03}S", secs, abs.nanos / NANOS_PER_MILLI)); - } else if abs.nanos % NANOS_PER_MICRO == 0 { - try!(write!(f, "T{}.{:06}S", secs, abs.nanos / NANOS_PER_MICRO)); - } else { - try!(write!(f, "T{}.{:09}S", secs, abs.nanos)); - } - } - Ok(()) - } -} - -/// Represents error when converting `Duration` to/from a standard library -/// implementation -/// -/// The `std::time::Duration` supports a range from zero to `u64::MAX` -/// *seconds*, while this module supports signed range of up to -/// `i64::MAX` of *milliseconds*. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct OutOfRangeError(()); - -impl fmt::Display for OutOfRangeError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.description()) - } -} - -impl Error for OutOfRangeError { - fn description(&self) -> &str { - "Source duration value is out of range for the target type" - } -} - -// Copied from libnum -#[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, MAX, OutOfRangeError}; - use std::{i32, i64}; - use std::time::Duration as StdDuration; - - #[test] - fn test_duration() { - assert!(Duration::seconds(1) != Duration::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() { - assert_eq!(Duration::zero().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 as i64).num_days(), i32::MAX as i64); - assert_eq!(Duration::days(i32::MIN as i64).num_days(), i32::MIN as i64); - } - - #[test] - fn test_duration_num_seconds() { - assert_eq!(Duration::zero().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); - } - - #[test] - fn test_duration_num_milliseconds() { - assert_eq!(Duration::zero().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(i64::MAX).num_milliseconds(), i64::MAX); - assert_eq!(Duration::milliseconds(i64::MIN).num_milliseconds(), i64::MIN); - assert_eq!(MAX.num_milliseconds(), i64::MAX); - assert_eq!(MIN.num_milliseconds(), i64::MIN); - } - - #[test] - fn test_duration_num_microseconds() { - assert_eq!(Duration::zero().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(i64::MAX).num_microseconds(), Some(i64::MAX)); - assert_eq!(Duration::microseconds(i64::MIN).num_microseconds(), Some(i64::MIN)); - assert_eq!(MAX.num_microseconds(), None); - assert_eq!(MIN.num_microseconds(), None); - - // overflow checks - const MICROS_PER_DAY: i64 = 86400_000_000; - assert_eq!(Duration::days(i64::MAX / MICROS_PER_DAY).num_microseconds(), - Some(i64::MAX / MICROS_PER_DAY * MICROS_PER_DAY)); - assert_eq!(Duration::days(i64::MIN / MICROS_PER_DAY).num_microseconds(), - Some(i64::MIN / MICROS_PER_DAY * MICROS_PER_DAY)); - assert_eq!(Duration::days(i64::MAX / MICROS_PER_DAY + 1).num_microseconds(), None); - assert_eq!(Duration::days(i64::MIN / MICROS_PER_DAY - 1).num_microseconds(), None); - } - - #[test] - fn test_duration_num_nanoseconds() { - assert_eq!(Duration::zero().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(i64::MAX).num_nanoseconds(), Some(i64::MAX)); - assert_eq!(Duration::nanoseconds(i64::MIN).num_nanoseconds(), Some(i64::MIN)); - assert_eq!(MAX.num_nanoseconds(), None); - assert_eq!(MIN.num_nanoseconds(), None); - - // overflow checks - const NANOS_PER_DAY: i64 = 86400_000_000_000; - assert_eq!(Duration::days(i64::MAX / NANOS_PER_DAY).num_nanoseconds(), - Some(i64::MAX / NANOS_PER_DAY * NANOS_PER_DAY)); - assert_eq!(Duration::days(i64::MIN / NANOS_PER_DAY).num_nanoseconds(), - Some(i64::MIN / NANOS_PER_DAY * NANOS_PER_DAY)); - assert_eq!(Duration::days(i64::MAX / NANOS_PER_DAY + 1).num_nanoseconds(), None); - assert_eq!(Duration::days(i64::MIN / NANOS_PER_DAY - 1).num_nanoseconds(), None); - } - - #[test] - fn test_duration_checked_ops() { - assert_eq!(Duration::milliseconds(i64::MAX - 1).checked_add(&Duration::microseconds(999)), - Some(Duration::milliseconds(i64::MAX - 2) + Duration::microseconds(1999))); - assert!(Duration::milliseconds(i64::MAX).checked_add(&Duration::microseconds(1000)) - .is_none()); - - assert_eq!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(0)), - Some(Duration::milliseconds(i64::MIN))); - assert!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(1)) - .is_none()); - } - - #[test] - fn test_duration_mul() { - assert_eq!(Duration::zero() * i32::MAX, Duration::zero()); - assert_eq!(Duration::zero() * i32::MIN, Duration::zero()); - assert_eq!(Duration::nanoseconds(1) * 0, Duration::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)); - assert_eq!(Duration::milliseconds(1500) * -2, Duration::seconds(-3)); - assert_eq!(Duration::milliseconds(-1500) * 2, Duration::seconds(-3)); - } - - #[test] - fn test_duration_div() { - assert_eq!(Duration::zero() / i32::MAX, Duration::zero()); - assert_eq!(Duration::zero() / i32::MIN, Duration::zero()); - assert_eq!(Duration::nanoseconds(123_456_789) / 1, Duration::nanoseconds(123_456_789)); - assert_eq!(Duration::nanoseconds(123_456_789) / -1, -Duration::nanoseconds(123_456_789)); - assert_eq!(-Duration::nanoseconds(123_456_789) / -1, Duration::nanoseconds(123_456_789)); - assert_eq!(-Duration::nanoseconds(123_456_789) / 1, -Duration::nanoseconds(123_456_789)); - assert_eq!(Duration::seconds(1) / 3, Duration::nanoseconds(333_333_333)); - assert_eq!(Duration::seconds(4) / 3, Duration::nanoseconds(1_333_333_333)); - assert_eq!(Duration::seconds(-1) / 2, Duration::milliseconds(-500)); - assert_eq!(Duration::seconds(1) / -2, Duration::milliseconds(-500)); - assert_eq!(Duration::seconds(-1) / -2, Duration::milliseconds(500)); - assert_eq!(Duration::seconds(-4) / 3, Duration::nanoseconds(-1_333_333_333)); - assert_eq!(Duration::seconds(-4) / -3, Duration::nanoseconds(1_333_333_333)); - } - - #[test] - fn test_duration_fmt() { - assert_eq!(Duration::zero().to_string(), "PT0S"); - assert_eq!(Duration::days(42).to_string(), "P42D"); - assert_eq!(Duration::days(-42).to_string(), "-P42D"); - assert_eq!(Duration::seconds(42).to_string(), "PT42S"); - assert_eq!(Duration::milliseconds(42).to_string(), "PT0.042S"); - assert_eq!(Duration::microseconds(42).to_string(), "PT0.000042S"); - assert_eq!(Duration::nanoseconds(42).to_string(), "PT0.000000042S"); - assert_eq!((Duration::days(7) + Duration::milliseconds(6543)).to_string(), - "P7DT6.543S"); - assert_eq!(Duration::seconds(-86401).to_string(), "-P1DT1S"); - assert_eq!(Duration::nanoseconds(-1).to_string(), "-PT0.000000001S"); - - // the format specifier should have no effect on `Duration` - assert_eq!(format!("{:30}", Duration::days(1) + Duration::milliseconds(2345)), - "P1DT2.345S"); - } - - #[test] - fn test_to_std() { - assert_eq!(Duration::seconds(1).to_std(), Ok(StdDuration::new(1, 0))); - assert_eq!(Duration::seconds(86401).to_std(), Ok(StdDuration::new(86401, 0))); - assert_eq!(Duration::milliseconds(123).to_std(), Ok(StdDuration::new(0, 123000000))); - assert_eq!(Duration::milliseconds(123765).to_std(), Ok(StdDuration::new(123, 765000000))); - assert_eq!(Duration::nanoseconds(777).to_std(), Ok(StdDuration::new(0, 777))); - assert_eq!(MAX.to_std(), Ok(StdDuration::new(9223372036854775, 807000000))); - assert_eq!(Duration::seconds(-1).to_std(), - Err(OutOfRangeError(()))); - assert_eq!(Duration::milliseconds(-1).to_std(), - Err(OutOfRangeError(()))); - } - - #[test] - fn test_from_std() { - assert_eq!(Ok(Duration::seconds(1)), - Duration::from_std(StdDuration::new(1, 0))); - assert_eq!(Ok(Duration::seconds(86401)), - Duration::from_std(StdDuration::new(86401, 0))); - assert_eq!(Ok(Duration::milliseconds(123)), - Duration::from_std(StdDuration::new(0, 123000000))); - assert_eq!(Ok(Duration::milliseconds(123765)), - Duration::from_std(StdDuration::new(123, 765000000))); - assert_eq!(Ok(Duration::nanoseconds(777)), - Duration::from_std(StdDuration::new(0, 777))); - assert_eq!(Ok(MAX), - Duration::from_std(StdDuration::new(9223372036854775, 807000000))); - assert_eq!(Duration::from_std(StdDuration::new(9223372036854776, 0)), - Err(OutOfRangeError(()))); - assert_eq!(Duration::from_std(StdDuration::new(9223372036854775, 807000001)), - Err(OutOfRangeError(()))); - } -} diff --git a/src/time/mod.rs b/src/time/mod.rs index c550efb2b1..bc8b4bd1de 100644 --- a/src/time/mod.rs +++ b/src/time/mod.rs @@ -1,3 +1,4 @@ +#![allow(missing_docs)] // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. @@ -632,7 +633,7 @@ pub fn strftime(format: &str, tm: &Tm) -> Result { #[cfg(test)] mod tests { - use crate::time::sys; + use ::time::sys; use super::{Timespec, get_time, precise_time_ns, precise_time_s, at_utc, at, strptime, PreciseTime, SteadyTime, ParseError, Duration}; @@ -1214,7 +1215,7 @@ mod tests { #[test] fn test_time_sub() { - let a = crate::time::now(); + let a = ::time::now(); let b = at(a.to_timespec() + Duration::seconds(5)); let c = b - a; assert_eq!(c.num_nanoseconds(), Some(super::NSEC_PER_SEC as i64 * 5)); diff --git a/src/time/sys.rs b/src/time/sys.rs index fdf2319db7..37bcec43d6 100644 --- a/src/time/sys.rs +++ b/src/time/sys.rs @@ -7,7 +7,7 @@ pub use self::inner::*; target_os = "redox", ))] mod common { - use crate::time::Tm; + use time::Tm; pub fn time_to_tm(ts: i64, tm: &mut Tm) { let leapyear = |year| -> bool { @@ -72,7 +72,7 @@ mod common { #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] mod inner { use std::ops::{Add, Sub}; - use crate::time::Tm; + use time::Tm; use Duration; use super::common::{time_to_tm, tm_to_time}; @@ -140,7 +140,7 @@ mod inner { use std::ops::{Add, Sub}; use syscall; use super::common::{time_to_tm, tm_to_time}; - use crate::time::NSEC_PER_SEC; + use time::NSEC_PER_SEC; use Duration; use Tm; @@ -276,7 +276,7 @@ mod inner { use libc::{self, time_t}; use std::mem; use std::io; - use crate::time::Tm; + use time::Tm; #[cfg(any(target_os = "macos", target_os = "ios"))] pub use self::mac::*; @@ -469,7 +469,7 @@ mod inner { pub fn set_los_angeles_time_zone() -> TzReset { use std::env; env::set_var("TZ", "America/Los_Angeles"); - crate::time::tzset(); + ::time::tzset(); TzReset } @@ -477,7 +477,7 @@ mod inner { pub fn set_london_with_dst_time_zone() -> TzReset { use std::env; env::set_var("TZ", "Europe/London"); - crate::time::tzset(); + ::time::tzset(); TzReset } @@ -487,7 +487,7 @@ mod inner { use std::cmp::Ordering; use std::ops::{Add, Sub}; use libc; - use crate::time::NSEC_PER_SEC; + use time::NSEC_PER_SEC; use Duration; From ed4c48ff787e94c7fe093ce6c064597e55f4ce2e Mon Sep 17 00:00:00 2001 From: Joey Ezechiels Date: Fri, 2 Nov 2018 23:46:41 +0100 Subject: [PATCH 5/6] Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f4587459cb..c98b81aa49 100644 --- a/README.md +++ b/README.md @@ -73,8 +73,8 @@ use chrono::prelude::*; ### Duration Chrono currently uses -the [`Duration`](https://docs.rs/chrono/struct.Duration.html) type -in the `time` module to represent the magnitude of a time span. +the [`time::Duration`](https://docs.rs/time/0.1.40/time/struct.Duration.html) type +from the `time` crate to represent the magnitude of a time span. Since this has the same name to the newer, standard type for duration, the reference will refer this type as `OldDuration`. Note that this is an "accurate" duration represented as seconds and @@ -98,9 +98,9 @@ type to represent a date and a time in a timezone. For more abstract moment-in-time tracking such as internal timekeeping that is unconcerned with timezones, consider -[`SystemTime`](https://doc.rust-lang.org/std/time/struct.SystemTime.html), +[`time::SystemTime`](https://doc.rust-lang.org/std/time/struct.SystemTime.html), which tracks your system clock, or -[`Instant`](https://doc.rust-lang.org/std/time/struct.Instant.html), which +[`time::Instant`](https://doc.rust-lang.org/std/time/struct.Instant.html), which is an opaque but monotonically-increasing representation of a moment in time. `DateTime` is timezone-aware and must be constructed from @@ -172,8 +172,7 @@ Addition and subtraction is also supported. The following illustrates most supported operations to the date and time: ```rust -use chrono::prelude::*; -use chrono::Duration; +use chrono::{prelude::*, Duration}; // assume this returned `2014-11-28T21:45:59.324310806+09:00`: let dt = Local::now(); @@ -388,3 +387,4 @@ and consequently `Utc.ymd(2014, 1, 30).with_month(2)` returns `None`. Advanced time zone handling is not yet supported. For now you can try the [Chrono-tz](https://github.com/chronotope/chrono-tz/) crate instead. + From 52adf963e6e98cd75a4c4523abd7a1465e2ac63d Mon Sep 17 00:00:00 2001 From: Joey Ezechiels Date: Sun, 4 Nov 2018 23:31:17 +0100 Subject: [PATCH 6/6] Add {Utc,Local}::now() constructor versions for the `wasm32` arch While likely providing only incomplete support for WebAssembly, this commit opens up chrono for use on the wasm32 architecture. --- Cargo.toml | 3 +++ src/lib.rs | 4 ++++ src/offset/local.rs | 10 +++++++++- src/offset/utc.rs | 15 +++++++++++++-- 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 43af047c1b..1a9f82963e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,9 @@ num-traits = { version = "0.2", default-features = false } rustc-serialize = { version = "0.3.20", optional = true } serde = { version = "1", optional = true } +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = { version = "0.2" } +js-sys = "0.3" # contains FFI bindings for the JS Date API [target.'cfg(unix)'.dependencies] libc = "0.2.1" diff --git a/src/lib.rs b/src/lib.rs index c5a1dd2327..54c6b1e3e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -422,6 +422,10 @@ extern crate num_traits; extern crate rustc_serialize; #[cfg(feature = "serde")] extern crate serde as serdelib; +#[cfg(target_arch = "wasm32")] +extern crate wasm_bindgen; +#[cfg(target_arch = "wasm32")] +extern crate js_sys; pub mod time; diff --git a/src/offset/local.rs b/src/offset/local.rs index 6aa4ab7558..cce193bc90 100644 --- a/src/offset/local.rs +++ b/src/offset/local.rs @@ -87,9 +87,18 @@ impl Local { } /// Returns a `DateTime` which corresponds to the current date. + #[cfg(not(target_arch = "wasm32"))] pub fn now() -> DateTime { tm_to_datetime(oldtime::now()) } + + /// Returns a `DateTime` which corresponds to the current date. + #[cfg(target_arch = "wasm32")] + pub fn now() -> DateTime { + use super::Utc; + let now: DateTime = super::Utc::now(); + now.with_timezone(&Local) + } } impl TimeZone for Local { @@ -179,4 +188,3 @@ mod tests { "unexpected timestr {:?}", timestr); } } - diff --git a/src/offset/utc.rs b/src/offset/utc.rs index d4e8d10b5a..83b92acfae 100644 --- a/src/offset/utc.rs +++ b/src/offset/utc.rs @@ -4,7 +4,7 @@ //! The UTC (Coordinated Universal Time) time zone. use std::fmt; -#[cfg(feature="clock")] +#[cfg(all(feature="clock", not(target_arch = "wasm32")))] use oldtime; use naive::{NaiveDate, NaiveDateTime}; @@ -38,11 +38,23 @@ impl Utc { pub fn today() -> Date { Utc::now().date() } /// Returns a `DateTime` which corresponds to the current date. + #[cfg(not(target_arch = "wasm32"))] pub fn now() -> DateTime { let spec = oldtime::get_time(); let naive = NaiveDateTime::from_timestamp(spec.sec, spec.nsec as u32); DateTime::from_utc(naive, Utc) } + + /// Returns a `DateTime` which corresponds to the current date. + #[cfg(target_arch = "wasm32")] + pub fn now() -> DateTime { + let now = js_sys::Date::new_0(); + let millisecs_since_unix_epoch: u64 = now.get_time() as u64; + let secs = millisecs_since_unix_epoch / 1000; + let nanos = 1_000_000 * (millisecs_since_unix_epoch - 1000 * secs); + let naive = NaiveDateTime::from_timestamp(secs as i64, nanos as u32); + DateTime::from_utc(naive, Utc) + } } impl TimeZone for Utc { @@ -72,4 +84,3 @@ impl fmt::Debug for Utc { impl fmt::Display for Utc { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "UTC") } } -