aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Galenson <jgalenson@google.com>2020-07-30 16:13:55 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-07-30 16:13:55 +0000
commit6da7c83c3f7f20063374b649978eb135b2acfbf6 (patch)
treefd909936e8a5efd1829e7e305b79499af2ab6e14
parent9854ee8380067f002795db09b26d28e4b23f9442 (diff)
parentc52c35e16b5d81a37074f6d43a9990f9d9c50b13 (diff)
downloadtime-6da7c83c3f7f20063374b649978eb135b2acfbf6.tar.gz
Import time-0.1.43 am: 36bb67a4da am: 01dc2aff93 am: c52c35e16b
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/time/+/1374051 Change-Id: Ib262efedff2e0ed1291f4bb8bc5a6f2c97667276
-rw-r--r--.cargo_vcs_info.json5
-rw-r--r--.gitignore2
-rw-r--r--Cargo.toml38
-rw-r--r--Cargo.toml.orig24
-rw-r--r--LICENSE-APACHE201
-rw-r--r--LICENSE-MIT25
-rw-r--r--README.md31
-rw-r--r--src/display.rs260
-rw-r--r--src/duration.rs655
-rw-r--r--src/lib.rs1282
-rw-r--r--src/parse.rs395
-rw-r--r--src/sys.rs917
12 files changed, 3835 insertions, 0 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..6e47ac0
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,5 @@
+{
+ "git": {
+ "sha1": "3d661fb201445c5d4ccde8214108e44c3fc86252"
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4fffb2f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/target
+/Cargo.lock
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..51e5ab1
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,38 @@
+# 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 believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+name = "time"
+version = "0.1.43"
+authors = ["The Rust Project Developers"]
+exclude = [".github", "benches"]
+description = "Utilities for working with time-related functions in Rust.\n"
+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(windows)".dependencies.winapi]
+version = "0.3.0"
+features = ["std", "minwinbase", "minwindef", "ntdef", "profileapi", "sysinfoapi", "timezoneapi"]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..c9381a1
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,24 @@
+[package]
+name = "time"
+version = "0.1.43"
+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"] }
+
+[dev-dependencies]
+log = "0.4"
+winapi = { version = "0.3.0", features = ["std", "processthreadsapi", "winbase"] }
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
new file mode 100644
index 0000000..16fe87b
--- /dev/null
+++ b/LICENSE-APACHE
@@ -0,0 +1,201 @@
+ 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.
diff --git a/LICENSE-MIT b/LICENSE-MIT
new file mode 100644
index 0000000..39d4bdb
--- /dev/null
+++ b/LICENSE-MIT
@@ -0,0 +1,25 @@
+Copyright (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.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f427eb3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,31 @@
+time
+====
+
+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;
+```
diff --git a/src/display.rs b/src/display.rs
new file mode 100644
index 0000000..705b430
--- /dev/null
+++ b/src/display.rs
@@ -0,0 +1,260 @@
+use std::fmt::{self, Write};
+
+use super::{TmFmt, Tm, Fmt};
+
+impl<'a> fmt::Display for TmFmt<'a> {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ match self.format {
+ Fmt::Str(ref s) => {
+ let mut chars = s.chars();
+ while let Some(ch) = chars.next() {
+ if ch == '%' {
+ // we've already validated that % always precedes
+ // another char
+ 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}
+}
diff --git a/src/duration.rs b/src/duration.rs
new file mode 100644
index 0000000..b33206c
--- /dev/null
+++ b/src/duration.rs
@@ -0,0 +1,655 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Temporal quantification
+
+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: 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 2<sup>63</sup> microseconds in either direction).
+ pub fn num_microseconds(&self) -> Option<i64> {
+ let secs_part = try_opt!(self.num_seconds().checked_mul(MICROS_PER_SEC));
+ let nanos_part = self.nanos_mod_sec() / NANOS_PER_MICRO;
+ secs_part.checked_add(nanos_part as i64)
+ }
+
+ /// Returns the total number of whole nanoseconds in the duration,
+ /// or `None` on overflow (exceeding 2<sup>63</sup> nanoseconds in either direction).
+ pub fn num_nanoseconds(&self) -> Option<i64> {
+ 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<Duration> {
+ 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<Duration> {
+ 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<Duration, OutOfRangeError> {
+ // 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<StdDuration, OutOfRangeError> {
+ 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<i32> 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<i32> 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(())));
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..07c38e4
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,1282 @@
+// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Simple time handling.
+//!
+//! # Usage
+//!
+//! This crate is [on crates.io](https://crates.io/crates/time) and can be
+//! used by adding `time` to the dependencies in your project's `Cargo.toml`.
+//!
+//! ```toml
+//! [dependencies]
+//! time = "0.1"
+//! ```
+//!
+//! And this in your crate root:
+//!
+//! ```rust
+//! extern crate time;
+//! ```
+//!
+//! This crate uses the same syntax for format strings as the
+//! [`strftime()`](http://man7.org/linux/man-pages/man3/strftime.3.html)
+//! function from the C standard library.
+
+#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
+ html_favicon_url = "https://www.rust-lang.org/favicon.ico",
+ html_root_url = "https://doc.rust-lang.org/time/")]
+#![allow(unknown_lints)]
+#![allow(ellipsis_inclusive_range_patterns)] // `..=` requires Rust 1.26
+#![allow(trivial_numeric_casts)]
+#![cfg_attr(test, deny(warnings))]
+
+#[cfg(unix)] extern crate libc;
+#[cfg(windows)] extern crate winapi;
+#[cfg(feature = "rustc-serialize")] extern crate rustc_serialize;
+
+#[cfg(test)] #[macro_use] extern crate log;
+
+use std::cmp::Ordering;
+use std::error::Error;
+use std::fmt;
+use std::ops::{Add, Sub};
+
+pub use duration::{Duration, OutOfRangeError};
+
+use self::ParseError::{InvalidDay, InvalidDayOfMonth, InvalidDayOfWeek,
+ InvalidDayOfYear, InvalidFormatSpecifier, InvalidHour,
+ InvalidMinute, InvalidMonth, InvalidSecond, InvalidTime,
+ InvalidYear, InvalidZoneOffset, InvalidSecondsSinceEpoch,
+ MissingFormatConverter, UnexpectedCharacter};
+
+pub use parse::strptime;
+
+mod display;
+mod duration;
+mod parse;
+mod sys;
+
+static NSEC_PER_SEC: i32 = 1_000_000_000;
+
+/// A record specifying a time value in seconds and nanoseconds, where
+/// nanoseconds represent the offset from the given second.
+///
+/// For example a timespec of 1.2 seconds after the beginning of the epoch would
+/// be represented as {sec: 1, nsec: 200000000}.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))]
+pub struct Timespec { pub sec: i64, pub nsec: i32 }
+/*
+ * Timespec assumes that pre-epoch Timespecs have negative sec and positive
+ * nsec fields. Darwin's and Linux's struct timespec functions handle pre-
+ * epoch timestamps using a "two steps back, one step forward" representation,
+ * though the man pages do not actually document this. For example, the time
+ * -1.2 seconds before the epoch is represented by `Timespec { sec: -2_i64,
+ * nsec: 800_000_000 }`.
+ */
+impl Timespec {
+ pub fn new(sec: i64, nsec: i32) -> Timespec {
+ assert!(nsec >= 0 && nsec < NSEC_PER_SEC);
+ Timespec { sec: sec, nsec: nsec }
+ }
+}
+
+impl Add<Duration> for Timespec {
+ type Output = Timespec;
+
+ fn add(self, other: Duration) -> Timespec {
+ let d_sec = other.num_seconds();
+ // It is safe to unwrap the nanoseconds, because there cannot be
+ // more than one second left, which fits in i64 and in i32.
+ let d_nsec = (other - Duration::seconds(d_sec))
+ .num_nanoseconds().unwrap() as i32;
+ let mut sec = self.sec + d_sec;
+ let mut nsec = self.nsec + d_nsec;
+ if nsec >= NSEC_PER_SEC {
+ nsec -= NSEC_PER_SEC;
+ sec += 1;
+ } else if nsec < 0 {
+ nsec += NSEC_PER_SEC;
+ sec -= 1;
+ }
+ Timespec::new(sec, nsec)
+ }
+}
+
+impl Sub<Duration> for Timespec {
+ type Output = Timespec;
+
+ fn sub(self, other: Duration) -> Timespec {
+ let d_sec = other.num_seconds();
+ // It is safe to unwrap the nanoseconds, because there cannot be
+ // more than one second left, which fits in i64 and in i32.
+ let d_nsec = (other - Duration::seconds(d_sec))
+ .num_nanoseconds().unwrap() as i32;
+ let mut sec = self.sec - d_sec;
+ let mut nsec = self.nsec - d_nsec;
+ if nsec >= NSEC_PER_SEC {
+ nsec -= NSEC_PER_SEC;
+ sec += 1;
+ } else if nsec < 0 {
+ nsec += NSEC_PER_SEC;
+ sec -= 1;
+ }
+ Timespec::new(sec, nsec)
+ }
+}
+
+impl Sub<Timespec> for Timespec {
+ type Output = Duration;
+
+ fn sub(self, other: Timespec) -> Duration {
+ let sec = self.sec - other.sec;
+ let nsec = self.nsec - other.nsec;
+ Duration::seconds(sec) + Duration::nanoseconds(nsec as i64)
+ }
+}
+
+/**
+ * Returns the current time as a `timespec` containing the seconds and
+ * nanoseconds since 1970-01-01T00:00:00Z.
+ */
+pub fn get_time() -> Timespec {
+ let (sec, nsec) = sys::get_time();
+ Timespec::new(sec, nsec)
+}
+
+
+/**
+ * Returns the current value of a high-resolution performance counter
+ * in nanoseconds since an unspecified epoch.
+ */
+#[inline]
+pub fn precise_time_ns() -> u64 {
+ sys::get_precise_ns()
+}
+
+
+/**
+ * Returns the current value of a high-resolution performance counter
+ * in seconds since an unspecified epoch.
+ */
+pub fn precise_time_s() -> f64 {
+ return (precise_time_ns() as f64) / 1000000000.;
+}
+
+/// An opaque structure representing a moment in time.
+///
+/// The only operation that can be performed on a `PreciseTime` is the
+/// calculation of the `Duration` of time that lies between them.
+///
+/// # Examples
+///
+/// Repeatedly call a function for 1 second:
+///
+/// ```rust
+/// use time::{Duration, PreciseTime};
+/// # fn do_some_work() {}
+///
+/// let start = PreciseTime::now();
+///
+/// while start.to(PreciseTime::now()) < Duration::seconds(1) {
+/// do_some_work();
+/// }
+/// ```
+#[derive(Copy, Clone)]
+pub struct PreciseTime(u64);
+
+impl PreciseTime {
+ /// Returns a `PreciseTime` representing the current moment in time.
+ pub fn now() -> PreciseTime {
+ PreciseTime(precise_time_ns())
+ }
+
+ /// Returns a `Duration` representing the span of time from the value of
+ /// `self` to the value of `later`.
+ ///
+ /// # Notes
+ ///
+ /// If `later` represents a time before `self`, the result of this method
+ /// is unspecified.
+ ///
+ /// If `later` represents a time more than 293 years after `self`, the
+ /// result of this method is unspecified.
+ #[inline]
+ pub fn to(&self, later: PreciseTime) -> Duration {
+ // NB: even if later is less than self due to overflow, this will work
+ // since the subtraction will underflow properly as well.
+ //
+ // We could deal with the overflow when casting to an i64, but all that
+ // gets us is the ability to handle intervals of up to 584 years, which
+ // seems not very useful :)
+ Duration::nanoseconds((later.0 - self.0) as i64)
+ }
+}
+
+/// A structure representing a moment in time.
+///
+/// `SteadyTime`s are generated by a "steady" clock, that is, a clock which
+/// never experiences discontinuous jumps and for which time always flows at
+/// the same rate.
+///
+/// # Examples
+///
+/// Repeatedly call a function for 1 second:
+///
+/// ```rust
+/// # use time::{Duration, SteadyTime};
+/// # fn do_some_work() {}
+/// let start = SteadyTime::now();
+///
+/// while SteadyTime::now() - start < Duration::seconds(1) {
+/// do_some_work();
+/// }
+/// ```
+#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug)]
+pub struct SteadyTime(sys::SteadyTime);
+
+impl SteadyTime {
+ /// Returns a `SteadyTime` representing the current moment in time.
+ pub fn now() -> SteadyTime {
+ SteadyTime(sys::SteadyTime::now())
+ }
+}
+
+impl fmt::Display for SteadyTime {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ // TODO: needs a display customization
+ fmt::Debug::fmt(self, fmt)
+ }
+}
+
+impl Sub for SteadyTime {
+ type Output = Duration;
+
+ fn sub(self, other: SteadyTime) -> Duration {
+ self.0 - other.0
+ }
+}
+
+impl Sub<Duration> for SteadyTime {
+ type Output = SteadyTime;
+
+ fn sub(self, other: Duration) -> SteadyTime {
+ SteadyTime(self.0 - other)
+ }
+}
+
+impl Add<Duration> for SteadyTime {
+ type Output = SteadyTime;
+
+ fn add(self, other: Duration) -> SteadyTime {
+ SteadyTime(self.0 + other)
+ }
+}
+
+#[cfg(not(any(windows, target_env = "sgx")))]
+pub fn tzset() {
+ extern { fn tzset(); }
+ unsafe { tzset() }
+}
+
+
+#[cfg(any(windows, target_env = "sgx"))]
+pub fn tzset() {}
+
+/// Holds a calendar date and time broken down into its components (year, month,
+/// day, and so on), also called a broken-down time value.
+// FIXME: use c_int instead of i32?
+#[repr(C)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
+#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))]
+pub struct Tm {
+ /// Seconds after the minute - [0, 60]
+ pub tm_sec: i32,
+
+ /// Minutes after the hour - [0, 59]
+ pub tm_min: i32,
+
+ /// Hours after midnight - [0, 23]
+ pub tm_hour: i32,
+
+ /// Day of the month - [1, 31]
+ pub tm_mday: i32,
+
+ /// Months since January - [0, 11]
+ pub tm_mon: i32,
+
+ /// Years since 1900
+ pub tm_year: i32,
+
+ /// Days since Sunday - [0, 6]. 0 = Sunday, 1 = Monday, ..., 6 = Saturday.
+ pub tm_wday: i32,
+
+ /// Days since January 1 - [0, 365]
+ pub tm_yday: i32,
+
+ /// Daylight Saving Time flag.
+ ///
+ /// This value is positive if Daylight Saving Time is in effect, zero if
+ /// Daylight Saving Time is not in effect, and negative if this information
+ /// is not available.
+ pub tm_isdst: i32,
+
+ /// Identifies the time zone that was used to compute this broken-down time
+ /// value, including any adjustment for Daylight Saving Time. This is the
+ /// number of seconds east of UTC. For example, for U.S. Pacific Daylight
+ /// Time, the value is `-7*60*60 = -25200`.
+ pub tm_utcoff: i32,
+
+ /// Nanoseconds after the second - [0, 10<sup>9</sup> - 1]
+ pub tm_nsec: i32,
+}
+
+impl Add<Duration> for Tm {
+ type Output = Tm;
+
+ /// The resulting Tm is in UTC.
+ // FIXME: The resulting Tm should have the same timezone as `self`;
+ // however, we need a function such as `at_tm(clock: Timespec, offset: i32)`
+ // for this.
+ fn add(self, other: Duration) -> Tm {
+ at_utc(self.to_timespec() + other)
+ }
+}
+
+impl Sub<Duration> for Tm {
+ type Output = Tm;
+
+ /// The resulting Tm is in UTC.
+ // FIXME: The resulting Tm should have the same timezone as `self`;
+ // however, we need a function such as `at_tm(clock: Timespec, offset: i32)`
+ // for this.
+ fn sub(self, other: Duration) -> Tm {
+ at_utc(self.to_timespec() - other)
+ }
+}
+
+impl Sub<Tm> for Tm {
+ type Output = Duration;
+
+ fn sub(self, other: Tm) -> Duration {
+ self.to_timespec() - other.to_timespec()
+ }
+}
+
+impl PartialOrd for Tm {
+ fn partial_cmp(&self, other: &Tm) -> Option<Ordering> {
+ self.to_timespec().partial_cmp(&other.to_timespec())
+ }
+}
+
+impl Ord for Tm {
+ fn cmp(&self, other: &Tm) -> Ordering {
+ self.to_timespec().cmp(&other.to_timespec())
+ }
+}
+
+pub fn empty_tm() -> Tm {
+ Tm {
+ tm_sec: 0,
+ tm_min: 0,
+ tm_hour: 0,
+ tm_mday: 0,
+ tm_mon: 0,
+ tm_year: 0,
+ tm_wday: 0,
+ tm_yday: 0,
+ tm_isdst: 0,
+ tm_utcoff: 0,
+ tm_nsec: 0,
+ }
+}
+
+/// Returns the specified time in UTC
+pub fn at_utc(clock: Timespec) -> Tm {
+ let Timespec { sec, nsec } = clock;
+ let mut tm = empty_tm();
+ sys::time_to_utc_tm(sec, &mut tm);
+ tm.tm_nsec = nsec;
+ tm
+}
+
+/// Returns the current time in UTC
+pub fn now_utc() -> Tm {
+ at_utc(get_time())
+}
+
+/// Returns the specified time in the local timezone
+pub fn at(clock: Timespec) -> Tm {
+ let Timespec { sec, nsec } = clock;
+ let mut tm = empty_tm();
+ sys::time_to_local_tm(sec, &mut tm);
+ tm.tm_nsec = nsec;
+ tm
+}
+
+/// Returns the current time in the local timezone
+pub fn now() -> Tm {
+ at(get_time())
+}
+
+impl Tm {
+ /// Convert time to the seconds from January 1, 1970
+ pub fn to_timespec(&self) -> Timespec {
+ let sec = match self.tm_utcoff {
+ 0 => sys::utc_tm_to_time(self),
+ _ => sys::local_tm_to_time(self)
+ };
+
+ Timespec::new(sec, self.tm_nsec)
+ }
+
+ /// Convert time to the local timezone
+ pub fn to_local(&self) -> Tm {
+ at(self.to_timespec())
+ }
+
+ /// Convert time to the UTC
+ pub fn to_utc(&self) -> Tm {
+ match self.tm_utcoff {
+ 0 => *self,
+ _ => at_utc(self.to_timespec())
+ }
+ }
+
+ /**
+ * Returns a TmFmt that outputs according to the `asctime` format in ISO
+ * C, in the local timezone.
+ *
+ * Example: "Thu Jan 1 00:00:00 1970"
+ */
+ pub fn ctime(&self) -> TmFmt {
+ TmFmt {
+ tm: self,
+ format: Fmt::Ctime,
+ }
+ }
+
+ /**
+ * Returns a TmFmt that outputs according to the `asctime` format in ISO
+ * C.
+ *
+ * Example: "Thu Jan 1 00:00:00 1970"
+ */
+ pub fn asctime(&self) -> TmFmt {
+ TmFmt {
+ tm: self,
+ format: Fmt::Str("%c"),
+ }
+ }
+
+ /// Formats the time according to the format string.
+ pub fn strftime<'a>(&'a self, format: &'a str) -> Result<TmFmt<'a>, ParseError> {
+ validate_format(TmFmt {
+ tm: self,
+ format: Fmt::Str(format),
+ })
+ }
+
+ /**
+ * Returns a TmFmt that outputs according to RFC 822.
+ *
+ * local: "Thu, 22 Mar 2012 07:53:18 PST"
+ * utc: "Thu, 22 Mar 2012 14:53:18 GMT"
+ */
+ pub fn rfc822(&self) -> TmFmt {
+ let fmt = if self.tm_utcoff == 0 {
+ "%a, %d %b %Y %T GMT"
+ } else {
+ "%a, %d %b %Y %T %Z"
+ };
+ TmFmt {
+ tm: self,
+ format: Fmt::Str(fmt),
+ }
+ }
+
+ /**
+ * Returns a TmFmt that outputs according to RFC 822 with Zulu time.
+ *
+ * local: "Thu, 22 Mar 2012 07:53:18 -0700"
+ * utc: "Thu, 22 Mar 2012 14:53:18 -0000"
+ */
+ pub fn rfc822z(&self) -> TmFmt {
+ TmFmt {
+ tm: self,
+ format: Fmt::Str("%a, %d %b %Y %T %z"),
+ }
+ }
+
+ /**
+ * Returns a TmFmt that outputs according to RFC 3339. RFC 3339 is
+ * compatible with ISO 8601.
+ *
+ * local: "2012-02-22T07:53:18-07:00"
+ * utc: "2012-02-22T14:53:18Z"
+ */
+ pub fn rfc3339<'a>(&'a self) -> TmFmt {
+ TmFmt {
+ tm: self,
+ format: Fmt::Rfc3339,
+ }
+ }
+}
+
+#[derive(Copy, PartialEq, Debug, Clone)]
+pub enum ParseError {
+ InvalidSecond,
+ InvalidMinute,
+ InvalidHour,
+ InvalidDay,
+ InvalidMonth,
+ InvalidYear,
+ InvalidDayOfWeek,
+ InvalidDayOfMonth,
+ InvalidDayOfYear,
+ InvalidZoneOffset,
+ InvalidTime,
+ InvalidSecondsSinceEpoch,
+ MissingFormatConverter,
+ InvalidFormatSpecifier(char),
+ UnexpectedCharacter(char, char),
+}
+
+impl fmt::Display for ParseError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ #[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<TmFmt<'a>, ParseError> {
+
+ match (fmt.tm.tm_wday, fmt.tm.tm_mon) {
+ (0...6, 0...11) => (),
+ (_wday, 0...11) => return Err(InvalidDayOfWeek),
+ (0...6, _mon) => return Err(InvalidMonth),
+ _ => return Err(InvalidDay)
+ }
+ match fmt.format {
+ Fmt::Str(ref s) => {
+ let mut chars = s.chars();
+ loop {
+ match chars.next() {
+ Some('%') => {
+ match chars.next() {
+ Some('A') | Some('a') | Some('B') | Some('b') |
+ Some('C') | Some('c') | Some('D') | Some('d') |
+ Some('e') | Some('F') | Some('f') | Some('G') |
+ Some('g') | Some('H') | Some('h') | Some('I') |
+ Some('j') | Some('k') | Some('l') | Some('M') |
+ Some('m') | Some('n') | Some('P') | Some('p') |
+ Some('R') | Some('r') | Some('S') | Some('s') |
+ Some('T') | Some('t') | Some('U') | Some('u') |
+ Some('V') | Some('v') | Some('W') | Some('w') |
+ Some('X') | Some('x') | Some('Y') | Some('y') |
+ Some('Z') | Some('z') | Some('+') | Some('%') => (),
+
+ Some(c) => return Err(InvalidFormatSpecifier(c)),
+ None => return Err(MissingFormatConverter),
+ }
+ },
+ None => break,
+ _ => ()
+ }
+ }
+ },
+ _ => ()
+ }
+ Ok(fmt)
+}
+
+/// Formats the time according to the format string.
+pub fn strftime(format: &str, tm: &Tm) -> Result<String, ParseError> {
+ tm.strftime(format).map(|fmt| fmt.to_string())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::{Timespec, get_time, precise_time_ns, precise_time_s,
+ at_utc, at, strptime, PreciseTime, SteadyTime, ParseError, Duration};
+ use super::ParseError::{InvalidTime, InvalidYear, MissingFormatConverter,
+ InvalidFormatSpecifier};
+
+ #[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<MutexGuard<'static, ()>>,
+ }
+
+ fn set_time_zone_la_or_london(london: bool) -> TzReset {
+ // Lock manages current timezone because some tests require LA some
+ // London
+ static mut LOCK: *mut Mutex<()> = 0 as *mut _;
+ #[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)
+ }
+}
diff --git a/src/parse.rs b/src/parse.rs
new file mode 100644
index 0000000..602bdc5
--- /dev/null
+++ b/src/parse.rs
@@ -0,0 +1,395 @@
+use 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<Tm, ParseError> {
+ 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<i32> {
+ 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<i32> {
+ 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<i64> {
+ 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<i32> {
+ let before = *ss;
+ match match_digits(ss, min_digits, max_digits, ws) {
+ Some(val) if val >= min && val <= max => Some(val),
+ _ => { *ss = before; None }
+ }
+}
+
+fn parse_char(s: &mut &str, c: char) -> Result<(), ParseError> {
+ match s.char_indices().next() {
+ Some((i, c2)) => {
+ if c == c2 {
+ *s = &s[i + c2.len_utf8()..];
+ Ok(())
+ } else {
+ Err(ParseError::UnexpectedCharacter(c, c2))
+ }
+ }
+ None => Err(ParseError::InvalidTime),
+ }
+}
diff --git a/src/sys.rs b/src/sys.rs
new file mode 100644
index 0000000..80de8fa
--- /dev/null
+++ b/src/sys.rs
@@ -0,0 +1,917 @@
+#![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(target_os = "emscripten")))]
+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<Duration> for SteadyTime {
+ type Output = SteadyTime;
+ fn sub(self, _other: Duration) -> SteadyTime {
+ unimplemented!()
+ }
+ }
+
+ impl Add<Duration> for SteadyTime {
+ type Output = SteadyTime;
+ fn add(self, _other: Duration) -> SteadyTime {
+ unimplemented!()
+ }
+ }
+}
+
+#[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<Duration> for SteadyTime {
+ type Output = SteadyTime;
+ fn sub(self, other: Duration) -> SteadyTime {
+ SteadyTime { t: self.t - other }
+ }
+ }
+
+ impl Add<Duration> 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<Duration> for SteadyTime {
+ type Output = SteadyTime;
+ fn sub(self, other: Duration) -> SteadyTime {
+ self + -other
+ }
+ }
+ impl Add<Duration> 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::ops::{Add, Sub};
+ use libc;
+
+ use Duration;
+
+ pub fn get_time() -> (i64, i32) {
+ let mut tv = libc::timespec { tv_sec: 0, tv_nsec: 0 };
+ unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, &mut tv); }
+ (tv.tv_sec as i64, tv.tv_nsec as i32)
+ }
+
+ pub fn get_precise_ns() -> u64 {
+ let mut ts = libc::timespec { tv_sec: 0, tv_nsec: 0 };
+ unsafe {
+ libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts);
+ }
+ (ts.tv_sec as u64) * 1000000000 + (ts.tv_nsec as u64)
+ }
+
+ #[derive(Copy)]
+ pub struct SteadyTime {
+ t: libc::timespec,
+ }
+
+ impl fmt::Debug for SteadyTime {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ write!(fmt, "SteadyTime {{ tv_sec: {:?}, tv_nsec: {:?} }}",
+ self.t.tv_sec, self.t.tv_nsec)
+ }
+ }
+
+ impl Clone for SteadyTime {
+ fn clone(&self) -> SteadyTime {
+ SteadyTime { t: self.t }
+ }
+ }
+
+ impl SteadyTime {
+ pub fn now() -> SteadyTime {
+ let mut t = SteadyTime {
+ t: libc::timespec {
+ tv_sec: 0,
+ tv_nsec: 0,
+ }
+ };
+ unsafe {
+ assert_eq!(0, libc::clock_gettime(libc::CLOCK_MONOTONIC,
+ &mut t.t));
+ }
+ t
+ }
+ }
+
+ impl Sub for SteadyTime {
+ type Output = Duration;
+ fn sub(self, other: SteadyTime) -> Duration {
+ if self.t.tv_nsec >= other.t.tv_nsec {
+ Duration::seconds(self.t.tv_sec as i64 - other.t.tv_sec as i64) +
+ Duration::nanoseconds(self.t.tv_nsec as i64 - other.t.tv_nsec as i64)
+ } else {
+ Duration::seconds(self.t.tv_sec as i64 - 1 - other.t.tv_sec as i64) +
+ Duration::nanoseconds(self.t.tv_nsec as i64 + ::NSEC_PER_SEC as i64 -
+ other.t.tv_nsec as i64)
+ }
+ }
+ }
+
+ impl Sub<Duration> for SteadyTime {
+ type Output = SteadyTime;
+ fn sub(self, other: Duration) -> SteadyTime {
+ self + -other
+ }
+ }
+
+ impl Add<Duration> 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<Ordering> {
+ 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<Duration> for SteadyTime {
+ type Output = SteadyTime;
+ fn sub(self, other: Duration) -> SteadyTime {
+ self + -other
+ }
+ }
+
+ impl Add<Duration> 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::<TIME_ZONE_INFORMATION>();
+ 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::<TIME_ZONE_INFORMATION>();
+ 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::<TOKEN_PRIVILEGES>();
+ 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);
+ }
+}