diff options
author | Ellie Huxtable <e@elm.sh> | 2021-05-17 19:51:09 +0100 |
---|---|---|
committer | Ellie Huxtable <e@elm.sh> | 2021-05-17 19:51:09 +0100 |
commit | d0215a937a7889a97e11778ee4b0f9a12de01278 (patch) | |
tree | 5a8fd5ad62e6b5a4c218746ff2d4bd97373a48de /vendor/humantime-2.1.0 | |
parent | 802a2258cbd839c5b82d24f74d7aebe4a27d8dc5 (diff) |
Vendor dependenciesvendor
Just testing how CI works with this. I tend to prefer vendoring, as it
means that if you have a copy of the code *you can always build it*.
Even if you're 20 years in the future
This is the output of
```
cargo vendor --versioned-dirs
```
Diffstat (limited to 'vendor/humantime-2.1.0')
-rw-r--r-- | vendor/humantime-2.1.0/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | vendor/humantime-2.1.0/Cargo.toml | 37 | ||||
-rw-r--r-- | vendor/humantime-2.1.0/LICENSE-APACHE | 202 | ||||
-rw-r--r-- | vendor/humantime-2.1.0/LICENSE-MIT | 26 | ||||
-rw-r--r-- | vendor/humantime-2.1.0/README.md | 68 | ||||
-rw-r--r-- | vendor/humantime-2.1.0/benches/datetime_format.rs | 56 | ||||
-rw-r--r-- | vendor/humantime-2.1.0/benches/datetime_parse.rs | 47 | ||||
-rw-r--r-- | vendor/humantime-2.1.0/bulk.yaml | 8 | ||||
-rw-r--r-- | vendor/humantime-2.1.0/src/date.rs | 623 | ||||
-rw-r--r-- | vendor/humantime-2.1.0/src/duration.rs | 456 | ||||
-rw-r--r-- | vendor/humantime-2.1.0/src/lib.rs | 34 | ||||
-rw-r--r-- | vendor/humantime-2.1.0/src/wrapper.rs | 107 | ||||
-rw-r--r-- | vendor/humantime-2.1.0/vagga.yaml | 92 |
13 files changed, 1757 insertions, 0 deletions
diff --git a/vendor/humantime-2.1.0/.cargo-checksum.json b/vendor/humantime-2.1.0/.cargo-checksum.json new file mode 100644 index 00000000..80dbdff4 --- /dev/null +++ b/vendor/humantime-2.1.0/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"c0d1443ae237dee3c09cb70185fa947d8d8cb660acfbcb8f650798bd4e0c019e","LICENSE-APACHE":"c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08","LICENSE-MIT":"f6deca8261a8f4a3403dc74c725c46051157fd36c27cd4b100277eb1f303ad11","README.md":"e4bb65f28ddffb11d7eb337e9585947651f2fc11a5e4290f0ca126e21c582c1e","benches/datetime_format.rs":"ffe2e459e9b48e8fdbfb3686f6297257d66b29369ecd6750ae9fbba527ccc681","benches/datetime_parse.rs":"8039c4bd5f1795dbb54e1e39da5988f1d2df6c86c42d8fd378094fc78074d31e","bulk.yaml":"17c2548388e0cd3a63473021a2f1e4ddedee082d79d9167cb31ad06a1890d3fc","src/date.rs":"a8159494372ba8ec8a3a0a5b69c9b185f3e7ab007f283188bf96a6f071151f20","src/duration.rs":"4939ae2d1c3056424de421c4b124d0fb387e058d9abc82a21b83b38d66a40753","src/lib.rs":"ad4dbed28080d9a64ef0100c96b20ff4988d9dde908f56e28ece7252f5932990","src/wrapper.rs":"badc640e77379a42b2fcb728337d60a764b7f00a1b5b1d50c7372ddc20941967","vagga.yaml":"8396fe1510117c1c7bc3e896b62290dcf2dd300346071297018b0077ad9e45ce"},"package":"9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"}
\ No newline at end of file diff --git a/vendor/humantime-2.1.0/Cargo.toml b/vendor/humantime-2.1.0/Cargo.toml new file mode 100644 index 00000000..950db73d --- /dev/null +++ b/vendor/humantime-2.1.0/Cargo.toml @@ -0,0 +1,37 @@ +# 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] +edition = "2018" +name = "humantime" +version = "2.1.0" +authors = ["Paul Colomiets <paul@colomiets.name>"] +description = " A parser and formatter for std::time::{Duration, SystemTime}\n" +homepage = "https://github.com/tailhook/humantime" +documentation = "https://docs.rs/humantime" +readme = "README.md" +keywords = ["time", "human", "human-friendly", "parser", "duration"] +categories = ["date-and-time"] +license = "MIT/Apache-2.0" +repository = "https://github.com/tailhook/humantime" + +[lib] +name = "humantime" +path = "src/lib.rs" +[dev-dependencies.chrono] +version = "0.4" + +[dev-dependencies.rand] +version = "0.6" + +[dev-dependencies.time] +version = "0.1" diff --git a/vendor/humantime-2.1.0/LICENSE-APACHE b/vendor/humantime-2.1.0/LICENSE-APACHE new file mode 100644 index 00000000..8f71f43f --- /dev/null +++ b/vendor/humantime-2.1.0/LICENSE-APACHE @@ -0,0 +1,202 @@ + 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/vendor/humantime-2.1.0/LICENSE-MIT b/vendor/humantime-2.1.0/LICENSE-MIT new file mode 100644 index 00000000..a099fbad --- /dev/null +++ b/vendor/humantime-2.1.0/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (c) 2016 The humantime Developers + +Includes parts of http date with the following copyright: +Copyright (c) 2016 Pyfisch + +Includes portions of musl libc with the following copyright: +Copyright © 2005-2013 Rich Felker + + +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/vendor/humantime-2.1.0/README.md b/vendor/humantime-2.1.0/README.md new file mode 100644 index 00000000..39156dcd --- /dev/null +++ b/vendor/humantime-2.1.0/README.md @@ -0,0 +1,68 @@ +Human Time +========== + +**Status: stable** + +[Documentation](https://docs.rs/humantime) | +[Github](https://github.com/tailhook/humantime) | +[Crate](https://crates.io/crates/humantime) + + +Features: + +* Parses durations in free form like `15days 2min 2s` +* Formats durations in similar form `2years 2min 12us` +* Parses and formats timestamp in `rfc3339` format: `2018-01-01T12:53:00Z` +* Parses timestamps in a weaker format: `2018-01-01 12:53:00` + +Timestamp parsing/formatting is super-fast because format is basically +fixed. + +Here are some micro-benchmarks: + +``` +test result: ok. 0 passed; 0 failed; 26 ignored; 0 measured; 0 filtered out + + Running target/release/deps/datetime_format-8facb4ac832d9770 + +running 2 tests +test rfc3339_chrono ... bench: 737 ns/iter (+/- 37) +test rfc3339_humantime_seconds ... bench: 73 ns/iter (+/- 2) + +test result: ok. 0 passed; 0 failed; 0 ignored; 2 measured; 0 filtered out + + Running target/release/deps/datetime_parse-342628f877d7867c + +running 6 tests +test datetime_utc_parse_millis ... bench: 228 ns/iter (+/- 11) +test datetime_utc_parse_nanos ... bench: 236 ns/iter (+/- 10) +test datetime_utc_parse_seconds ... bench: 204 ns/iter (+/- 18) +test rfc3339_humantime_millis ... bench: 28 ns/iter (+/- 1) +test rfc3339_humantime_nanos ... bench: 36 ns/iter (+/- 2) +test rfc3339_humantime_seconds ... bench: 24 ns/iter (+/- 1) + +test result: ok. 0 passed; 0 failed; 0 ignored; 6 measured; 0 filtered out +``` + +See [humantime-serde] for serde integration (previous crate [serde-humantime] looks unmaintained). + +[serde-humantime]: https://docs.rs/serde-humantime/0.1.1/serde_humantime/ +[humantime-serde]: https://docs.rs/humantime-serde + +License +======= + +Licensed under either of + +* Apache License, Version 2.0, (./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* MIT license (./LICENSE-MIT or http://opensource.org/licenses/MIT) + +at your option. + +Contribution +------------ + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/vendor/humantime-2.1.0/benches/datetime_format.rs b/vendor/humantime-2.1.0/benches/datetime_format.rs new file mode 100644 index 00000000..77d47667 --- /dev/null +++ b/vendor/humantime-2.1.0/benches/datetime_format.rs @@ -0,0 +1,56 @@ +#![feature(test)] +extern crate test; + +use std::io::Write; +use std::time::{Duration, UNIX_EPOCH}; + +use humantime::format_rfc3339; + +#[bench] +fn rfc3339_humantime_seconds(b: &mut test::Bencher) { + let time = UNIX_EPOCH + Duration::new(1_483_228_799, 0); + let mut buf = Vec::with_capacity(100); + b.iter(|| { + buf.truncate(0); + write!(&mut buf, "{}", format_rfc3339(time)).unwrap() + }); +} + +#[bench] +fn rfc3339_chrono(b: &mut test::Bencher) { + use chrono::{DateTime, NaiveDateTime, Utc}; + use chrono::format::Item; + use chrono::format::Item::*; + use chrono::format::Numeric::*; + use chrono::format::Fixed::*; + use chrono::format::Pad::*; + + let time = DateTime::<Utc>::from_utc( + NaiveDateTime::from_timestamp(1_483_228_799, 0), Utc); + let mut buf = Vec::with_capacity(100); + + // formatting code from env_logger + const ITEMS: &[Item<'static>] = { + &[ + Numeric(Year, Zero), + Literal("-"), + Numeric(Month, Zero), + Literal("-"), + Numeric(Day, Zero), + Literal("T"), + Numeric(Hour, Zero), + Literal(":"), + Numeric(Minute, Zero), + Literal(":"), + Numeric(Second, Zero), + Fixed(TimezoneOffsetZ), + ] + }; + + + b.iter(|| { + buf.truncate(0); + write!(&mut buf, "{}", time.format_with_items(ITEMS.iter().cloned())) + .unwrap() + }); +} diff --git a/vendor/humantime-2.1.0/benches/datetime_parse.rs b/vendor/humantime-2.1.0/benches/datetime_parse.rs new file mode 100644 index 00000000..4248da28 --- /dev/null +++ b/vendor/humantime-2.1.0/benches/datetime_parse.rs @@ -0,0 +1,47 @@ +#![feature(test)] +extern crate test; + +use chrono::{DateTime}; +use humantime::parse_rfc3339; + +#[bench] +fn rfc3339_humantime_seconds(b: &mut test::Bencher) { + b.iter(|| { + parse_rfc3339("2018-02-13T23:08:32Z").unwrap() + }); +} + +#[bench] +fn datetime_utc_parse_seconds(b: &mut test::Bencher) { + b.iter(|| { + DateTime::parse_from_rfc3339("2018-02-13T23:08:32Z").unwrap() + }); +} + +#[bench] +fn rfc3339_humantime_millis(b: &mut test::Bencher) { + b.iter(|| { + parse_rfc3339("2018-02-13T23:08:32.123Z").unwrap() + }); +} + +#[bench] +fn datetime_utc_parse_millis(b: &mut test::Bencher) { + b.iter(|| { + DateTime::parse_from_rfc3339("2018-02-13T23:08:32.123Z").unwrap() + }); +} + +#[bench] +fn rfc3339_humantime_nanos(b: &mut test::Bencher) { + b.iter(|| { + parse_rfc3339("2018-02-13T23:08:32.123456983Z").unwrap() + }); +} + +#[bench] +fn datetime_utc_parse_nanos(b: &mut test::Bencher) { + b.iter(|| { + DateTime::parse_from_rfc3339("2018-02-13T23:08:32.123456983Z").unwrap() + }); +} diff --git a/vendor/humantime-2.1.0/bulk.yaml b/vendor/humantime-2.1.0/bulk.yaml new file mode 100644 index 00000000..cdb9763b --- /dev/null +++ b/vendor/humantime-2.1.0/bulk.yaml @@ -0,0 +1,8 @@ +minimum-bulk: v0.4.5 + +versions: + +- file: Cargo.toml + block-start: ^\[package\] + block-end: ^\[.*\] + regex: ^version\s*=\s*"(\S+)" diff --git a/vendor/humantime-2.1.0/src/date.rs b/vendor/humantime-2.1.0/src/date.rs new file mode 100644 index 00000000..9d28ee7d --- /dev/null +++ b/vendor/humantime-2.1.0/src/date.rs @@ -0,0 +1,623 @@ +use std::error::Error as StdError; +use std::fmt; +use std::str; +use std::time::{SystemTime, Duration, UNIX_EPOCH}; + +#[cfg(target_os="cloudabi")] +mod max { + pub const SECONDS: u64 = ::std::u64::MAX / 1_000_000_000; + #[allow(unused)] + pub const TIMESTAMP: &'static str = "2554-07-21T23:34:33Z"; +} +#[cfg(all( + target_pointer_width="32", + not(target_os="cloudabi"), + not(target_os="windows"), + not(all(target_arch="wasm32", not(target_os="emscripten"))) +))] +mod max { + pub const SECONDS: u64 = ::std::i32::MAX as u64; + #[allow(unused)] + pub const TIMESTAMP: &'static str = "2038-01-19T03:14:07Z"; +} + +#[cfg(any( + target_pointer_width="64", + target_os="windows", + all(target_arch="wasm32", not(target_os="emscripten")), +))] +mod max { + pub const SECONDS: u64 = 253_402_300_800-1; // last second of year 9999 + #[allow(unused)] + pub const TIMESTAMP: &str = "9999-12-31T23:59:59Z"; +} + +/// Error parsing datetime (timestamp) +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum Error { + /// Numeric component is out of range + OutOfRange, + /// Bad character where digit is expected + InvalidDigit, + /// Other formatting errors + InvalidFormat, +} + +impl StdError for Error {} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::OutOfRange => write!(f, "numeric component is out of range"), + Error::InvalidDigit => write!(f, "bad character where digit is expected"), + Error::InvalidFormat => write!(f, "timestamp format is invalid"), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +enum Precision { + Smart, + Seconds, + Millis, + Micros, + Nanos, +} + +/// A wrapper type that allows you to Display a SystemTime +#[derive(Debug, Clone)] +pub struct Rfc3339Timestamp(SystemTime, Precision); + +#[inline] +fn two_digits(b1: u8, b2: u8) -> Result<u64, Error> { + if b1 < b'0' || b2 < b'0' || b1 > b'9' || b2 > b'9' { + return Err(Error::InvalidDigit); + } + Ok(((b1 - b'0')*10 + (b2 - b'0')) as u64) +} + +/// Parse RFC3339 timestamp `2018-02-14T00:28:07Z` +/// +/// Supported feature: any precision of fractional +/// digits `2018-02-14T00:28:07.133Z`. +/// +/// Unsupported feature: localized timestamps. Only UTC is supported. +pub fn parse_rfc3339(s: &str) -> Result<SystemTime, Error> { + if s.len() < "2018-02-14T00:28:07Z".len() { + return Err(Error::InvalidFormat); + } + let b = s.as_bytes(); + if b[10] != b'T' || b[b.len()-1] != b'Z' { + return Err(Error::InvalidFormat); + } + parse_rfc3339_weak(s) +} + +/// Parse RFC3339-like timestamp `2018-02-14 00:28:07` +/// +/// Supported features: +/// +/// 1. Any precision of fractional digits `2018-02-14 00:28:07.133`. +/// 2. Supports timestamp with or without either of `T` or `Z` +/// 3. Anything valid for `parse_3339` is valid for this function +/// +/// Unsupported feature: localized timestamps. Only UTC is supported, even if +/// `Z` is not specified. +/// +/// This function is intended to use for parsing human input. Whereas +/// `parse_rfc3339` is for strings generated programmatically. +pub fn parse_rfc3339_weak(s: &str) -> Result<SystemTime, Error> { + if s.len() < "2018-02-14T00:28:07".len() { + return Err(Error::InvalidFormat); + } + let b = s.as_bytes(); // for careless slicing + if b[4] != b'-' || b[7] != b'-' || (b[10] != b'T' && b[10] != b' ') || + b[13] != b':' || b[16] != b':' + { + return Err(Error::InvalidFormat); + } + let year = two_digits(b[0], b[1])? * 100 + two_digits(b[2], b[3])?; + let month = two_digits(b[5], b[6])?; + let day = two_digits(b[8], b[9])?; + let hour = two_digits(b[11], b[12])?; + let minute = two_digits(b[14], b[15])?; + let mut second = two_digits(b[17], b[18])?; + + if year < 1970 || hour > 23 || minute > 59 || second > 60 { + return Err(Error::OutOfRange); + } + // TODO(tailhook) should we check that leaps second is only on midnight ? + if second == 60 { + second = 59 + }; + let leap_years = ((year - 1) - 1968) / 4 - ((year - 1) - 1900) / 100 + + ((year - 1) - 1600) / 400; + let leap = is_leap_year(year); + let (mut ydays, mdays) = match month { + 1 => (0, 31), + 2 if leap => (31, 29), + 2 => (31, 28), + 3 => (59, 31), + 4 => (90, 30), + 5 => (120, 31), + 6 => (151, 30), + 7 => (181, 31), + 8 => (212, 31), + 9 => (243, 30), + 10 => (273, 31), + 11 => (304, 30), + 12 => (334, 31), + _ => return Err(Error::OutOfRange), + }; + if day > mdays || day == 0 { + return Err(Error::OutOfRange); + } + ydays += day - 1; + if leap && month > 2 { + ydays += 1; + } + let days = (year - 1970) * 365 + leap_years + ydays; + + let time = second + minute * 60 + hour * 3600; + + let mut nanos = 0; + let mut mult = 100_000_000; + if b.get(19) == Some(&b'.') { + for idx in 20..b.len() { + if b[idx] == b'Z' { + if idx == b.len()-1 { + break; + } else { + return Err(Error::InvalidDigit); + } + } + if b[idx] < b'0' || b[idx] > b'9' { + return Err(Error::InvalidDigit); + } + nanos += mult * (b[idx] - b'0') as u32; + mult /= 10; + } + } else if b.len() != 19 && (b.len() > 20 || b[19] != b'Z') { + return Err(Error::InvalidFormat); + } + + let total_seconds = time + days * 86400; + if total_seconds > max::SECONDS { + return Err(Error::OutOfRange); + } + + Ok(UNIX_EPOCH + Duration::new(total_seconds, nanos)) +} + +fn is_leap_year(y: u64) -> bool { + y % 4 == 0 && (y % 100 != 0 || y % 400 == 0) +} + +/// Format an RFC3339 timestamp `2018-02-14T00:28:07Z` +/// +/// This function formats timestamp with smart precision: i.e. if it has no +/// fractional seconds, they aren't written at all. And up to nine digits if +/// they are. +/// +/// The value is always UTC and ignores system timezone. +pub fn format_rfc3339(system_time: SystemTime) -> Rfc3339Timestamp { + Rfc3339Timestamp(system_time, Precision::Smart) +} + +/// Format an RFC3339 timestamp `2018-02-14T00:28:07Z` +/// +/// This format always shows timestamp without fractional seconds. +/// +/// The value is always UTC and ignores system timezone. +pub fn format_rfc3339_seconds(system_time: SystemTime) -> Rfc3339Timestamp { + Rfc3339Timestamp(system_time, Precision::Seconds) +} + +/// Format an RFC3339 timestamp `2018-02-14T00:28:07.000Z` +/// +/// This format always shows milliseconds even if millisecond value is zero. +/// +/// The value is always UTC and ignores system timezone. +pub fn format_rfc3339_millis(system_time: SystemTime) -> Rfc3339Timestamp { + Rfc3339Timestamp(system_time, Precision::Millis) +} + +/// Format an RFC3339 timestamp `2018-02-14T00:28:07.000000Z` +/// +/// This format always shows microseconds even if microsecond value is zero. +/// +/// The value is always UTC and ignores system timezone. +pub fn format_rfc3339_micros(system_time: SystemTime) -> Rfc3339Timestamp { + Rfc3339Timestamp(system_time, Precision::Micros) +} + +/// Format an RFC3339 timestamp `2018-02-14T00:28:07.000000000Z` +/// +/// This format always shows nanoseconds even if nanosecond value is zero. +/// +/// The value is always UTC and ignores system timezone. +pub fn format_rfc3339_nanos(system_time: SystemTime) -> Rfc3339Timestamp { + Rfc3339Timestamp(system_time, Precision::Nanos) +} + +impl Rfc3339Timestamp { + /// Returns a reference to the [`SystemTime`][] that is being formatted. + pub fn get_ref(&self) -> &SystemTime { + &self.0 + } +} + +impl fmt::Display for Rfc3339Timestamp { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::Precision::*; + + let dur = self.0.duration_since(UNIX_EPOCH) + .expect("all times should be after the epoch"); + let secs_since_epoch = dur.as_secs(); + let nanos = dur.subsec_nanos(); + + if secs_since_epoch >= 253_402_300_800 { // year 9999 + return Err(fmt::Error); + } + + /* 2000-03-01 (mod 400 year, immediately after feb29 */ + const LEAPOCH: i64 = 11017; + const DAYS_PER_400Y: i64 = 365*400 + 97; + const DAYS_PER_100Y: i64 = 365*100 + 24; + const DAYS_PER_4Y: i64 = 365*4 + 1; + + let days = (secs_since_epoch / 86400) as i64 - LEAPOCH; + let secs_of_day = secs_since_epoch % 86400; + + let mut qc_cycles = days / DAYS_PER_400Y; + let mut remdays = days % DAYS_PER_400Y; + + if remdays < 0 { + remdays += DAYS_PER_400Y; + qc_cycles -= 1; + } + + let mut c_cycles = remdays / DAYS_PER_100Y; + if c_cycles == 4 { c_cycles -= 1; } + remdays -= c_cycles * DAYS_PER_100Y; + + let mut q_cycles = remdays / DAYS_PER_4Y; + if q_cycles == 25 { q_cycles -= 1; } + remdays -= q_cycles * DAYS_PER_4Y; + + let mut remyears = remdays / 365; + if remyears == 4 { remyears -= 1; } + remdays -= remyears * 365; + + let mut year = 2000 + + remyears + 4*q_cycles + 100*c_cycles + 400*qc_cycles; + + let months = [31,30,31,30,31,31,30,31,30,31,31,29]; + let mut mon = 0; + for mon_len in months.iter() { + mon += 1; + if remdays < *mon_len { + break; + } + remdays -= *mon_len; + } + let mday = remdays+1; + let mon = if mon + 2 > 12 { + year += 1; |