Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…
| // 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 <LICENSE-APACHE or | |
| // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
| // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
| // option. This file may not be copied, modified, or distributed | |
| // except according to those terms. | |
| //! Simple time handling. | |
| //! | |
| //! # Usage | |
| //! | |
| //! This crate is [on crates.io](https://crates.io/crates/time) and can be | |
| //! used by adding `time` to the dependencies in your project's `Cargo.toml`. | |
| //! | |
| //! ```toml | |
| //! [dependencies] | |
| //! time = "0.1" | |
| //! ``` | |
| //! | |
| //! And this in your crate root: | |
| //! | |
| //! ```rust | |
| //! extern crate time; | |
| //! ``` | |
| //! | |
| //! 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))] | |
| #[cfg(target_os = "redox")] extern crate syscall; | |
| #[cfg(unix)] extern crate libc; | |
| #[cfg(windows)] extern crate winapi; | |
| #[cfg(feature = "rustc-serialize")] extern crate rustc_serialize; | |
| #[cfg(test)] #[macro_use] extern crate log; | |
| use std::cmp::Ordering; | |
| use std::error::Error; | |
| use std::fmt; | |
| use std::ops::{Add, Sub}; | |
| pub use duration::{Duration, OutOfRangeError}; | |
| use self::ParseError::{InvalidDay, InvalidDayOfMonth, InvalidDayOfWeek, | |
| InvalidDayOfYear, InvalidFormatSpecifier, InvalidHour, | |
| InvalidMinute, InvalidMonth, InvalidSecond, InvalidTime, | |
| InvalidYear, InvalidZoneOffset, InvalidSecondsSinceEpoch, | |
| MissingFormatConverter, UnexpectedCharacter}; | |
| pub use parse::strptime; | |
| mod display; | |
| mod duration; | |
| mod parse; | |
| mod sys; | |
| 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<Duration> 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<Duration> 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<Timespec> 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. | |
| */ | |
| 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 time::{Duration, PreciseTime}; | |
| /// # fn do_some_work() {} | |
| /// | |
| /// let start = PreciseTime::now(); | |
| /// | |
| /// while start.to(PreciseTime::now()) < Duration::seconds(1) { | |
| /// do_some_work(); | |
| /// } | |
| /// ``` | |
| #[derive(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 time::{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<Duration> for SteadyTime { | |
| type Output = SteadyTime; | |
| fn sub(self, other: Duration) -> SteadyTime { | |
| SteadyTime(self.0 - other) | |
| } | |
| } | |
| impl Add<Duration> for SteadyTime { | |
| type Output = SteadyTime; | |
| fn add(self, other: Duration) -> SteadyTime { | |
| SteadyTime(self.0 + other) | |
| } | |
| } | |
| #[cfg(not(any(windows, target_env = "sgx")))] | |
| pub fn tzset() { | |
| extern { fn tzset(); } | |
| unsafe { tzset() } | |
| } | |
| #[cfg(any(windows, target_env = "sgx"))] | |
| 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, 10<sup>9</sup> - 1] | |
| pub tm_nsec: i32, | |
| } | |
| impl Add<Duration> 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<Duration> 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<Tm> 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<Ordering> { | |
| 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 | |
| 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 | |
| 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" | |
| */ | |
| 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. | |
| pub fn strftime<'a>(&'a self, format: &'a str) -> Result<TmFmt<'a>, 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" | |
| */ | |
| 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" | |
| */ | |
| 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<TmFmt<'a>, 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. | |
| pub fn strftime(format: &str, tm: &Tm) -> Result<String, ParseError> { | |
| tm.strftime(format).map(|fmt| fmt.to_string()) | |
| } | |
| #[cfg(test)] | |
| mod tests { | |
| 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<MutexGuard<'static, ()>>, | |
| } | |
| 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 = ::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) | |
| } | |
| } |