time-0.1.45/.cargo_vcs_info.json0000644000000001360000000000100120610ustar { "git": { "sha1": "d147fa71f65fcdc743a1b4f7cf132d84bd14e56f" }, "path_in_vcs": "" }time-0.1.45/.gitignore000064400000000000000000000000241046102023000126350ustar 00000000000000/target /Cargo.lock time-0.1.45/Cargo.toml0000644000000025500000000000100100610ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] name = "time" version = "0.1.45" authors = ["The Rust Project Developers"] exclude = [ ".github", "benches", ] description = """ Utilities for working with time-related functions in Rust. """ homepage = "https://github.com/time-rs/time" documentation = "https://docs.rs/time/~0.1" readme = "README.md" license = "MIT/Apache-2.0" repository = "https://github.com/time-rs/time" [dependencies.libc] version = "0.2.69" [dependencies.rustc-serialize] version = "0.3" optional = true [dev-dependencies.log] version = "0.4" [dev-dependencies.winapi] version = "0.3.0" features = [ "std", "processthreadsapi", "winbase", ] [target."cfg(target_os = \"wasi\")".dependencies.wasi] version = "=0.10.0" [target."cfg(windows)".dependencies.winapi] version = "0.3.0" features = [ "std", "minwinbase", "minwindef", "ntdef", "profileapi", "sysinfoapi", "timezoneapi", ] time-0.1.45/Cargo.toml.orig000064400000000000000000000014631046102023000135440ustar 00000000000000[package] name = "time" version = "0.1.45" authors = ["The Rust Project Developers"] license = "MIT/Apache-2.0" homepage = "https://github.com/time-rs/time" repository = "https://github.com/time-rs/time" documentation = "https://docs.rs/time/~0.1" description = """ Utilities for working with time-related functions in Rust. """ readme = "README.md" exclude = [".github", "benches"] [dependencies] libc = "0.2.69" rustc-serialize = { version = "0.3", optional = true } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.0", features = ["std", "minwinbase", "minwindef", "ntdef", "profileapi", "sysinfoapi", "timezoneapi"] } [target.'cfg(target_os = "wasi")'.dependencies] wasi = "=0.10.0" [dev-dependencies] log = "0.4" winapi = { version = "0.3.0", features = ["std", "processthreadsapi", "winbase"] } time-0.1.45/LICENSE-APACHE000064400000000000000000000251371046102023000126050ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. time-0.1.45/LICENSE-MIT000064400000000000000000000020571046102023000123110ustar 00000000000000Copyright (c) 2014 The Rust Project Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. time-0.1.45/README.md000064400000000000000000000015201046102023000121260ustar 00000000000000time ==== Utilities for working with time-related functions in Rust [![build status](https://github.com/time-rs/time/workflows/Build/badge.svg?branch=v0.1)](https://github.com/time-rs/time/actions?query=branch%3Av0.1) [![Documentation](https://docs.rs/time/badge.svg?version=0.1)](https://docs.rs/time/~0.1) ![rustc 1.21.0](https://img.shields.io/badge/rustc-1.21.0-blue) ## time v0.1.x is Deprecated The 0.1.x series of this library is deprecated and in maintenance mode. No new features will be added. Active development now occurs in the 0.2.x series. If you need additional functionality that this crate does not provide, check out the [`chrono`](https://github.com/chronotope/chrono) crate. ## Usage Put this in your `Cargo.toml`: ```toml [dependencies] time = "0.1" ``` And this in your crate root: ```rust extern crate time; ``` time-0.1.45/src/display.rs000064400000000000000000000205561046102023000134630ustar 00000000000000use 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 parse_type(fmt, chars.next().unwrap(), self.tm)?; } else { 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' => { parse_type(fmt, 'a', tm)?; fmt.write_str(" ")?; parse_type(fmt, 'b', tm)?; fmt.write_str(" ")?; parse_type(fmt, 'e', tm)?; fmt.write_str(" ")?; parse_type(fmt, 'T', tm)?; fmt.write_str(" ")?; parse_type(fmt, 'Y', tm) } 'D' | 'x' => { parse_type(fmt, 'm', tm)?; fmt.write_str("/")?; parse_type(fmt, 'd', tm)?; 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' => { parse_type(fmt, 'Y', tm)?; fmt.write_str("-")?; parse_type(fmt, 'm', tm)?; 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' => { parse_type(fmt, 'H', tm)?; fmt.write_str(":")?; parse_type(fmt, 'M', tm) } 'r' => { parse_type(fmt, 'I', tm)?; fmt.write_str(":")?; parse_type(fmt, 'M', tm)?; fmt.write_str(":")?; parse_type(fmt, 'S', tm)?; fmt.write_str(" ")?; parse_type(fmt, 'p', tm) } 'S' => write!(fmt, "{:02}", tm.tm_sec), 's' => write!(fmt, "{}", tm.to_timespec().sec), 'T' | 'X' => { parse_type(fmt, 'H', tm)?; fmt.write_str(":")?; parse_type(fmt, 'M', tm)?; 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' => { parse_type(fmt, 'e', tm)?; fmt.write_str("-")?; parse_type(fmt, 'b', tm)?; 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} } time-0.1.45/src/duration.rs000064400000000000000000000637131046102023000136450ustar 00000000000000// 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, Hash)] 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 out 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)) } /// Returns the raw value of duration. #[cfg(target_env = "sgx")] pub(crate) fn raw(&self) -> (i64, i32) { (self.secs, self.nanos) } } 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; write!(f, "{}P", sign)?; if hasdate { write!(f, "{}D", days)?; } if hastime { if abs.nanos == 0 { write!(f, "T{}S", secs)?; } else if abs.nanos % NANOS_PER_MILLI == 0 { write!(f, "T{}.{:03}S", secs, abs.nanos / NANOS_PER_MILLI)?; } else if abs.nanos % NANOS_PER_MICRO == 0 { write!(f, "T{}.{:06}S", secs, abs.nanos / NANOS_PER_MICRO)?; } else { 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 { #[allow(deprecated)] 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(()))); } } time-0.1.45/src/lib.rs000064400000000000000000001205321046102023000125570ustar 00000000000000// 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. //! //! # 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(unknown_lints)] #![allow(ellipsis_inclusive_range_patterns)] // `..=` requires Rust 1.26 #![allow(trivial_numeric_casts)] #[cfg(unix)] extern crate libc; #[cfg(windows)] extern crate winapi; #[cfg(feature = "rustc-serialize")] extern crate rustc_serialize; #[cfg(target_os = "wasi")] extern crate wasi; #[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 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. */ 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 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(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, 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 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, 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 { #[allow(deprecated)] 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. pub fn strftime(format: &str, tm: &Tm) -> Result { 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}; #[allow(deprecated)] // `Once::new` is const starting in Rust 1.32 use std::sync::ONCE_INIT; use std::sync::{Once, Mutex, MutexGuard, LockResult}; use std::i32; 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 _; #[allow(deprecated)] // `Once::new` is const starting in Rust 1.32 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 = 1577836800i64; // 2020-01-01T00:00:00Z static SOME_FUTURE_DATE: i64 = i32::MAX as i64; // Y2038 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) } } time-0.1.45/src/parse.rs000064400000000000000000000327231046102023000131270ustar 00000000000000use super::{Timespec, Tm, at_utc, ParseError, NSEC_PER_SEC}; /// Parses the time from the string according to the format string. 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() { parse_type(&mut s, ch, &mut tm)?; } } else { 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 { #[allow(deprecated)] // use `trim_start_matches` starting in 1.30 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), } } time-0.1.45/src/sys.rs000064400000000000000000000735601046102023000126370ustar 00000000000000#![allow(bad_style)] pub use self::inner::*; #[cfg(any( all(target_arch = "wasm32", not(target_os = "emscripten")), target_env = "sgx" ))] mod common { use 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(any(target_os = "emscripten", target_os = "wasi"))))] mod inner { use std::ops::{Add, Sub}; use 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 = "wasi")] mod inner { use std::ops::{Add, Sub}; use Tm; use Duration; use super::common::{time_to_tm, tm_to_time}; use wasi::{clock_time_get, CLOCKID_MONOTONIC, CLOCKID_REALTIME}; #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] pub struct SteadyTime { t: u64 } 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 ts = get_precise_ns(); ( ts as i64 / 1_000_000_000, (ts as i64 % 1_000_000_000) as i32, ) } pub fn get_precise_ns() -> u64 { unsafe { clock_time_get(CLOCKID_REALTIME, 1_000_000_000) } .expect("Host doesn't implement a real-time clock") } impl SteadyTime { pub fn now() -> SteadyTime { SteadyTime { t: unsafe { clock_time_get(CLOCKID_MONOTONIC, 1_000_000_000) } .expect("Host doesn't implement a monotonic clock"), } } } 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(target_env = "sgx")] mod inner { use std::ops::{Add, Sub}; use Tm; use Duration; use super::common::{time_to_tm, tm_to_time}; use std::time::SystemTime; /// The number of nanoseconds in seconds. const NANOS_PER_SEC: u64 = 1_000_000_000; #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] pub struct SteadyTime { t: Duration } 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) { SteadyTime::now().t.raw() } pub fn get_precise_ns() -> u64 { // This unwrap is safe because current time is well ahead of UNIX_EPOCH, unless system // clock is adjusted backward. let std_duration = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); std_duration.as_secs() * NANOS_PER_SEC + std_duration.subsec_nanos() as u64 } impl SteadyTime { pub fn now() -> SteadyTime { // This unwrap is safe because current time is well ahead of UNIX_EPOCH, unless system // clock is adjusted backward. let std_duration = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); // This unwrap is safe because duration is well within the limits of i64. let duration = Duration::from_std(std_duration).unwrap(); SteadyTime { t: duration } } } impl Sub for SteadyTime { type Output = Duration; fn sub(self, other: SteadyTime) -> Duration { self.t - other.t } } impl Sub for SteadyTime { type Output = SteadyTime; fn sub(self, other: Duration) -> SteadyTime { SteadyTime { t: self.t - other } } } impl Add for SteadyTime { type Output = SteadyTime; fn add(self, other: Duration) -> SteadyTime { SteadyTime { t: self.t + other } } } } #[cfg(unix)] mod inner { use libc::{self, time_t}; use std::mem; use std::io; use 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(any(target_os = "solaris", target_os = "illumos"))] 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", target_os = "illumos"))] 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(any(target_os = "solaris", target_os = "illumos"))] 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(any(target_os = "solaris", target_os = "illumos")))] 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", target_os = "illumos" )))] 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 { #[allow(deprecated)] use libc::{self, timeval, mach_timebase_info}; #[allow(deprecated)] use std::sync::{Once, ONCE_INIT}; use std::ops::{Add, Sub}; use Duration; #[allow(deprecated)] 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) } #[allow(deprecated)] #[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"); ::tzset(); TzReset } #[cfg(test)] pub fn set_london_with_dst_time_zone() -> TzReset { use std::env; env::set_var("TZ", "Europe/London"); ::tzset(); TzReset } #[cfg(all(not(target_os = "macos"), not(target_os = "ios")))] mod unix { use std::fmt; use std::cmp::Ordering; use std::mem::zeroed; use std::ops::{Add, Sub}; use libc; use Duration; pub fn get_time() -> (i64, i32) { // SAFETY: libc::timespec is zero initializable. let mut tv: libc::timespec = unsafe { zeroed() }; 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 { // SAFETY: libc::timespec is zero initializable. let mut ts: libc::timespec = unsafe { zeroed() }; 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 { // SAFETY: libc::timespec is zero initializable. t: unsafe { zeroed() } }; 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; #[allow(deprecated)] 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; #[allow(deprecated)] 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 winapi::um::processthreadsapi::*; use winapi::um::winbase::LookupPrivilegeValueA; const SE_PRIVILEGE_ENABLED: DWORD = 2; #[allow(deprecated)] 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; } 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.Privileges.len(), 1); let c = ::std::ffi::CString::new("SeTimeZonePrivilege").unwrap(); call!(LookupPrivilegeValueA(0 as *const _, c.as_ptr(), &mut tkp.Privileges[0].Luid)); tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; tkp.PrivilegeCount = 1; call!(AdjustTokenPrivileges(hToken, FALSE, &mut 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); } }