Bug 1951517 - Update Rust chrono crate to 0.4.40. r=glandium,supply-chain-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D245369
This commit is contained in:
49
Cargo.lock
generated
49
Cargo.lock
generated
@@ -54,6 +54,12 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "android_log-sys"
|
||||
version = "0.2.0"
|
||||
@@ -782,16 +788,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.19"
|
||||
version = "0.4.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
|
||||
checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"num-integer",
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"time 0.1.45",
|
||||
"winapi",
|
||||
"wasm-bindgen",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2973,6 +2980,30 @@ dependencies = [
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_calendar"
|
||||
version = "1.5.2"
|
||||
@@ -7655,6 +7686,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.2.0"
|
||||
|
||||
@@ -646,6 +646,12 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.7.0 -> 0.8.1"
|
||||
|
||||
[[audits.android-tzdata]]
|
||||
who = "Mark Hammond <mhammond@skippinet.com.au>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.1.1"
|
||||
notes = "Small crate parsing a file. No unsafe code"
|
||||
|
||||
[[audits.android_logger]]
|
||||
who = "Jan-Erik Rediger <jrediger@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
@@ -1159,6 +1165,12 @@ who = "Bobby Holley <bobbyholley@gmail.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.1.2 -> 0.1.2@git:ed8a4c6f900a90d4dbc1d64b856e61490a1c3570"
|
||||
|
||||
[[audits.chrono]]
|
||||
who = "Mark Hammond <mhammond@skippinet.com.au>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.4.19 -> 0.4.40"
|
||||
notes = "Significant refactor of both implementation and dependencies."
|
||||
|
||||
[[audits.circular]]
|
||||
who = "Alex Franchuk <afranchuk@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
@@ -2728,6 +2740,11 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-run"
|
||||
delta = "0.14.23 -> 0.14.24"
|
||||
|
||||
[[audits.iana-time-zone]]
|
||||
who = "Mark Hammond <mhammond@skippinet.com.au>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.1.61 -> 0.1.63"
|
||||
|
||||
[[audits.icu_calendar]]
|
||||
who = "André Bargull <andre.bargull@gmail.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
@@ -6233,6 +6250,12 @@ criteria = "safe-to-deploy"
|
||||
delta = "0.1.2 -> 0.3.1"
|
||||
notes = "Maintained by me. I have written or reviewed all of the code."
|
||||
|
||||
[[audits.windows-link]]
|
||||
who = "Mark Hammond <mhammond@skippinet.com.au>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.1.1"
|
||||
notes = "A microsoft crate allowing unsafe calls to windows apis."
|
||||
|
||||
[[audits.winreg]]
|
||||
who = "Ray Kraesig <rkraesig@mozilla.com>"
|
||||
criteria = "safe-to-run"
|
||||
|
||||
@@ -1202,6 +1202,11 @@ criteria = "safe-to-deploy"
|
||||
delta = "0.4.1 -> 0.5.0"
|
||||
notes = "Minor changes for a `no_std` upgrade but otherwise everything looks as expected."
|
||||
|
||||
[[audits.bytecode-alliance.audits.iana-time-zone-haiku]]
|
||||
who = "Dan Gohman <dev@sunfishcode.online>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.1.2"
|
||||
|
||||
[[audits.bytecode-alliance.audits.id-arena]]
|
||||
who = "Nick Fitzgerald <fitzgen@gmail.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
@@ -1705,6 +1710,13 @@ criteria = "safe-to-run"
|
||||
version = "0.14.20"
|
||||
aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT"
|
||||
|
||||
[[audits.google.audits.iana-time-zone]]
|
||||
who = "Manish Goregaokar <manishearth@google.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.1.61"
|
||||
notes = "Some unsafe: interfacing with system timezone APIs"
|
||||
aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
|
||||
|
||||
[[audits.google.audits.indexmap]]
|
||||
who = "Lukasz Anforowicz <lukasza@chromium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
1
third_party/rust/android-tzdata/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/android-tzdata/.cargo-checksum.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"files":{"Cargo.toml":"a87d9acc9827a50c7a96a88720c5dd055cbc08b1144dff95bd572ff977d4a79a","LICENSE-APACHE":"4458503dd48e88c4e0b945fb252a08b93c40ec757309b8ffa7c594dfa1e35104","LICENSE-MIT":"002c2696d92b5c8cf956c11072baa58eaf9f6ade995c031ea635c6a1ee342ad1","README.md":"6dfe0c602dc61eebe118900ed66a2c1f7887b9fe95b36e1c2974c4e8fa7ebd4b","src/lib.rs":"8f421233df83f82e737930ca8a2ad254966334183148bcc170f9c405df230de2","src/tzdata.rs":"78920925b04219910511e9a1f036f468cd2925c0054f280d6a00b106529046e7"},"package":"e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"}
|
||||
34
third_party/rust/android-tzdata/Cargo.toml
vendored
Normal file
34
third_party/rust/android-tzdata/Cargo.toml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
authors = ["RumovZ"]
|
||||
include = [
|
||||
"src/**/*",
|
||||
"LICENSE-*",
|
||||
"README.md",
|
||||
]
|
||||
description = "Parser for the Android-specific tzdata file"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"parser",
|
||||
"android",
|
||||
"timezone",
|
||||
]
|
||||
categories = ["date-and-time"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/RumovZ/android-tzdata"
|
||||
|
||||
[dev-dependencies.zip]
|
||||
version = "0.6.4"
|
||||
201
third_party/rust/android-tzdata/LICENSE-APACHE
vendored
Normal file
201
third_party/rust/android-tzdata/LICENSE-APACHE
vendored
Normal file
@@ -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.
|
||||
21
third_party/rust/android-tzdata/LICENSE-MIT
vendored
Normal file
21
third_party/rust/android-tzdata/LICENSE-MIT
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) [year] [fullname]
|
||||
|
||||
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.
|
||||
20
third_party/rust/android-tzdata/README.md
vendored
Normal file
20
third_party/rust/android-tzdata/README.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
# android-tzdata
|
||||
|
||||
Parser for the Android-specific tzdata file.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
- Apache License, Version 2.0
|
||||
([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
- MIT license
|
||||
([LICENSE-MIT](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.
|
||||
29
third_party/rust/android-tzdata/src/lib.rs
vendored
Normal file
29
third_party/rust/android-tzdata/src/lib.rs
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
//! Parser for the Android-specific tzdata file.
|
||||
|
||||
mod tzdata;
|
||||
|
||||
/// Tries to locate the `tzdata` file, parse it, and return the entry for the
|
||||
/// requested time zone.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an [std::io::Error] if the `tzdata` file cannot be found and parsed, or
|
||||
/// if it does not contain the requested timezone entry.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use std::error::Error;
|
||||
/// # use android_tzdata::find_tz_data;
|
||||
/// #
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// let tz_data = find_tz_data("Europe/Kiev")?;
|
||||
/// // Check it's version 2 of the [Time Zone Information Format](https://www.ietf.org/archive/id/draft-murchison-rfc8536bis-02.html).
|
||||
/// assert!(tz_data.starts_with(b"TZif2"));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn find_tz_data(tz_name: impl AsRef<str>) -> Result<Vec<u8>, std::io::Error> {
|
||||
let mut file = tzdata::find_file()?;
|
||||
tzdata::find_tz_data_in_file(&mut file, tz_name.as_ref())
|
||||
}
|
||||
166
third_party/rust/android-tzdata/src/tzdata.rs
vendored
Normal file
166
third_party/rust/android-tzdata/src/tzdata.rs
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
//! Logic was mainly ported from https://android.googlesource.com/platform/libcore/+/jb-mr2-release/luni/src/main/java/libcore/util/ZoneInfoDB.java
|
||||
|
||||
use core::{cmp::Ordering, convert::TryInto};
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, ErrorKind, Read, Seek, SeekFrom},
|
||||
};
|
||||
|
||||
// The database uses 32-bit (4 byte) integers.
|
||||
const TZ_INT_SIZE: usize = 4;
|
||||
// The first 12 bytes contain a special version string.
|
||||
const MAGIC_SIZE: usize = 12;
|
||||
const HEADER_SIZE: usize = MAGIC_SIZE + 3 * TZ_INT_SIZE;
|
||||
// The database reserves 40 bytes for each id.
|
||||
const TZ_NAME_SIZE: usize = 40;
|
||||
const INDEX_ENTRY_SIZE: usize = TZ_NAME_SIZE + 3 * TZ_INT_SIZE;
|
||||
const TZDATA_LOCATIONS: [TzdataLocation; 2] = [
|
||||
TzdataLocation {
|
||||
env_var: "ANDROID_DATA",
|
||||
path: "/misc/zoneinfo/",
|
||||
},
|
||||
TzdataLocation {
|
||||
env_var: "ANDROID_ROOT",
|
||||
path: "/usr/share/zoneinfo/",
|
||||
},
|
||||
];
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TzdataLocation {
|
||||
env_var: &'static str,
|
||||
path: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Header {
|
||||
index_offset: usize,
|
||||
data_offset: usize,
|
||||
_zonetab_offset: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Index(Vec<u8>);
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct IndexEntry<'a> {
|
||||
_name: &'a [u8],
|
||||
offset: usize,
|
||||
length: usize,
|
||||
_raw_utc_offset: usize,
|
||||
}
|
||||
|
||||
pub(super) fn find_file() -> Result<File, io::Error> {
|
||||
for location in &TZDATA_LOCATIONS {
|
||||
if let Ok(env_value) = std::env::var(location.env_var) {
|
||||
if let Ok(file) = File::open(format!("{}{}tzdata", env_value, location.path)) {
|
||||
return Ok(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(io::Error::from(io::ErrorKind::NotFound))
|
||||
}
|
||||
|
||||
pub(super) fn find_tz_data_in_file(
|
||||
mut file: impl Read + Seek,
|
||||
tz_name: &str,
|
||||
) -> Result<Vec<u8>, io::Error> {
|
||||
let header = Header::new(&mut file)?;
|
||||
let index = Index::new(&mut file, header)?;
|
||||
if let Some(entry) = index.find_entry(tz_name) {
|
||||
file.seek(SeekFrom::Start((entry.offset + header.data_offset) as u64))?;
|
||||
let mut tz_data = vec![0u8; entry.length];
|
||||
file.read_exact(&mut tz_data)?;
|
||||
Ok(tz_data)
|
||||
} else {
|
||||
Err(io::Error::from(ErrorKind::NotFound))
|
||||
}
|
||||
}
|
||||
|
||||
impl Header {
|
||||
fn new(mut file: impl Read + Seek) -> Result<Self, io::Error> {
|
||||
let mut buf = [0; HEADER_SIZE];
|
||||
file.read_exact(&mut buf)?;
|
||||
if !buf.starts_with(b"tzdata") || buf[MAGIC_SIZE - 1] != 0u8 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"invalid magic number",
|
||||
));
|
||||
}
|
||||
Ok(Self {
|
||||
index_offset: parse_tz_int(&buf, MAGIC_SIZE) as usize,
|
||||
data_offset: parse_tz_int(&buf, MAGIC_SIZE + TZ_INT_SIZE) as usize,
|
||||
_zonetab_offset: parse_tz_int(&buf, MAGIC_SIZE + 2 * TZ_INT_SIZE) as usize,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Index {
|
||||
fn new(mut file: impl Read + Seek, header: Header) -> Result<Self, io::Error> {
|
||||
file.seek(SeekFrom::Start(header.index_offset as u64))?;
|
||||
let size = header.data_offset - header.index_offset;
|
||||
let mut bytes = vec![0; size];
|
||||
file.read_exact(&mut bytes)?;
|
||||
Ok(Self(bytes))
|
||||
}
|
||||
|
||||
fn find_entry(&self, name: &str) -> Option<IndexEntry> {
|
||||
let name_bytes = name.as_bytes();
|
||||
let name_len = name_bytes.len();
|
||||
if name_len > TZ_NAME_SIZE {
|
||||
return None;
|
||||
}
|
||||
|
||||
let zeros = [0u8; TZ_NAME_SIZE];
|
||||
let cmp = |chunk: &&[u8]| -> Ordering {
|
||||
// tz names always have TZ_NAME_SIZE bytes and are right-padded with 0s
|
||||
// so we check that a chunk starts with `name` and the remaining bytes are 0
|
||||
chunk[..name_len]
|
||||
.cmp(name_bytes)
|
||||
.then_with(|| chunk[name_len..TZ_NAME_SIZE].cmp(&zeros[name_len..]))
|
||||
};
|
||||
|
||||
let chunks: Vec<_> = self.0.chunks_exact(INDEX_ENTRY_SIZE).collect();
|
||||
chunks
|
||||
.binary_search_by(cmp)
|
||||
.map(|idx| IndexEntry::new(chunks[idx]))
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IndexEntry<'a> {
|
||||
fn new(bytes: &'a [u8]) -> Self {
|
||||
Self {
|
||||
_name: bytes[..TZ_NAME_SIZE]
|
||||
.splitn(2, |&b| b == 0u8)
|
||||
.next()
|
||||
.unwrap(),
|
||||
offset: parse_tz_int(bytes, TZ_NAME_SIZE) as usize,
|
||||
length: parse_tz_int(bytes, TZ_NAME_SIZE + TZ_INT_SIZE) as usize,
|
||||
_raw_utc_offset: parse_tz_int(bytes, TZ_NAME_SIZE + 2 * TZ_INT_SIZE) as usize,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Panics if slice does not contain [TZ_INT_SIZE] bytes beginning at start.
|
||||
fn parse_tz_int(slice: &[u8], start: usize) -> u32 {
|
||||
u32::from_be_bytes(slice[start..start + TZ_INT_SIZE].try_into().unwrap())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::fs::File;
|
||||
use std::io::Cursor;
|
||||
|
||||
#[test]
|
||||
fn parse() {
|
||||
let mut archive = File::open("tests/resources/tzdata.zip").unwrap();
|
||||
let mut zip = zip::ZipArchive::new(&mut archive).unwrap();
|
||||
let mut file = zip.by_index(0).unwrap();
|
||||
let mut data = Vec::new();
|
||||
file.read_to_end(&mut data).unwrap();
|
||||
let cursor = Cursor::new(data);
|
||||
let tz = find_tz_data_in_file(cursor, "Europe/Kiev").unwrap();
|
||||
assert!(tz.starts_with(b"TZif2"));
|
||||
}
|
||||
}
|
||||
2
third_party/rust/chrono/.cargo-checksum.json
vendored
2
third_party/rust/chrono/.cargo-checksum.json
vendored
@@ -1 +1 @@
|
||||
{"files":{"AUTHORS.txt":"bcca43c486176e81edcb64d065d418986cb5ca71af7ee4a648236a644305960d","CHANGELOG.md":"a925f71022f1c9e34e5df7c016ab2a32e9be22fc503929aa87d567900ebe064b","Cargo.toml":"13fab9c53580e2754c4fa075768ee9591e8d4b4a437ac9dbae503c4d4b6daf09","LICENSE.txt":"46610329ff0b38effb9cb05979ff1ef761e465fed96b2eaca39e439d00129fd7","README.md":"39985917c9bc71ee87a24808369ddfab22f3c6343da7c87c2274c965e1eec9db","benches/chrono.rs":"4de07b4c7bc907926e5a6ed20fc00a30e4e0491604f3d78eece81f1a8b45870a","benches/serde.rs":"e1a9624bcad4892c4cc7b76d5ad14607a016305a27b2231c2c77814b1d4448a8","rustfmt.toml":"f74204a6f92aa7422a16ecb2ffe2d5bae0f123b778d08b5db1a398a3c9ca4306","src/date.rs":"ec234e777efa9d8cd2f0c7f87db9296eda04bd4d56c994232a3329eb863acd34","src/datetime.rs":"63e582cd17f3070cbcb586bea15b5102ead1a898252e14b1817a498750043102","src/div.rs":"6c0a08648bb688b418b42f1c7be8387057a9db9774d3f09e97df2a183b79cd9f","src/format/locales.rs":"8c14cb93c68b747947b74ab2933aae53c8a0edd009ff16815e756015fbea6e9f","src/format/mod.rs":"09c66dee24e69325ce9a7be5b6c830317515ee37f7c2a26f48835484ed155c89","src/format/parse.rs":"c98217756370c6186bdab117373912208c018a2c6411f9be0fd1aecab54fb10c","src/format/parsed.rs":"6d4453a5fedc753fae268e0f545fcc817d48f9ce6803231d1c83aeb1f626c138","src/format/scan.rs":"1846554a45c776d164239ec6874881c6c97468631a1c00c8c956630420ea408f","src/format/strftime.rs":"8bbe43ca06c8a5e71187431890a54ff1faeb3be990c0d9d8c2fd8ee8b5c1361f","src/lib.rs":"522163d278acefa80fd1af4afeec2fdd2648e13e3fbdde8e6d31214253b013f9","src/naive/date.rs":"8be6146b3c15c395a71d30449a799b6e4387d1cd515e7e849509b824161fa6e3","src/naive/datetime.rs":"fd0de90d13793e5a8ecf99f63fa27592a6add57ba39a116495f9b2b253323fce","src/naive/internals.rs":"8c1aa5ab3373e04b51d36ed1591d667e1450055208b8c44d3fb720a496722c57","src/naive/isoweek.rs":"a8c5ae43ee1b916a2f79d42230aea448bf85691d615c4d271fcf1809e763df01","src/naive/time.rs":"b83e4ae0a809badce9131a19e3c5c75dbb823db4ef2f79d50bd522126ba0b48f","src/offset/fixed.rs":"19b97271300b821407756e14f64a42d48eb25a71c7011b2967885e4946e089ef","src/offset/local.rs":"9bc3af0ebf35a49858647302ac7e44abe344cbb76819d273311e50d234d1e6b2","src/offset/mod.rs":"a1036f75fc686603b216f9bb45b1689c8b34198b617b204800ceafb87b66ec45","src/offset/utc.rs":"2940ade0e834a9e1247600e92d38ee7bb11653f2d267862f99f292f617e25ca4","src/oldtime.rs":"780bc4ae5652affa8f7020580bb5977e9f32b304b0905c742124fd87a82ae50e","src/round.rs":"f7ae453ec0caacffc23bc0bad38b2c59b616c097ccaa0a15c0e7bcb8d1e1aed3","src/sys.rs":"4a3e8a96a2060e7df82c57405a5de4fffa54132a77fdd919970d51f3c43442cb","src/sys/stub.rs":"78babcdbe867ce5978bd69935e141ee15313ee7d90edce52355a19ab906a019b","src/sys/unix.rs":"c838ba088423c2b26643bd7191fe1914f099c14d632c9874f236c9c2c9dbd2d6","src/sys/windows.rs":"5c19383e9ffe11c16102008b5984dbd3455fd4003df15ac1565720c0a5edfac8","tests/wasm.rs":"d152681d5a79d9bbb69684433388bb8d7ca90e181387d4690cf5bda8cf25d17f"},"package":"670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"}
|
||||
{"files":{"CITATION.cff":"6eb81794e04ee1d4bb56dd2767a2d5c6e2200f35f5d976437d7e299059494c66","Cargo.lock":"2b2fabd7ad614c4e3f0cbeaa2de479bd489ce477b7a28640d5b6bb5ef5038557","Cargo.toml":"41cec533b67eba086cd152582c428f2cd5baa5f29b0a5783d4c2f7ea4978190e","LICENSE.txt":"a3d4e565d73d108b07074757733e1dcd3ee20617350a135e1122935881311d50","src/date.rs":"10f03590ccab03c3efc076ef55bf218f1e93fdd5f85e4440f46b7590373619b1","src/datetime/mod.rs":"be0e61a845a465fda353fb579948087d9aadad6543313071fd8740f7a2c1f384","src/datetime/serde.rs":"3b9fbeb0b7f269d3162aeb7373e9829b58690603860ec5e23cdebb63a06efca2","src/datetime/tests.rs":"602dfd5b78c3aa7e6b30b0567f070e08d485649ce02bb40a511d1710f7301e63","src/format/formatting.rs":"0a3286b78a0c0b08adedbbd06c1894ce3ace1cf919db80cfe3ba603f7604aaeb","src/format/locales.rs":"d49de3da43941c94b1108f8fe84915cc5e1c13a1f1c84fd7356f9b7c61f19242","src/format/mod.rs":"df752a29ae2293fa71d75374b070925b1874198cc6ddbf5bcd717b26457bf44a","src/format/parse.rs":"b9d57bf8174b453864ed373d1ebb328202c0f67bd727a4270f430c088c311017","src/format/parsed.rs":"8d653af39d192a4c5234e36f72880757c9ff55ff55e979ec23c665b2fd6f34a5","src/format/scan.rs":"687117a94f7c825a8dd9f6462f9a6dbc400adaf13774d0fb8cdf025598d21cd2","src/format/strftime.rs":"066b8bea999aa69e760c05c17b683a0a411612b28a5bbf645eb8f9307fc708e4","src/lib.rs":"ea193c5d2214424137ffeb42fde6024f6003ed952525bcea331f1700bd6fad64","src/month.rs":"0f174e6b0047a1138f1713e596889097228eed89e267aa744bfecdd0a1efc5e4","src/naive/date/mod.rs":"4076847fa7926dfd70843de2f0322790354cb1cd4c9fd2e62455bacc4f00b318","src/naive/date/tests.rs":"d90f09052d4b4bbab760450d37b19ca698153a10e27249d05f3c11dc297a3e62","src/naive/datetime/mod.rs":"1465f96a1ed702e1080bc0129e84d435960987039f6425b9f7d09c0dcb8c2241","src/naive/datetime/serde.rs":"eeb8e5a6ea9632799673a6f9e4b72de0ae606ada29b20cbc1b1dbb0f9312b41c","src/naive/datetime/tests.rs":"f890c8ba06a0426d823ec4ee538d84fe38ccba6e3bd1503442cca9853186d3e5","src/naive/internals.rs":"6e2da15f601a1d15742e2ac8eaf0e23aaf61a3c92480c749c86c460a56907701","src/naive/isoweek.rs":"5c253901e2ee50316839e5f2490c59f5ca3c714d7523e01406cbdfc4e5bc6e7e","src/naive/mod.rs":"3d13c4e36a4d9f7a759fd2edf11c6e9e0b54c0c4380a671281964fe3e0315101","src/naive/time/mod.rs":"305c105fb5837b12f773aa6b90e9973bfe90895a97d0d2a20a67c704cbbf64c8","src/naive/time/serde.rs":"6c260c6d0c45dec113f3dcff6056450c524511c37f7c30da9324ad80aff2288b","src/naive/time/tests.rs":"70143375785969ed348fcc1ceab50b670d239209191b938823dd7b25a97ced40","src/offset/fixed.rs":"cfd1c9d6ffedb9dd219e26b966e5bdd4fa52d24f3fdb598142822cb6f8b51388","src/offset/local/mod.rs":"d27850947cfb649ada0bce052136e01e27035f06f8753adcb4f7bfc0c3332e40","src/offset/local/tz_info/mod.rs":"c8096637c04687ea396e444c25fdf0008e2b9975ab3c173a41dd63ac213c7877","src/offset/local/tz_info/parser.rs":"ec21d8739a86fb4e77551733e13af9964fbc01f80c87d7a164f6185ca9928c22","src/offset/local/tz_info/rule.rs":"bfc9e6257aeaaa23edef69b00acee58233846702eb969b8011d1b2425d15d10c","src/offset/local/tz_info/timezone.rs":"91863819f1b17d73682f62a46d7420a3390088075f0c93577e42e048fafe5a6f","src/offset/local/unix.rs":"3d701d5d37be23b90ac97d16832553ff6c458e98d1e293616528d85885db8c57","src/offset/local/win_bindings.rs":"a7e705048f99c594fcb5eca2ed619b9ceec6ac4f00d4bf21dc132d0a8e079281","src/offset/local/win_bindings.txt":"d680de1e8fd07d435d20143877dcb3d719d72e24ff7fe5baae97e956c24f7fed","src/offset/local/windows.rs":"e9909f06e84c6334433fe24e1db14ca61c116889df9db6903917a3083fdd4606","src/offset/mod.rs":"ab1021bd280e921c46fa28ae64e1ba5a30522a5f1dcdc5ebfe384c1c8a721f75","src/offset/utc.rs":"4b5449427cebf609cb5bbcc07ae70faeb3eab4f742d5a6a521e9118227d6eb2d","src/round.rs":"81f4cb707e723256429daf7a15d8d4f510f6f6baa892dc220fa2862da7f24e08","src/time_delta.rs":"b62f63d0ed22a55b2dab53fce16cc36db4c8b73ad0f4fb2fdc536b970c89ea27","src/traits.rs":"14ecbb8c4faaef20aea69a24f4df61d472d87771fef57ae990b10bf950a46315","src/weekday.rs":"6f8374bf39300fbf08ce6b3f64247afb29b2da9d60b5ff78f72b201b05272305","tests/dateutils.rs":"905e0f9f8d07f7cbee1b1a36369a1402e4cf62370b52dda1557c563b7a0de758","tests/wasm.rs":"252f16aeeaacbf26ca268b9a5e53aee3560cd1f071142a7a16d1509a02758a17","tests/win_bindings.rs":"62206936e3bd2ae1cc889c45d65d6182f553dc6c2b29c83cb0d897b2406a9040"},"package":"1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"}
|
||||
41
third_party/rust/chrono/AUTHORS.txt
vendored
41
third_party/rust/chrono/AUTHORS.txt
vendored
@@ -1,41 +0,0 @@
|
||||
Chrono is mainly written by Kang Seonghoon <public+rust@mearie.org>,
|
||||
and also the following people (in ascending order):
|
||||
|
||||
Alex Mikhalev <alexmikhalevalex@gmail.com>
|
||||
Alexander Bulaev <alexbool@yandex-team.ru>
|
||||
Ashley Mannix <ashleymannix@live.com.au>
|
||||
Ben Boeckel <mathstuf@gmail.com>
|
||||
Ben Eills <ben@beneills.com>
|
||||
Brandon W Maister <bwm@knewton.com>
|
||||
Brandon W Maister <quodlibetor@gmail.com>
|
||||
Cecile Tonglet <cecile.tonglet@cecton.com>
|
||||
Colin Ray <r.colinray@gmail.com>
|
||||
Corey Farwell <coreyf@rwell.org>
|
||||
Dan <dan@ebip.co.uk>
|
||||
Danilo Bargen <mail@dbrgn.ch>
|
||||
David Hewson <dev@daveid.co.uk>
|
||||
David Ross <daboross@daboross.net>
|
||||
David Tolnay <dtolnay@gmail.com>
|
||||
David Willie <david.willie.1@gmail.com>
|
||||
Eric Findlay <e.findlay@protonmail.ch>
|
||||
Eunchong Yu <kroisse@gmail.com>
|
||||
Frans Skarman <frans.skarman@gmail.com>
|
||||
Huon Wilson <dbau.pp+github@gmail.com>
|
||||
Igor Gnatenko <ignatenko@redhat.com>
|
||||
Jim Turner <jturner314@gmail.com>
|
||||
Jisoo Park <xxxyel@gmail.com>
|
||||
Joe Wilm <joe@jwilm.com>
|
||||
John Heitmann <jheitmann@gmail.com>
|
||||
John Nagle <nagle@sitetruth.com>
|
||||
Jonas mg <jonasmg@yepmail.net>
|
||||
János Illés <ijanos@gmail.com>
|
||||
Ken Tossell <ken@tossell.net>
|
||||
Martin Risell Lilja <martin.risell.lilja@gmail.com>
|
||||
Richard Petrie <rap1011@ksu.edu>
|
||||
Ryan Lewis <ryansname@gmail.com>
|
||||
Sergey V. Shadoy <shadoysv@yandex.ru>
|
||||
Sergey V. Galtsev <sergey.v.galtsev@github.com>
|
||||
Steve Klabnik <steve@steveklabnik.com>
|
||||
Tom Gallacher <tomgallacher23@gmail.com>
|
||||
klutzy <klutzytheklutzy@gmail.com>
|
||||
kud1ing <github@kudling.de>
|
||||
740
third_party/rust/chrono/CHANGELOG.md
vendored
740
third_party/rust/chrono/CHANGELOG.md
vendored
@@ -1,740 +0,0 @@
|
||||
ChangeLog for Chrono
|
||||
====================
|
||||
|
||||
This documents all notable changes to [Chrono](https://github.com/chronotope/chrono).
|
||||
|
||||
Chrono obeys the principle of [Semantic Versioning](http://semver.org/), with one caveat: we may
|
||||
move previously-existing code behind a feature gate and put it behind a new feature. This new
|
||||
feature will always be placed in the `previously-default` feature, which you can use to prevent
|
||||
breakage if you use `no-default-features`.
|
||||
|
||||
There were/are numerous minor versions before 1.0 due to the language changes.
|
||||
Versions with only mechanical changes will be omitted from the following list.
|
||||
|
||||
## 0.4.20 (unreleased)
|
||||
|
||||
## 0.4.19
|
||||
|
||||
* Correct build on solaris/illumos
|
||||
|
||||
## 0.4.18
|
||||
|
||||
* Restore support for x86_64-fortanix-unknown-sgx
|
||||
|
||||
## 0.4.17
|
||||
|
||||
* Fix a name resolution error in wasm-bindgen code introduced by removing the dependency on time
|
||||
v0.1
|
||||
|
||||
## 0.4.16
|
||||
|
||||
### Features
|
||||
|
||||
* Add %Z specifier to the `FromStr`, similar to the glibc strptime
|
||||
(does not set the offset from the timezone name)
|
||||
|
||||
* Drop the dependency on time v0.1, which is deprecated, unless the `oldtime`
|
||||
feature is active. This feature is active by default in v0.4.16 for backwards
|
||||
compatibility, but will likely be removed in v0.5. Code that imports
|
||||
`time::Duration` should be switched to import `chrono::Duration` instead to
|
||||
avoid breakage.
|
||||
|
||||
## 0.4.15
|
||||
|
||||
### Fixes
|
||||
|
||||
* Correct usage of vec in specific feature combinations (@quodlibetor)
|
||||
|
||||
## 0.4.14 **YANKED**
|
||||
|
||||
### Features
|
||||
|
||||
* Add day and week iterators for `NaiveDate` (@gnzlbg & @robyoung)
|
||||
* Add a `Month` enum (@hhamana)
|
||||
* Add `locales`. All format functions can now use locales, see the documentation for the
|
||||
`unstable-locales` feature.
|
||||
* Fix `Local.from_local_datetime` method for wasm
|
||||
|
||||
### Improvements
|
||||
|
||||
* Added MIN and MAX values for `NaiveTime`, `NaiveDateTime` and `DateTime<Utc>`.
|
||||
|
||||
## 0.4.13
|
||||
|
||||
### Features
|
||||
|
||||
* Add `DurationRound` trait that allows rounding and truncating by `Duration` (@robyoung)
|
||||
|
||||
### Internal Improvements
|
||||
|
||||
* Code improvements to impl `From` for `js_sys` in wasm to reuse code (@schrieveslaach)
|
||||
|
||||
## 0.4.12
|
||||
|
||||
### New Methods and impls
|
||||
|
||||
* `Duration::abs` to ensure that a duration is just a magnitude (#418 @abreis).
|
||||
|
||||
### Compatibility improvements
|
||||
|
||||
* impl `From` for `js_sys` in wasm (#424 @schrieveslaach)
|
||||
* Bump required version of `time` for redox support.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* serde modules do a better job with `Option` types (#417 @mwkroening and #429
|
||||
@fx-kirin)
|
||||
* Use js runtime when using wasmbind to get the local offset (#412
|
||||
@quodlibetor)
|
||||
|
||||
### Internal Improvements
|
||||
|
||||
* Migrate to github actions from travis-ci, make the overall CI experience more comprehensible,
|
||||
significantly faster and more correct (#439 @quodlibetor)
|
||||
|
||||
## 0.4.11
|
||||
|
||||
### Improvements
|
||||
|
||||
* Support a space or `T` in `FromStr` for `DateTime<Tz>`, meaning that e.g.
|
||||
`dt.to_string().parse::<DateTime<Utc>>()` now correctly works on round-trip.
|
||||
(@quodlibetor in #378)
|
||||
* Support "negative UTC" in `parse_from_rfc2822` (@quodlibetor #368 reported in
|
||||
#102)
|
||||
* Support comparisons of DateTimes with different timezones (@dlalic in #375)
|
||||
* Many documentation improvements
|
||||
|
||||
### Bitrot and external integration fixes
|
||||
|
||||
* Don't use wasmbind on wasi (@coolreader18 #365)
|
||||
* Avoid deprecation warnings for `Error::description` (@AnderEnder and
|
||||
@quodlibetor #376)
|
||||
|
||||
### Internal improvements
|
||||
|
||||
* Use Criterion for benchmarks (@quodlibetor)
|
||||
|
||||
## 0.4.10
|
||||
|
||||
### Compatibility notes
|
||||
|
||||
* Putting some functionality behind an `alloc` feature to improve no-std
|
||||
support (in #341) means that if you were relying on chrono with
|
||||
`no-default-features` *and* using any of the functions that require alloc
|
||||
support (i.e. any of the string-generating functions like `to_rfc3339`) you
|
||||
will need to add the `alloc` feature in your Cargo.toml.
|
||||
|
||||
### Improvements
|
||||
|
||||
* `DateTime::parse_from_str` is more than 2x faster in some cases. (@michalsrb
|
||||
#358)
|
||||
* Significant improvements to no-std and alloc support (This should also make
|
||||
many format/serialization operations induce zero unnecessary allocations)
|
||||
(@CryZe #341)
|
||||
|
||||
### Features
|
||||
|
||||
* Functions that were accepting `Iterator` of `Item`s (for example
|
||||
`format_with_items`) now accept `Iterator` of `Borrow<Item>`, so one can
|
||||
use values or references. (@michalsrb #358)
|
||||
* Add built-in support for structs with nested `Option<Datetime>` etc fields
|
||||
(@manifest #302)
|
||||
|
||||
### Internal/doc improvements
|
||||
|
||||
* Use markdown footnotes on the `strftime` docs page (@qudlibetor #359)
|
||||
* Migrate from `try!` -> `?` (question mark) because it is now emitting
|
||||
deprecation warnings and has been stable since rustc 1.13.0
|
||||
* Deny dead code
|
||||
|
||||
## 0.4.9
|
||||
|
||||
### Fixes
|
||||
|
||||
* Make Datetime arithmatic adjust their offsets after discovering their new
|
||||
timestamps (@quodlibetor #337)
|
||||
* Put wasm-bindgen related code and dependencies behind a `wasmbind` feature
|
||||
gate. (@quodlibetor #335)
|
||||
|
||||
## 0.4.8
|
||||
|
||||
### Fixes
|
||||
|
||||
* Add '0' to single-digit days in rfc2822 date format (@wyhaya #323)
|
||||
* Correctly pad DelayedFormat (@SamokhinIlya #320)
|
||||
|
||||
### Features
|
||||
|
||||
* Support `wasm-unknown-unknown` via wasm-bindgen (in addition to
|
||||
emscripten/`wasm-unknown-emscripten`). (finished by @evq in #331, initial
|
||||
work by @jjpe #287)
|
||||
|
||||
## 0.4.7
|
||||
|
||||
### Fixes
|
||||
|
||||
* Disable libc default features so that CI continues to work on rust 1.13
|
||||
* Fix panic on negative inputs to timestamp_millis (@cmars #292)
|
||||
* Make `LocalResult` `Copy/Eq/Hash`
|
||||
|
||||
### Features
|
||||
|
||||
* Add `std::convert::From` conversions between the different timezone formats
|
||||
(@mqudsi #271)
|
||||
* Add `timestamp_nanos` methods (@jean-airoldie #308)
|
||||
* Documentation improvements
|
||||
|
||||
## 0.4.6
|
||||
|
||||
### Maintenance
|
||||
|
||||
* Doc improvements -- improve README CI verification, external links
|
||||
* winapi upgrade to 0.3
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Features
|
||||
|
||||
* Added `NaiveDate::from_weekday_of_month{,_opt}` for getting eg. the 2nd Friday of March 2017.
|
||||
|
||||
## 0.4.5
|
||||
|
||||
### Features
|
||||
|
||||
* Added several more serde deserialization helpers (@novacrazy #258)
|
||||
* Enabled all features on the playground (@davidtwco #267)
|
||||
* Derive `Hash` on `FixedOffset` (@LuoZijun #254)
|
||||
* Improved docs (@storyfeet #261, @quodlibetor #252)
|
||||
|
||||
## 0.4.4
|
||||
|
||||
### Features
|
||||
|
||||
* Added support for parsing nanoseconds without the leading dot (@emschwartz #251)
|
||||
|
||||
## 0.4.3
|
||||
|
||||
### Features
|
||||
|
||||
* Added methods to DateTime/NaiveDateTime to present the stored value as a number
|
||||
of nanoseconds since the UNIX epoch (@harkonenbade #247)
|
||||
* Added a serde serialise/deserialise module for nanosecond timestamps. (@harkonenbade #247)
|
||||
* Added "Permissive" timezone parsing which allows a numeric timezone to
|
||||
be specified without minutes. (@quodlibetor #242)
|
||||
|
||||
## 0.4.2
|
||||
|
||||
### Deprecations
|
||||
|
||||
* More strongly deprecate RustcSerialize: remove it from documentation unless
|
||||
the feature is enabled, issue a deprecation warning if the rustc-serialize
|
||||
feature is enabled (@quodlibetor #174)
|
||||
|
||||
### Features
|
||||
|
||||
* Move all uses of the system clock behind a `clock` feature, for use in
|
||||
environments where we don't have access to the current time. (@jethrogb #236)
|
||||
* Implement subtraction of two `Date`s, `Time`s, or `DateTime`s, returning a
|
||||
`Duration` (@tobz1000 #237)
|
||||
|
||||
## 0.4.1
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Allow parsing timestamps with subsecond precision (@jonasbb)
|
||||
* RFC2822 allows times to not include the second (@upsuper)
|
||||
|
||||
### Features
|
||||
|
||||
* New `timestamp_millis` method on `DateTime` and `NaiveDateTim` that returns
|
||||
number of milliseconds since the epoch. (@quodlibetor)
|
||||
* Support exact decimal width on subsecond display for RFC3339 via a new
|
||||
`to_rfc3339_opts` method on `DateTime` (@dekellum)
|
||||
* Use no_std-compatible num dependencies (@cuviper)
|
||||
* Add `SubsecRound` trait that allows rounding to the nearest second
|
||||
(@dekellum)
|
||||
|
||||
### Code Hygiene and Docs
|
||||
|
||||
* Docs! (@alatiera @kosta @quodlibetor @kennytm)
|
||||
* Run clippy and various fixes (@quodlibetor)
|
||||
|
||||
## 0.4.0 (2017-06-22)
|
||||
|
||||
This was originally planned as a minor release but was pushed to a major
|
||||
release due to the compatibility concern raised.
|
||||
|
||||
### Added
|
||||
|
||||
- `IsoWeek` has been added for the ISO week without time zone.
|
||||
|
||||
- The `+=` and `-=` operators against `time::Duration` are now supported for
|
||||
`NaiveDate`, `NaiveTime` and `NaiveDateTime`. (#99)
|
||||
|
||||
(Note that this does not invalidate the eventual deprecation of `time::Duration`.)
|
||||
|
||||
- `SystemTime` and `DateTime<Tz>` types can be now converted to each other via `From`.
|
||||
Due to the obvious lack of time zone information in `SystemTime`,
|
||||
the forward direction is limited to `DateTime<Utc>` and `DateTime<Local>` only.
|
||||
|
||||
### Changed
|
||||
|
||||
- Intermediate implementation modules have been flattened (#161),
|
||||
and `UTC` has been renamed to `Utc` in accordance with the current convention (#148).
|
||||
|
||||
The full list of changes is as follows:
|
||||
|
||||
Before | After
|
||||
---------------------------------------- | ----------------------------
|
||||
`chrono::date::Date` | `chrono::Date`
|
||||
`chrono::date::MIN` | `chrono::MIN_DATE`
|
||||
`chrono::date::MAX` | `chrono::MAX_DATE`
|
||||
`chrono::datetime::DateTime` | `chrono::DateTime`
|
||||
`chrono::naive::time::NaiveTime` | `chrono::naive::NaiveTime`
|
||||
`chrono::naive::date::NaiveDate` | `chrono::naive::NaiveDate`
|
||||
`chrono::naive::date::MIN` | `chrono::naive::MIN_DATE`
|
||||
`chrono::naive::date::MAX` | `chrono::naive::MAX_DATE`
|
||||
`chrono::naive::datetime::NaiveDateTime` | `chrono::naive::NaiveDateTime`
|
||||
`chrono::offset::utc::UTC` | `chrono::offset::Utc`
|
||||
`chrono::offset::fixed::FixedOffset` | `chrono::offset::FixedOffset`
|
||||
`chrono::offset::local::Local` | `chrono::offset::Local`
|
||||
`chrono::format::parsed::Parsed` | `chrono::format::Parsed`
|
||||
|
||||
With an exception of `Utc`, this change does not affect any direct usage of
|
||||
`chrono::*` or `chrono::prelude::*` types.
|
||||
|
||||
- `Datelike::isoweekdate` is replaced by `Datelike::iso_week` which only returns the ISO week.
|
||||
|
||||
The original method used to return a tuple of year number, week number and day of the week,
|
||||
but this duplicated the `Datelike::weekday` method and it had been hard to deal with
|
||||
the raw year and week number for the ISO week date.
|
||||
This change isolates any logic and API for the week date into a separate type.
|
||||
|
||||
- `NaiveDateTime` and `DateTime` can now be deserialized from an integral UNIX timestamp. (#125)
|
||||
|
||||
This turns out to be very common input for web-related usages.
|
||||
The existing string representation is still supported as well.
|
||||
|
||||
- `chrono::serde` and `chrono::naive::serde` modules have been added
|
||||
for the serialization utilities. (#125)
|
||||
|
||||
Currently they contain the `ts_seconds` modules that can be used to
|
||||
serialize `NaiveDateTime` and `DateTime` values into an integral UNIX timestamp.
|
||||
This can be combined with Serde's `[de]serialize_with` attributes
|
||||
to fully support the (de)serialization to/from the timestamp.
|
||||
|
||||
For rustc-serialize, there are separate `chrono::TsSeconds` and `chrono::naive::TsSeconds` types
|
||||
that are newtype wrappers implementing different (de)serialization logics.
|
||||
This is a suboptimal API, however, and it is strongly recommended to migrate to Serde.
|
||||
|
||||
### Fixed
|
||||
|
||||
- The major version was made to fix the broken Serde dependency issues. (#146, #156, #158, #159)
|
||||
|
||||
The original intention to technically break the dependency was
|
||||
to facilitate the use of Serde 1.0 at the expense of temporary breakage.
|
||||
Whether this was appropriate or not is quite debatable,
|
||||
but it became clear that there are several high-profile crates requiring Serde 0.9
|
||||
and it is not feasible to force them to use Serde 1.0 anyway.
|
||||
|
||||
To the end, the new major release was made with some known lower-priority breaking changes.
|
||||
0.3.1 is now yanked and any remaining 0.3 users can safely roll back to 0.3.0.
|
||||
|
||||
- Various documentation fixes and goodies. (#92, #131, #136)
|
||||
|
||||
## 0.3.1 (2017-05-02)
|
||||
|
||||
### Added
|
||||
|
||||
- `Weekday` now implements `FromStr`, `Serialize` and `Deserialize`. (#113)
|
||||
|
||||
The syntax is identical to `%A`, i.e. either the shortest or the longest form of English names.
|
||||
|
||||
### Changed
|
||||
|
||||
- Serde 1.0 is now supported. (#142)
|
||||
|
||||
This is technically a breaking change because Serde 0.9 and 1.0 are not compatible,
|
||||
but this time we decided not to issue a minor version because
|
||||
we have already seen Serde 0.8 and 0.9 compatibility problems even after 0.3.0 and
|
||||
a new minor version turned out to be not very helpful for this kind of issues.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed a bug that the leap second can be mapped wrongly in the local time zone.
|
||||
Only occurs when the local time zone is behind UTC. (#130)
|
||||
|
||||
## 0.3.0 (2017-02-07)
|
||||
|
||||
The project has moved to the [Chronotope](https://github.com/chronotope/) organization.
|
||||
|
||||
### Added
|
||||
|
||||
- `chrono::prelude` module has been added. All other glob imports are now discouraged.
|
||||
|
||||
- `FixedOffset` can be added to or subtracted from any timelike types.
|
||||
|
||||
- `FixedOffset::local_minus_utc` and `FixedOffset::utc_minus_local` methods have been added.
|
||||
Note that the old `Offset::local_minus_utc` method is gone; see below.
|
||||
|
||||
- Serde support for non-self-describing formats like Bincode is added. (#89)
|
||||
|
||||
- Added `Item::Owned{Literal,Space}` variants for owned formatting items. (#76)
|
||||
|
||||
- Formatting items and the `Parsed` type have been slightly adjusted so that
|
||||
they can be internally extended without breaking any compatibility.
|
||||
|
||||
- `Weekday` is now `Hash`able. (#109)
|
||||
|
||||
- `ParseError` now implements `Eq` as well as `PartialEq`. (#114)
|
||||
|
||||
- More documentation improvements. (#101, #108, #112)
|
||||
|
||||
### Changed
|
||||
|
||||
- Chrono now only supports Rust 1.13.0 or later (previously: Rust 1.8.0 or later).
|
||||
|
||||
- Serde 0.9 is now supported.
|
||||
Due to the API difference, support for 0.8 or older is discontinued. (#122)
|
||||
|
||||
- Rustc-serialize implementations are now on par with corresponding Serde implementations.
|
||||
They both standardize on the `std::fmt::Debug` textual output.
|
||||
|
||||
**This is a silent breaking change (hopefully the last though).**
|
||||
You should be prepared for the format change if you depended on rustc-serialize.
|
||||
|
||||
- `Offset::local_minus_utc` is now `Offset::fix`, and returns `FixedOffset` instead of a duration.
|
||||
|
||||
This makes every time zone operation operate within a bias less than one day,
|
||||
and vastly simplifies many logics.
|
||||
|
||||
- `chrono::format::format` now receives `FixedOffset` instead of `time::Duration`.
|
||||
|
||||
- The following methods and implementations have been renamed and older names have been *removed*.
|
||||
The older names will be reused for the same methods with `std::time::Duration` in the future.
|
||||
|
||||
- `checked_*` → `checked_*_signed` in `Date`, `DateTime`, `NaiveDate` and `NaiveDateTime` types
|
||||
|
||||
- `overflowing_*` → `overflowing_*_signed` in the `NaiveTime` type
|
||||
|
||||
- All subtraction implementations between two time instants have been moved to
|
||||
`signed_duration_since`, following the naming in `std::time`.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed a panic when the `Local` offset receives a leap second. (#123)
|
||||
|
||||
### Removed
|
||||
|
||||
- Rustc-serialize support for `Date<Tz>` types and all offset types has been dropped.
|
||||
|
||||
These implementations were automatically derived and never had been in a good shape.
|
||||
Moreover there are no corresponding Serde implementations, limiting their usefulness.
|
||||
In the future they may be revived with more complete implementations.
|
||||
|
||||
- The following method aliases deprecated in the 0.2 branch have been removed.
|
||||
|
||||
- `DateTime::num_seconds_from_unix_epoch` (→ `DateTime::timestamp`)
|
||||
- `NaiveDateTime::from_num_seconds_from_unix_epoch` (→ `NaiveDateTime::from_timestamp`)
|
||||
- `NaiveDateTime::from_num_seconds_from_unix_epoch_opt` (→ `NaiveDateTime::from_timestamp_opt`)
|
||||
- `NaiveDateTime::num_seconds_unix_epoch` (→ `NaiveDateTime::timestamp`)
|
||||
|
||||
- Formatting items are no longer `Copy`, except for `chrono::format::Pad`.
|
||||
|
||||
- `chrono::offset::add_with_leapsecond` has been removed.
|
||||
Use a direct addition with `FixedOffset` instead.
|
||||
|
||||
## 0.2.25 (2016-08-04)
|
||||
|
||||
This is the last version officially supports Rust 1.12.0 or older.
|
||||
|
||||
(0.2.24 was accidentally uploaded without a proper check for warnings in the default state,
|
||||
and replaced by 0.2.25 very shortly. Duh.)
|
||||
|
||||
### Added
|
||||
|
||||
- Serde 0.8 is now supported. 0.7 also remains supported. (#86)
|
||||
|
||||
### Fixed
|
||||
|
||||
- The deserialization implementation for rustc-serialize now properly verifies the input.
|
||||
All serialization codes are also now thoroughly tested. (#42)
|
||||
|
||||
## 0.2.23 (2016-08-03)
|
||||
|
||||
### Added
|
||||
|
||||
- The documentation was greatly improved for several types,
|
||||
and tons of cross-references have been added. (#77, #78, #80, #82)
|
||||
|
||||
- `DateTime::timestamp_subsec_{millis,micros,nanos}` methods have been added. (#81)
|
||||
|
||||
### Fixed
|
||||
|
||||
- When the system time records a leap second,
|
||||
the nanosecond component was mistakenly reset to zero. (#84)
|
||||
|
||||
- `Local` offset misbehaves in Windows for August and later,
|
||||
due to the long-standing libtime bug (dates back to mid-2015).
|
||||
Workaround has been implemented. (#85)
|
||||
|
||||
## 0.2.22 (2016-04-22)
|
||||
|
||||
### Fixed
|
||||
|
||||
- `%.6f` and `%.9f` used to print only three digits when the nanosecond part is zero. (#71)
|
||||
- The documentation for `%+` has been updated to reflect the current status. (#71)
|
||||
|
||||
## 0.2.21 (2016-03-29)
|
||||
|
||||
### Fixed
|
||||
|
||||
- `Fixed::LongWeekdayName` was unable to recognize `"sunday"` (whoops). (#66)
|
||||
|
||||
## 0.2.20 (2016-03-06)
|
||||
|
||||
### Changed
|
||||
|
||||
- `serde` dependency has been updated to 0.7. (#63, #64)
|
||||
|
||||
## 0.2.19 (2016-02-05)
|
||||
|
||||
### Added
|
||||
|
||||
- The documentation for `Date` is made clear about its ambiguity and guarantees.
|
||||
|
||||
### Fixed
|
||||
|
||||
- `DateTime::date` had been wrong when the local date and the UTC date is in disagreement. (#61)
|
||||
|
||||
## 0.2.18 (2016-01-23)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Chrono no longer pulls a superfluous `rand` dependency. (#57)
|
||||
|
||||
## 0.2.17 (2015-11-22)
|
||||
|
||||
### Added
|
||||
|
||||
- Naive date and time types and `DateTime` now have a `serde` support.
|
||||
They serialize as an ISO 8601 / RFC 3339 string just like `Debug`. (#51)
|
||||
|
||||
## 0.2.16 (2015-09-06)
|
||||
|
||||
### Added
|
||||
|
||||
- Added `%.3f`, `%.6f` and `%.9f` specifier for formatting fractional seconds
|
||||
up to 3, 6 or 9 decimal digits. This is a natural extension to the existing `%f`.
|
||||
Note that this is (not yet) generic, no other value of precision is supported. (#45)
|
||||
|
||||
### Changed
|
||||
|
||||
- Forbade unsized types from implementing `Datelike` and `Timelike`.
|
||||
This does not make a big harm as any type implementing them should be already sized
|
||||
to be practical, but this change still can break highly generic codes. (#46)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed a broken link in the `README.md`. (#41)
|
||||
|
||||
## 0.2.15 (2015-07-05)
|
||||
|
||||
### Added
|
||||
|
||||
- Padding modifiers `%_?`, `%-?` and `%0?` are implemented.
|
||||
They are glibc extensions which seem to be reasonably widespread (e.g. Ruby).
|
||||
|
||||
- Added `%:z` specifier and corresponding formatting items
|
||||
which is essentially the same as `%z` but with a colon.
|
||||
|
||||
- Added a new specifier `%.f` which precision adapts from the input.
|
||||
This was added as a response to the UX problems in the original nanosecond specifier `%f`.
|
||||
|
||||
### Fixed
|
||||
|
||||
- `Numeric::Timestamp` specifier (`%s`) was ignoring the time zone offset when provided.
|
||||
|
||||
- Improved the documentation and associated tests for `strftime`.
|
||||
|
||||
## 0.2.14 (2015-05-15)
|
||||
|
||||
### Fixed
|
||||
|
||||
- `NaiveDateTime +/- Duration` or `NaiveTime +/- Duration` could have gone wrong
|
||||
when the `Duration` to be added is negative and has a fractional second part.
|
||||
This was caused by an underflow in the conversion from `Duration` to the parts;
|
||||
the lack of tests for this case allowed a bug. (#37)
|
||||
|
||||
## 0.2.13 (2015-04-29)
|
||||
|
||||
### Added
|
||||
|
||||
- The optional dependency on `rustc_serialize` and
|
||||
relevant `Rustc{En,De}codable` implementations for supported types has been added.
|
||||
This is enabled by the `rustc-serialize` Cargo feature. (#34)
|
||||
|
||||
### Changed
|
||||
|
||||
- `chrono::Duration` reexport is changed to that of crates.io `time` crate.
|
||||
This enables Rust 1.0 beta compatibility.
|
||||
|
||||
## 0.2.4 (2015-03-03)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Clarified the meaning of `Date<Tz>` and fixed unwanted conversion problem
|
||||
that only occurs with positive UTC offsets. (#27)
|
||||
|
||||
## 0.2.3 (2015-02-27)
|
||||
|
||||
### Added
|
||||
|
||||
- `DateTime<Tz>` and `Date<Tz>` is now `Copy`/`Send` when `Tz::Offset` is `Copy`/`Send`.
|
||||
The implementations for them were mistakenly omitted. (#25)
|
||||
|
||||
### Fixed
|
||||
|
||||
- `Local::from_utc_datetime` didn't set a correct offset. (#26)
|
||||
|
||||
## 0.2.1 (2015-02-21)
|
||||
|
||||
### Changed
|
||||
|
||||
- `DelayedFormat` no longer conveys a redundant lifetime.
|
||||
|
||||
## 0.2.0 (2015-02-19)
|
||||
|
||||
### Added
|
||||
|
||||
- `Offset` is splitted into `TimeZone` (constructor) and `Offset` (storage) types.
|
||||
You would normally see only the former, as the latter is mostly an implementation detail.
|
||||
Most importantly, `Local` now can be used to directly construct timezone-aware values.
|
||||
|
||||
Some types (currently, `UTC` and `FixedOffset`) are both `TimeZone` and `Offset`,
|
||||
but others aren't (e.g. `Local` is not what is being stored to each `DateTime` values).
|
||||
|
||||
- `LocalResult::map` convenience method has been added.
|
||||
|
||||
- `TimeZone` now allows a construction of `DateTime` values from UNIX timestamp,
|
||||
via `timestamp` and `timestamp_opt` methods.
|
||||
|
||||
- `TimeZone` now also has a method for parsing `DateTime`, namely `datetime_from_str`.
|
||||
|
||||
- The following methods have been added to all date and time types:
|
||||
|
||||
- `checked_add`
|
||||
- `checked_sub`
|
||||
- `format_with_items`
|
||||
|
||||
- The following methods have been added to all timezone-aware types:
|
||||
|
||||
- `timezone`
|
||||
- `with_timezone`
|
||||
- `naive_utc`
|
||||
- `naive_local`
|
||||
|
||||
- `parse_from_str` method has been added to all naive types and `DateTime<FixedOffset>`.
|
||||
|
||||
- All naive types and instances of `DateTime` with time zones `UTC`, `Local` and `FixedOffset`
|
||||
implement the `FromStr` trait. They parse what `std::fmt::Debug` would print.
|
||||
|
||||
- `chrono::format` has been greatly rewritten.
|
||||
|
||||
- The formatting syntax parser is modular now, available at `chrono::format::strftime`.
|
||||
|
||||
- The parser and resolution algorithm is also modular, the former is available at
|
||||
`chrono::format::parse` while the latter is available at `chrono::format::parsed`.
|
||||
|
||||
- Explicit support for RFC 2822 and 3339 syntaxes is landed.
|
||||
|
||||
- There is a minor formatting difference with atypical values,
|
||||
e.g. for years not between 1 BCE and 9999 CE.
|
||||
|
||||
### Changed
|
||||
|
||||
- Most uses of `Offset` are converted to `TimeZone`.
|
||||
In fact, *all* user-facing code is expected to be `Offset`-free.
|
||||
|
||||
- `[Naive]DateTime::*num_seconds_from_unix_epoch*` methods have been renamed to
|
||||
simply `timestamp` or `from_timestamp*`. The original names have been deprecated.
|
||||
|
||||
### Removed
|
||||
|
||||
- `Time` has been removed. This also prompts a related set of methods in `TimeZone`.
|
||||
|
||||
This is in principle possible, but in practice has seen a little use
|
||||
because it can only be meaningfully constructed via an existing `DateTime` value.
|
||||
This made many operations to `Time` unintuitive or ambiguous,
|
||||
so we simply let it go.
|
||||
|
||||
In the case that `Time` is really required, one can use a simpler `NaiveTime`.
|
||||
`NaiveTime` and `NaiveDate` can be freely combined and splitted,
|
||||
and `TimeZone::from_{local,utc}_datetime` can be used to convert from/to the local time.
|
||||
|
||||
- `with_offset` method has been removed. Use `with_timezone` method instead.
|
||||
(This is not deprecated since it is an integral part of offset reform.)
|
||||
|
||||
## 0.1.14 (2015-01-10)
|
||||
|
||||
### Added
|
||||
|
||||
- Added a missing `std::fmt::String` impl for `Local`.
|
||||
|
||||
## 0.1.13 (2015-01-10)
|
||||
|
||||
### Changed
|
||||
|
||||
- Most types now implement both `std::fmt::Show` and `std::fmt::String`,
|
||||
with the former used for the stricter output and the latter used for more casual output.
|
||||
|
||||
### Removed
|
||||
|
||||
- `Offset::name` has been replaced by a `std::fmt::String` implementation to `Offset`.
|
||||
|
||||
## 0.1.12 (2015-01-08)
|
||||
|
||||
### Removed
|
||||
|
||||
- `Duration + T` no longer works due to the updated impl reachability rules.
|
||||
Use `T + Duration` as a workaround.
|
||||
|
||||
## 0.1.4 (2014-12-13)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed a bug that `Date::and_*` methods with an offset that can change the date are
|
||||
off by one day.
|
||||
|
||||
## 0.1.3 (2014-11-28)
|
||||
|
||||
### Added
|
||||
|
||||
- `{Date,Time,DateTime}::with_offset` methods have been added.
|
||||
|
||||
- `LocalResult` now implements a common set of traits.
|
||||
|
||||
- `LocalResult::and_*` methods have been added.
|
||||
They are useful for safely chaining `LocalResult<Date<Off>>` methods
|
||||
to make `LocalResult<DateTime<Off>>`.
|
||||
|
||||
### Changed
|
||||
|
||||
- `Offset::name` now returns `SendStr`.
|
||||
|
||||
- `{Date,Time} - Duration` overloadings are now allowed.
|
||||
|
||||
## 0.1.2 (2014-11-24)
|
||||
|
||||
### Added
|
||||
|
||||
- `Duration + Date` overloading is now allowed.
|
||||
|
||||
### Changed
|
||||
|
||||
- Chrono no longer needs `num` dependency.
|
||||
|
||||
## 0.1.0 (2014-11-20)
|
||||
|
||||
The initial version that was available to `crates.io`.
|
||||
|
||||
33
third_party/rust/chrono/CITATION.cff
vendored
Normal file
33
third_party/rust/chrono/CITATION.cff
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
# Parser settings.
|
||||
cff-version: 1.2.0
|
||||
message: Please cite this crate using these information.
|
||||
|
||||
# Version information.
|
||||
date-released: 2025-02-26
|
||||
version: 0.4.40
|
||||
|
||||
# Project information.
|
||||
abstract: Date and time library for Rust
|
||||
authors:
|
||||
- alias: quodlibetor
|
||||
family-names: Maister
|
||||
given-names: Brandon W.
|
||||
- alias: djc
|
||||
family-names: Ochtman
|
||||
given-names: Dirkjan
|
||||
- alias: lifthrasiir
|
||||
family-names: Seonghoon
|
||||
given-names: Kang
|
||||
- alias: esheppa
|
||||
family-names: Sheppard
|
||||
given-names: Eric
|
||||
- alias: pitdicker
|
||||
family-names: Dicker
|
||||
given-names: Paul
|
||||
license:
|
||||
- Apache-2.0
|
||||
- MIT
|
||||
repository-artifact: https://crates.io/crates/chrono
|
||||
repository-code: https://github.com/chronotope/chrono
|
||||
title: chrono
|
||||
url: https://docs.rs/chrono
|
||||
808
third_party/rust/chrono/Cargo.lock
generated
vendored
Normal file
808
third_party/rust/chrono/Cargo.lock
generated
vendored
Normal file
@@ -0,0 +1,808 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
|
||||
dependencies = [
|
||||
"derive_arbitrary",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
||||
dependencies = [
|
||||
"funty",
|
||||
"radium",
|
||||
"tap",
|
||||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||
|
||||
[[package]]
|
||||
name = "bytecheck"
|
||||
version = "0.6.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2"
|
||||
dependencies = [
|
||||
"bytecheck_derive",
|
||||
"ptr_meta",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytecheck_derive"
|
||||
version = "0.6.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.40"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"arbitrary",
|
||||
"bincode",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"pure-rust-locales",
|
||||
"rkyv",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"similar-asserts",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-test",
|
||||
"windows-bindgen",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "derive_arbitrary"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.170"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "minicov"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ptr_meta"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1"
|
||||
dependencies = [
|
||||
"ptr_meta_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ptr_meta_derive"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pure-rust-locales"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1190fd18ae6ce9e137184f207593877e70f39b015040156b1e05081cdfe3733a"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
|
||||
[[package]]
|
||||
name = "rend"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c"
|
||||
dependencies = [
|
||||
"bytecheck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rkyv"
|
||||
version = "0.7.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"bytecheck",
|
||||
"hashbrown",
|
||||
"ptr_meta",
|
||||
"rend",
|
||||
"rkyv_derive",
|
||||
"seahash",
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rkyv_derive"
|
||||
version = "0.7.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "seahash"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.218"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.218"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.139"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "similar"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "similar-asserts"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b441962c817e33508847a22bd82f03a30cff43642dc2fae8b050566121eb9a"
|
||||
dependencies = [
|
||||
"console",
|
||||
"similar",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tap"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test"
|
||||
version = "0.3.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"minicov",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-bindgen-test-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test-macro"
|
||||
version = "0.3.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-bindgen"
|
||||
version = "0.60.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d72f8d00d9d4bd00634be0ddbb927cec37d010b55753c4c07a401f4035da6268"
|
||||
dependencies = [
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
|
||||
dependencies = [
|
||||
"tap",
|
||||
]
|
||||
186
third_party/rust/chrono/Cargo.toml
vendored
186
third_party/rust/chrono/Cargo.toml
vendored
@@ -3,84 +3,141 @@
|
||||
# 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
|
||||
# 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)
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2021"
|
||||
rust-version = "1.61.0"
|
||||
name = "chrono"
|
||||
version = "0.4.19"
|
||||
authors = ["Kang Seonghoon <public+rust@mearie.org>", "Brandon W Maister <quodlibetor@gmail.com>"]
|
||||
exclude = ["/ci/*", "/.travis.yml", "/appveyor.yml", "/Makefile"]
|
||||
version = "0.4.40"
|
||||
build = false
|
||||
include = [
|
||||
"src/*",
|
||||
"tests/*.rs",
|
||||
"LICENSE.txt",
|
||||
"CITATION.cff",
|
||||
]
|
||||
autolib = false
|
||||
autobins = false
|
||||
autoexamples = false
|
||||
autotests = false
|
||||
autobenches = false
|
||||
description = "Date and time library for Rust"
|
||||
homepage = "https://github.com/chronotope/chrono"
|
||||
documentation = "https://docs.rs/chrono/"
|
||||
readme = "README.md"
|
||||
keywords = ["date", "time", "calendar"]
|
||||
keywords = [
|
||||
"date",
|
||||
"time",
|
||||
"calendar",
|
||||
]
|
||||
categories = ["date-and-time"]
|
||||
license = "MIT/Apache-2.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/chronotope/chrono"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["serde"]
|
||||
features = [
|
||||
"arbitrary",
|
||||
"rkyv",
|
||||
"serde",
|
||||
"unstable-locales",
|
||||
]
|
||||
rustdoc-args = [
|
||||
"--cfg",
|
||||
"docsrs",
|
||||
]
|
||||
|
||||
[package.metadata.playground]
|
||||
features = ["serde"]
|
||||
|
||||
[features]
|
||||
__internal_bench = []
|
||||
alloc = []
|
||||
clock = [
|
||||
"winapi",
|
||||
"iana-time-zone",
|
||||
"android-tzdata",
|
||||
"now",
|
||||
]
|
||||
default = [
|
||||
"clock",
|
||||
"std",
|
||||
"oldtime",
|
||||
"wasmbind",
|
||||
]
|
||||
libc = []
|
||||
now = ["std"]
|
||||
oldtime = []
|
||||
rkyv = [
|
||||
"dep:rkyv",
|
||||
"rkyv/size_32",
|
||||
]
|
||||
rkyv-16 = [
|
||||
"dep:rkyv",
|
||||
"rkyv?/size_16",
|
||||
]
|
||||
rkyv-32 = [
|
||||
"dep:rkyv",
|
||||
"rkyv?/size_32",
|
||||
]
|
||||
rkyv-64 = [
|
||||
"dep:rkyv",
|
||||
"rkyv?/size_64",
|
||||
]
|
||||
rkyv-validation = ["rkyv?/validation"]
|
||||
std = ["alloc"]
|
||||
unstable-locales = ["pure-rust-locales"]
|
||||
wasmbind = [
|
||||
"wasm-bindgen",
|
||||
"js-sys",
|
||||
]
|
||||
winapi = ["windows-link"]
|
||||
|
||||
[lib]
|
||||
name = "chrono"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[bench]]
|
||||
name = "chrono"
|
||||
harness = false
|
||||
required-features = ["__internal_bench"]
|
||||
[[test]]
|
||||
name = "dateutils"
|
||||
path = "tests/dateutils.rs"
|
||||
|
||||
[[bench]]
|
||||
name = "serde"
|
||||
harness = false
|
||||
required-features = ["serde"]
|
||||
[dependencies.libc]
|
||||
version = "0.2.69"
|
||||
[[test]]
|
||||
name = "wasm"
|
||||
path = "tests/wasm.rs"
|
||||
|
||||
[[test]]
|
||||
name = "win_bindings"
|
||||
path = "tests/win_bindings.rs"
|
||||
|
||||
[dependencies.arbitrary]
|
||||
version = "1.0.0"
|
||||
features = ["derive"]
|
||||
optional = true
|
||||
|
||||
[dependencies.num-integer]
|
||||
version = "0.1.36"
|
||||
default-features = false
|
||||
|
||||
[dependencies.num-traits]
|
||||
version = "0.2"
|
||||
default-features = false
|
||||
|
||||
[dependencies.pure-rust-locales]
|
||||
version = "0.5.2"
|
||||
version = "0.8"
|
||||
optional = true
|
||||
|
||||
[dependencies.rustc-serialize]
|
||||
version = "0.3.20"
|
||||
[dependencies.rkyv]
|
||||
version = "0.7.43"
|
||||
optional = true
|
||||
default-features = false
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0.99"
|
||||
optional = true
|
||||
default-features = false
|
||||
|
||||
[dependencies.time]
|
||||
version = "0.1.43"
|
||||
optional = true
|
||||
[dev-dependencies.bincode]
|
||||
version = "0.8.0"
|
||||
|
||||
[dev-dependencies.criterion]
|
||||
version = "0.3"
|
||||
|
||||
[dev-dependencies.doc-comment]
|
||||
version = "0.3"
|
||||
|
||||
[dev-dependencies.num-iter]
|
||||
version = "0.1.35"
|
||||
default-features = false
|
||||
version = "1.3.0"
|
||||
|
||||
[dev-dependencies.serde_derive]
|
||||
version = "1"
|
||||
@@ -89,31 +146,32 @@ default-features = false
|
||||
[dev-dependencies.serde_json]
|
||||
version = "1"
|
||||
|
||||
[features]
|
||||
__doctest = []
|
||||
__internal_bench = []
|
||||
alloc = []
|
||||
clock = ["libc", "std", "winapi"]
|
||||
default = ["clock", "std", "oldtime"]
|
||||
oldtime = ["time"]
|
||||
std = []
|
||||
unstable-locales = ["pure-rust-locales", "alloc"]
|
||||
wasmbind = ["wasm-bindgen", "js-sys"]
|
||||
[target."cfg(all(target_arch = \"wasm32\", not(any(target_os = \"emscripten\", target_os = \"wasi\"))))".dependencies.js-sys]
|
||||
[dev-dependencies.similar-asserts]
|
||||
version = "1.6.1"
|
||||
|
||||
[target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.dependencies.js-sys]
|
||||
version = "0.3"
|
||||
optional = true
|
||||
|
||||
[target."cfg(all(target_arch = \"wasm32\", not(any(target_os = \"emscripten\", target_os = \"wasi\"))))".dependencies.wasm-bindgen]
|
||||
[target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.dependencies.wasm-bindgen]
|
||||
version = "0.2"
|
||||
optional = true
|
||||
[target."cfg(all(target_arch = \"wasm32\", not(any(target_os = \"emscripten\", target_os = \"wasi\"))))".dev-dependencies.wasm-bindgen-test]
|
||||
version = "0.3"
|
||||
[target."cfg(windows)".dependencies.winapi]
|
||||
version = "0.3.0"
|
||||
features = ["std", "minwinbase", "minwindef", "timezoneapi"]
|
||||
optional = true
|
||||
[badges.appveyor]
|
||||
repository = "chronotope/chrono"
|
||||
|
||||
[badges.travis-ci]
|
||||
repository = "chronotope/chrono"
|
||||
[target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.dev-dependencies.wasm-bindgen-test]
|
||||
version = "0.3"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies.android-tzdata]
|
||||
version = "0.1.1"
|
||||
optional = true
|
||||
|
||||
[target."cfg(unix)".dependencies.iana-time-zone]
|
||||
version = "0.1.45"
|
||||
features = ["fallback"]
|
||||
optional = true
|
||||
|
||||
[target."cfg(windows)".dependencies.windows-link]
|
||||
version = "0.1"
|
||||
optional = true
|
||||
|
||||
[target."cfg(windows)".dev-dependencies.windows-bindgen]
|
||||
version = "0.60"
|
||||
|
||||
2
third_party/rust/chrono/LICENSE.txt
vendored
2
third_party/rust/chrono/LICENSE.txt
vendored
@@ -1,5 +1,5 @@
|
||||
Rust-chrono is dual-licensed under The MIT License [1] and
|
||||
Apache 2.0 License [2]. Copyright (c) 2014--2017, Kang Seonghoon and
|
||||
Apache 2.0 License [2]. Copyright (c) 2014--2025, Kang Seonghoon and
|
||||
contributors.
|
||||
|
||||
Nota Bene: This is same as the Rust Project's own license.
|
||||
|
||||
419
third_party/rust/chrono/README.md
vendored
419
third_party/rust/chrono/README.md
vendored
@@ -1,419 +0,0 @@
|
||||
[Chrono][docsrs]: Date and Time for Rust
|
||||
========================================
|
||||
|
||||
[![Chrono GitHub Actions][gh-image]][gh-checks]
|
||||
[![Chrono on crates.io][cratesio-image]][cratesio]
|
||||
[![Chrono on docs.rs][docsrs-image]][docsrs]
|
||||
[![Join the chat at https://gitter.im/chrono-rs/chrono][gitter-image]][gitter]
|
||||
|
||||
[gh-image]: https://github.com/chronotope/chrono/workflows/test/badge.svg
|
||||
[gh-checks]: https://github.com/chronotope/chrono/actions?query=workflow%3Atest
|
||||
[cratesio-image]: https://img.shields.io/crates/v/chrono.svg
|
||||
[cratesio]: https://crates.io/crates/chrono
|
||||
[docsrs-image]: https://docs.rs/chrono/badge.svg
|
||||
[docsrs]: https://docs.rs/chrono
|
||||
[gitter-image]: https://badges.gitter.im/chrono-rs/chrono.svg
|
||||
[gitter]: https://gitter.im/chrono-rs/chrono
|
||||
|
||||
It aims to be a feature-complete superset of
|
||||
the [time](https://github.com/rust-lang-deprecated/time) library.
|
||||
In particular,
|
||||
|
||||
* Chrono strictly adheres to ISO 8601.
|
||||
* Chrono is timezone-aware by default, with separate timezone-naive types.
|
||||
* Chrono is space-optimal and (while not being the primary goal) reasonably efficient.
|
||||
|
||||
There were several previous attempts to bring a good date and time library to Rust,
|
||||
which Chrono builds upon and should acknowledge:
|
||||
|
||||
* [Initial research on
|
||||
the wiki](https://github.com/rust-lang/rust-wiki-backup/blob/master/Lib-datetime.md)
|
||||
* Dietrich Epp's [datetime-rs](https://github.com/depp/datetime-rs)
|
||||
* Luis de Bethencourt's [rust-datetime](https://github.com/luisbg/rust-datetime)
|
||||
|
||||
Any significant changes to Chrono are documented in
|
||||
the [`CHANGELOG.md`](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md) file.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
Put this in your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
chrono = "0.4"
|
||||
```
|
||||
|
||||
### Features
|
||||
|
||||
Chrono supports various runtime environments and operating systems, and has
|
||||
several features that may be enabled or disabled.
|
||||
|
||||
Default features:
|
||||
|
||||
- `alloc`: Enable features that depend on allocation (primarily string formatting)
|
||||
- `std`: Enables functionality that depends on the standard library. This
|
||||
is a superset of `alloc` and adds interoperation with standard library types
|
||||
and traits.
|
||||
- `clock`: enables reading the system time (`now`), independent of whether
|
||||
`std::time::SystemTime` is present, depends on having a libc.
|
||||
|
||||
Optional features:
|
||||
|
||||
- `wasmbind`: Enable integration with [wasm-bindgen][] and its `js-sys` project
|
||||
- [`serde`][]: Enable serialization/deserialization via serde.
|
||||
- `unstable-locales`: Enable localization. This adds various methods with a
|
||||
`_localized` suffix. The implementation and API may change or even be
|
||||
removed in a patch release. Feedback welcome.
|
||||
|
||||
[`serde`]: https://github.com/serde-rs/serde
|
||||
[wasm-bindgen]: https://github.com/rustwasm/wasm-bindgen
|
||||
|
||||
See the [cargo docs][] for examples of specifying features.
|
||||
|
||||
[cargo docs]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#choosing-features
|
||||
|
||||
## Overview
|
||||
|
||||
### Duration
|
||||
|
||||
Chrono currently uses its own [`Duration`] type to represent the magnitude
|
||||
of a time span. Since this has the same name as the newer, standard type for
|
||||
duration, the reference will refer this type as `OldDuration`.
|
||||
|
||||
Note that this is an "accurate" duration represented as seconds and
|
||||
nanoseconds and does not represent "nominal" components such as days or
|
||||
months.
|
||||
|
||||
When the `oldtime` feature is enabled, [`Duration`] is an alias for the
|
||||
[`time::Duration`](https://docs.rs/time/0.1.40/time/struct.Duration.html)
|
||||
type from v0.1 of the time crate. time v0.1 is deprecated, so new code
|
||||
should disable the `oldtime` feature and use the `chrono::Duration` type
|
||||
instead. The `oldtime` feature is enabled by default for backwards
|
||||
compatibility, but future versions of Chrono are likely to remove the
|
||||
feature entirely.
|
||||
|
||||
Chrono does not yet natively support
|
||||
the standard [`Duration`](https://doc.rust-lang.org/std/time/struct.Duration.html) type,
|
||||
but it will be supported in the future.
|
||||
Meanwhile you can convert between two types with
|
||||
[`Duration::from_std`](https://docs.rs/time/0.1.40/time/struct.Duration.html#method.from_std)
|
||||
and
|
||||
[`Duration::to_std`](https://docs.rs/time/0.1.40/time/struct.Duration.html#method.to_std)
|
||||
methods.
|
||||
|
||||
### Date and Time
|
||||
|
||||
Chrono provides a
|
||||
[**`DateTime`**](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html)
|
||||
type to represent a date and a time in a timezone.
|
||||
|
||||
For more abstract moment-in-time tracking such as internal timekeeping
|
||||
that is unconcerned with timezones, consider
|
||||
[`time::SystemTime`](https://doc.rust-lang.org/std/time/struct.SystemTime.html),
|
||||
which tracks your system clock, or
|
||||
[`time::Instant`](https://doc.rust-lang.org/std/time/struct.Instant.html), which
|
||||
is an opaque but monotonically-increasing representation of a moment in time.
|
||||
|
||||
`DateTime` is timezone-aware and must be constructed from
|
||||
the [**`TimeZone`**](https://docs.rs/chrono/0.4/chrono/offset/trait.TimeZone.html) object,
|
||||
which defines how the local date is converted to and back from the UTC date.
|
||||
There are three well-known `TimeZone` implementations:
|
||||
|
||||
* [**`Utc`**](https://docs.rs/chrono/0.4/chrono/offset/struct.Utc.html) specifies the UTC time zone. It is most efficient.
|
||||
|
||||
* [**`Local`**](https://docs.rs/chrono/0.4/chrono/offset/struct.Local.html) specifies the system local time zone.
|
||||
|
||||
* [**`FixedOffset`**](https://docs.rs/chrono/0.4/chrono/offset/struct.FixedOffset.html) specifies
|
||||
an arbitrary, fixed time zone such as UTC+09:00 or UTC-10:30.
|
||||
This often results from the parsed textual date and time.
|
||||
Since it stores the most information and does not depend on the system environment,
|
||||
you would want to normalize other `TimeZone`s into this type.
|
||||
|
||||
`DateTime`s with different `TimeZone` types are distinct and do not mix,
|
||||
but can be converted to each other using
|
||||
the [`DateTime::with_timezone`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.with_timezone) method.
|
||||
|
||||
You can get the current date and time in the UTC time zone
|
||||
([`Utc::now()`](https://docs.rs/chrono/0.4/chrono/offset/struct.Utc.html#method.now))
|
||||
or in the local time zone
|
||||
([`Local::now()`](https://docs.rs/chrono/0.4/chrono/offset/struct.Local.html#method.now)).
|
||||
|
||||
```rust
|
||||
use chrono::prelude::*;
|
||||
|
||||
let utc: DateTime<Utc> = Utc::now(); // e.g. `2014-11-28T12:45:59.324310806Z`
|
||||
let local: DateTime<Local> = Local::now(); // e.g. `2014-11-28T21:45:59.324310806+09:00`
|
||||
```
|
||||
|
||||
Alternatively, you can create your own date and time.
|
||||
This is a bit verbose due to Rust's lack of function and method overloading,
|
||||
but in turn we get a rich combination of initialization methods.
|
||||
|
||||
```rust
|
||||
use chrono::prelude::*;
|
||||
use chrono::offset::LocalResult;
|
||||
|
||||
let dt = Utc.ymd(2014, 7, 8).and_hms(9, 10, 11); // `2014-07-08T09:10:11Z`
|
||||
// July 8 is 188th day of the year 2014 (`o` for "ordinal")
|
||||
assert_eq!(dt, Utc.yo(2014, 189).and_hms(9, 10, 11));
|
||||
// July 8 is Tuesday in ISO week 28 of the year 2014.
|
||||
assert_eq!(dt, Utc.isoywd(2014, 28, Weekday::Tue).and_hms(9, 10, 11));
|
||||
|
||||
let dt = Utc.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12); // `2014-07-08T09:10:11.012Z`
|
||||
assert_eq!(dt, Utc.ymd(2014, 7, 8).and_hms_micro(9, 10, 11, 12_000));
|
||||
assert_eq!(dt, Utc.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 12_000_000));
|
||||
|
||||
// dynamic verification
|
||||
assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(21, 15, 33),
|
||||
LocalResult::Single(Utc.ymd(2014, 7, 8).and_hms(21, 15, 33)));
|
||||
assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(80, 15, 33), LocalResult::None);
|
||||
assert_eq!(Utc.ymd_opt(2014, 7, 38).and_hms_opt(21, 15, 33), LocalResult::None);
|
||||
|
||||
// other time zone objects can be used to construct a local datetime.
|
||||
// obviously, `local_dt` is normally different from `dt`, but `fixed_dt` should be identical.
|
||||
let local_dt = Local.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12);
|
||||
let fixed_dt = FixedOffset::east(9 * 3600).ymd(2014, 7, 8).and_hms_milli(18, 10, 11, 12);
|
||||
assert_eq!(dt, fixed_dt);
|
||||
```
|
||||
|
||||
Various properties are available to the date and time, and can be altered individually.
|
||||
Most of them are defined in the traits [`Datelike`](https://docs.rs/chrono/0.4/chrono/trait.Datelike.html) and
|
||||
[`Timelike`](https://docs.rs/chrono/0.4/chrono/trait.Timelike.html) which you should `use` before.
|
||||
Addition and subtraction is also supported.
|
||||
The following illustrates most supported operations to the date and time:
|
||||
|
||||
```rust
|
||||
|
||||
use chrono::prelude::*;
|
||||
use chrono::Duration;
|
||||
|
||||
// assume this returned `2014-11-28T21:45:59.324310806+09:00`:
|
||||
let dt = FixedOffset::east(9*3600).ymd(2014, 11, 28).and_hms_nano(21, 45, 59, 324310806);
|
||||
|
||||
// property accessors
|
||||
assert_eq!((dt.year(), dt.month(), dt.day()), (2014, 11, 28));
|
||||
assert_eq!((dt.month0(), dt.day0()), (10, 27)); // for unfortunate souls
|
||||
assert_eq!((dt.hour(), dt.minute(), dt.second()), (21, 45, 59));
|
||||
assert_eq!(dt.weekday(), Weekday::Fri);
|
||||
assert_eq!(dt.weekday().number_from_monday(), 5); // Mon=1, ..., Sun=7
|
||||
assert_eq!(dt.ordinal(), 332); // the day of year
|
||||
assert_eq!(dt.num_days_from_ce(), 735565); // the number of days from and including Jan 1, 1
|
||||
|
||||
// time zone accessor and manipulation
|
||||
assert_eq!(dt.offset().fix().local_minus_utc(), 9 * 3600);
|
||||
assert_eq!(dt.timezone(), FixedOffset::east(9 * 3600));
|
||||
assert_eq!(dt.with_timezone(&Utc), Utc.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806));
|
||||
|
||||
// a sample of property manipulations (validates dynamically)
|
||||
assert_eq!(dt.with_day(29).unwrap().weekday(), Weekday::Sat); // 2014-11-29 is Saturday
|
||||
assert_eq!(dt.with_day(32), None);
|
||||
assert_eq!(dt.with_year(-300).unwrap().num_days_from_ce(), -109606); // November 29, 301 BCE
|
||||
|
||||
// arithmetic operations
|
||||
let dt1 = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
|
||||
let dt2 = Utc.ymd(2014, 11, 14).and_hms(10, 9, 8);
|
||||
assert_eq!(dt1.signed_duration_since(dt2), Duration::seconds(-2 * 3600 + 2));
|
||||
assert_eq!(dt2.signed_duration_since(dt1), Duration::seconds(2 * 3600 - 2));
|
||||
assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0) + Duration::seconds(1_000_000_000),
|
||||
Utc.ymd(2001, 9, 9).and_hms(1, 46, 40));
|
||||
assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_000),
|
||||
Utc.ymd(1938, 4, 24).and_hms(22, 13, 20));
|
||||
```
|
||||
|
||||
### Formatting and Parsing
|
||||
|
||||
Formatting is done via the [`format`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.format) method,
|
||||
which format is equivalent to the familiar `strftime` format.
|
||||
|
||||
See [`format::strftime`](https://docs.rs/chrono/0.4/chrono/format/strftime/index.html#specifiers)
|
||||
documentation for full syntax and list of specifiers.
|
||||
|
||||
The default `to_string` method and `{:?}` specifier also give a reasonable representation.
|
||||
Chrono also provides [`to_rfc2822`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.to_rfc2822) and
|
||||
[`to_rfc3339`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.to_rfc3339) methods
|
||||
for well-known formats.
|
||||
|
||||
Chrono now also provides date formatting in almost any language without the
|
||||
help of an additional C library. This functionality is under the feature
|
||||
`unstable-locales`:
|
||||
|
||||
```text
|
||||
chrono { version = "0.4", features = ["unstable-locales"]
|
||||
```
|
||||
|
||||
The `unstable-locales` feature requires and implies at least the `alloc` feature.
|
||||
|
||||
```rust
|
||||
use chrono::prelude::*;
|
||||
|
||||
let dt = Utc.ymd(2014, 11, 28).and_hms(12, 0, 9);
|
||||
assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2014-11-28 12:00:09");
|
||||
assert_eq!(dt.format("%a %b %e %T %Y").to_string(), "Fri Nov 28 12:00:09 2014");
|
||||
assert_eq!(dt.format_localized("%A %e %B %Y, %T", Locale::fr_BE).to_string(), "vendredi 28 novembre 2014, 12:00:09");
|
||||
assert_eq!(dt.format("%a %b %e %T %Y").to_string(), dt.format("%c").to_string());
|
||||
|
||||
assert_eq!(dt.to_string(), "2014-11-28 12:00:09 UTC");
|
||||
assert_eq!(dt.to_rfc2822(), "Fri, 28 Nov 2014 12:00:09 +0000");
|
||||
assert_eq!(dt.to_rfc3339(), "2014-11-28T12:00:09+00:00");
|
||||
assert_eq!(format!("{:?}", dt), "2014-11-28T12:00:09Z");
|
||||
|
||||
// Note that milli/nanoseconds are only printed if they are non-zero
|
||||
let dt_nano = Utc.ymd(2014, 11, 28).and_hms_nano(12, 0, 9, 1);
|
||||
assert_eq!(format!("{:?}", dt_nano), "2014-11-28T12:00:09.000000001Z");
|
||||
```
|
||||
|
||||
Parsing can be done with three methods:
|
||||
|
||||
1. The standard [`FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html) trait
|
||||
(and [`parse`](https://doc.rust-lang.org/std/primitive.str.html#method.parse) method
|
||||
on a string) can be used for parsing `DateTime<FixedOffset>`, `DateTime<Utc>` and
|
||||
`DateTime<Local>` values. This parses what the `{:?}`
|
||||
([`std::fmt::Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html))
|
||||
format specifier prints, and requires the offset to be present.
|
||||
|
||||
2. [`DateTime::parse_from_str`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.parse_from_str) parses
|
||||
a date and time with offsets and returns `DateTime<FixedOffset>`.
|
||||
This should be used when the offset is a part of input and the caller cannot guess that.
|
||||
It *cannot* be used when the offset can be missing.
|
||||
[`DateTime::parse_from_rfc2822`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.parse_from_rfc2822)
|
||||
and
|
||||
[`DateTime::parse_from_rfc3339`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.parse_from_rfc3339)
|
||||
are similar but for well-known formats.
|
||||
|
||||
3. [`Offset::datetime_from_str`](https://docs.rs/chrono/0.4/chrono/offset/trait.TimeZone.html#method.datetime_from_str) is
|
||||
similar but returns `DateTime` of given offset.
|
||||
When the explicit offset is missing from the input, it simply uses given offset.
|
||||
It issues an error when the input contains an explicit offset different
|
||||
from the current offset.
|
||||
|
||||
More detailed control over the parsing process is available via
|
||||
[`format`](https://docs.rs/chrono/0.4/chrono/format/index.html) module.
|
||||
|
||||
```rust
|
||||
use chrono::prelude::*;
|
||||
|
||||
let dt = Utc.ymd(2014, 11, 28).and_hms(12, 0, 9);
|
||||
let fixed_dt = dt.with_timezone(&FixedOffset::east(9*3600));
|
||||
|
||||
// method 1
|
||||
assert_eq!("2014-11-28T12:00:09Z".parse::<DateTime<Utc>>(), Ok(dt.clone()));
|
||||
assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<Utc>>(), Ok(dt.clone()));
|
||||
assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<FixedOffset>>(), Ok(fixed_dt.clone()));
|
||||
|
||||
// method 2
|
||||
assert_eq!(DateTime::parse_from_str("2014-11-28 21:00:09 +09:00", "%Y-%m-%d %H:%M:%S %z"),
|
||||
Ok(fixed_dt.clone()));
|
||||
assert_eq!(DateTime::parse_from_rfc2822("Fri, 28 Nov 2014 21:00:09 +0900"),
|
||||
Ok(fixed_dt.clone()));
|
||||
assert_eq!(DateTime::parse_from_rfc3339("2014-11-28T21:00:09+09:00"), Ok(fixed_dt.clone()));
|
||||
|
||||
// method 3
|
||||
assert_eq!(Utc.datetime_from_str("2014-11-28 12:00:09", "%Y-%m-%d %H:%M:%S"), Ok(dt.clone()));
|
||||
assert_eq!(Utc.datetime_from_str("Fri Nov 28 12:00:09 2014", "%a %b %e %T %Y"), Ok(dt.clone()));
|
||||
|
||||
// oops, the year is missing!
|
||||
assert!(Utc.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err());
|
||||
// oops, the format string does not include the year at all!
|
||||
assert!(Utc.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T").is_err());
|
||||
// oops, the weekday is incorrect!
|
||||
assert!(Utc.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err());
|
||||
```
|
||||
|
||||
Again : See [`format::strftime`](https://docs.rs/chrono/0.4/chrono/format/strftime/index.html#specifiers)
|
||||
documentation for full syntax and list of specifiers.
|
||||
|
||||
### Conversion from and to EPOCH timestamps
|
||||
|
||||
Use [`Utc.timestamp(seconds, nanoseconds)`](https://docs.rs/chrono/0.4/chrono/offset/trait.TimeZone.html#method.timestamp)
|
||||
to construct a [`DateTime<Utc>`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html) from a UNIX timestamp
|
||||
(seconds, nanoseconds that passed since January 1st 1970).
|
||||
|
||||
Use [`DateTime.timestamp`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.timestamp) to get the timestamp (in seconds)
|
||||
from a [`DateTime`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html). Additionally, you can use
|
||||
[`DateTime.timestamp_subsec_nanos`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.timestamp_subsec_nanos)
|
||||
to get the number of additional number of nanoseconds.
|
||||
|
||||
```rust
|
||||
// We need the trait in scope to use Utc::timestamp().
|
||||
use chrono::{DateTime, TimeZone, Utc};
|
||||
|
||||
// Construct a datetime from epoch:
|
||||
let dt = Utc.timestamp(1_500_000_000, 0);
|
||||
assert_eq!(dt.to_rfc2822(), "Fri, 14 Jul 2017 02:40:00 +0000");
|
||||
|
||||
// Get epoch value from a datetime:
|
||||
let dt = DateTime::parse_from_rfc2822("Fri, 14 Jul 2017 02:40:00 +0000").unwrap();
|
||||
assert_eq!(dt.timestamp(), 1_500_000_000);
|
||||
```
|
||||
|
||||
### Individual date
|
||||
|
||||
Chrono also provides an individual date type ([**`Date`**](https://docs.rs/chrono/0.4/chrono/struct.Date.html)).
|
||||
It also has time zones attached, and have to be constructed via time zones.
|
||||
Most operations available to `DateTime` are also available to `Date` whenever appropriate.
|
||||
|
||||
```rust
|
||||
use chrono::prelude::*;
|
||||
use chrono::offset::LocalResult;
|
||||
|
||||
assert_eq!(Utc::today(), Utc::now().date());
|
||||
assert_eq!(Local::today(), Local::now().date());
|
||||
|
||||
assert_eq!(Utc.ymd(2014, 11, 28).weekday(), Weekday::Fri);
|
||||
assert_eq!(Utc.ymd_opt(2014, 11, 31), LocalResult::None);
|
||||
assert_eq!(Utc.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(),
|
||||
"070809");
|
||||
```
|
||||
|
||||
There is no timezone-aware `Time` due to the lack of usefulness and also the complexity.
|
||||
|
||||
`DateTime` has [`date`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.date) method
|
||||
which returns a `Date` which represents its date component.
|
||||
There is also a [`time`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.time) method,
|
||||
which simply returns a naive local time described below.
|
||||
|
||||
### Naive date and time
|
||||
|
||||
Chrono provides naive counterparts to `Date`, (non-existent) `Time` and `DateTime`
|
||||
as [**`NaiveDate`**](https://docs.rs/chrono/0.4/chrono/naive/struct.NaiveDate.html),
|
||||
[**`NaiveTime`**](https://docs.rs/chrono/0.4/chrono/naive/struct.NaiveTime.html) and
|
||||
[**`NaiveDateTime`**](https://docs.rs/chrono/0.4/chrono/naive/struct.NaiveDateTime.html) respectively.
|
||||
|
||||
They have almost equivalent interfaces as their timezone-aware twins,
|
||||
but are not associated to time zones obviously and can be quite low-level.
|
||||
They are mostly useful for building blocks for higher-level types.
|
||||
|
||||
Timezone-aware `DateTime` and `Date` types have two methods returning naive versions:
|
||||
[`naive_local`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.naive_local) returns
|
||||
a view to the naive local time,
|
||||
and [`naive_utc`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.naive_utc) returns
|
||||
a view to the naive UTC time.
|
||||
|
||||
## Limitations
|
||||
|
||||
Only proleptic Gregorian calendar (i.e. extended to support older dates) is supported.
|
||||
Be very careful if you really have to deal with pre-20C dates, they can be in Julian or others.
|
||||
|
||||
Date types are limited in about +/- 262,000 years from the common epoch.
|
||||
Time types are limited in the nanosecond accuracy.
|
||||
|
||||
[Leap seconds are supported in the representation but
|
||||
Chrono doesn't try to make use of them](https://docs.rs/chrono/0.4/chrono/naive/struct.NaiveTime.html#leap-second-handling).
|
||||
(The main reason is that leap seconds are not really predictable.)
|
||||
Almost *every* operation over the possible leap seconds will ignore them.
|
||||
Consider using `NaiveDateTime` with the implicit TAI (International Atomic Time) scale
|
||||
if you want.
|
||||
|
||||
Chrono inherently does not support an inaccurate or partial date and time representation.
|
||||
Any operation that can be ambiguous will return `None` in such cases.
|
||||
For example, "a month later" of 2014-01-30 is not well-defined
|
||||
and consequently `Utc.ymd(2014, 1, 30).with_month(2)` returns `None`.
|
||||
|
||||
Non ISO week handling is not yet supported.
|
||||
For now you can use the [chrono_ext](https://crates.io/crates/chrono_ext)
|
||||
crate ([sources](https://github.com/bcourtine/chrono-ext/)).
|
||||
|
||||
Advanced time zone handling is not yet supported.
|
||||
For now you can try the [Chrono-tz](https://github.com/chronotope/chrono-tz/) crate instead.
|
||||
|
||||
116
third_party/rust/chrono/benches/chrono.rs
vendored
116
third_party/rust/chrono/benches/chrono.rs
vendored
@@ -1,116 +0,0 @@
|
||||
//! Benchmarks for chrono that just depend on std
|
||||
|
||||
extern crate chrono;
|
||||
extern crate criterion;
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||
|
||||
use chrono::prelude::*;
|
||||
use chrono::{DateTime, FixedOffset, Utc, __BenchYearFlags};
|
||||
|
||||
fn bench_datetime_parse_from_rfc2822(c: &mut Criterion) {
|
||||
c.bench_function("bench_datetime_parse_from_rfc2822", |b| {
|
||||
b.iter(|| {
|
||||
let str = black_box("Wed, 18 Feb 2015 23:16:09 +0000");
|
||||
DateTime::parse_from_rfc2822(str).unwrap()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_datetime_parse_from_rfc3339(c: &mut Criterion) {
|
||||
c.bench_function("bench_datetime_parse_from_rfc3339", |b| {
|
||||
b.iter(|| {
|
||||
let str = black_box("2015-02-18T23:59:60.234567+05:00");
|
||||
DateTime::parse_from_rfc3339(str).unwrap()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_datetime_from_str(c: &mut Criterion) {
|
||||
c.bench_function("bench_datetime_from_str", |b| {
|
||||
b.iter(|| {
|
||||
use std::str::FromStr;
|
||||
let str = black_box("2019-03-30T18:46:57.193Z");
|
||||
DateTime::<Utc>::from_str(str).unwrap()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_datetime_to_rfc2822(c: &mut Criterion) {
|
||||
let pst = FixedOffset::east(8 * 60 * 60);
|
||||
let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_000);
|
||||
c.bench_function("bench_datetime_to_rfc2822", |b| b.iter(|| black_box(dt).to_rfc2822()));
|
||||
}
|
||||
|
||||
fn bench_datetime_to_rfc3339(c: &mut Criterion) {
|
||||
let pst = FixedOffset::east(8 * 60 * 60);
|
||||
let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_000);
|
||||
c.bench_function("bench_datetime_to_rfc3339", |b| b.iter(|| black_box(dt).to_rfc3339()));
|
||||
}
|
||||
|
||||
fn bench_year_flags_from_year(c: &mut Criterion) {
|
||||
c.bench_function("bench_year_flags_from_year", |b| {
|
||||
b.iter(|| {
|
||||
for year in -999i32..1000 {
|
||||
__BenchYearFlags::from_year(year);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns the number of multiples of `div` in the range `start..end`.
|
||||
///
|
||||
/// If the range `start..end` is back-to-front, i.e. `start` is greater than `end`, the
|
||||
/// behaviour is defined by the following equation:
|
||||
/// `in_between(start, end, div) == - in_between(end, start, div)`.
|
||||
///
|
||||
/// When `div` is 1, this is equivalent to `end - start`, i.e. the length of `start..end`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `div` is not positive.
|
||||
fn in_between(start: i32, end: i32, div: i32) -> i32 {
|
||||
assert!(div > 0, "in_between: nonpositive div = {}", div);
|
||||
let start = (start.div_euclid(div), start.rem_euclid(div));
|
||||
let end = (end.div_euclid(div), end.rem_euclid(div));
|
||||
// The lowest multiple of `div` greater than or equal to `start`, divided.
|
||||
let start = start.0 + (start.1 != 0) as i32;
|
||||
// The lowest multiple of `div` greater than or equal to `end`, divided.
|
||||
let end = end.0 + (end.1 != 0) as i32;
|
||||
end - start
|
||||
}
|
||||
|
||||
/// Alternative implementation to `Datelike::num_days_from_ce`
|
||||
fn num_days_from_ce_alt<Date: Datelike>(date: &Date) -> i32 {
|
||||
let year = date.year();
|
||||
let diff = move |div| in_between(1, year, div);
|
||||
// 365 days a year, one more in leap years. In the gregorian calendar, leap years are all
|
||||
// the multiples of 4 except multiples of 100 but including multiples of 400.
|
||||
date.ordinal() as i32 + 365 * diff(1) + diff(4) - diff(100) + diff(400)
|
||||
}
|
||||
|
||||
fn bench_num_days_from_ce(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("num_days_from_ce");
|
||||
for year in &[1, 500, 2000, 2019] {
|
||||
let d = NaiveDate::from_ymd(*year, 1, 1);
|
||||
group.bench_with_input(BenchmarkId::new("new", year), &d, |b, y| {
|
||||
b.iter(|| num_days_from_ce_alt(y))
|
||||
});
|
||||
group.bench_with_input(BenchmarkId::new("classic", year), &d, |b, y| {
|
||||
b.iter(|| y.num_days_from_ce())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
bench_datetime_parse_from_rfc2822,
|
||||
bench_datetime_parse_from_rfc3339,
|
||||
bench_datetime_from_str,
|
||||
bench_datetime_to_rfc2822,
|
||||
bench_datetime_to_rfc3339,
|
||||
bench_year_flags_from_year,
|
||||
bench_num_days_from_ce,
|
||||
);
|
||||
|
||||
criterion_main!(benches);
|
||||
30
third_party/rust/chrono/benches/serde.rs
vendored
30
third_party/rust/chrono/benches/serde.rs
vendored
@@ -1,30 +0,0 @@
|
||||
extern crate chrono;
|
||||
extern crate criterion;
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
|
||||
use chrono::NaiveDateTime;
|
||||
|
||||
fn bench_ser_naivedatetime_string(c: &mut Criterion) {
|
||||
c.bench_function("bench_ser_naivedatetime_string", |b| {
|
||||
let dt: NaiveDateTime = "2000-01-01T00:00:00".parse().unwrap();
|
||||
b.iter(|| {
|
||||
black_box(serde_json::to_string(&dt)).unwrap();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_ser_naivedatetime_writer(c: &mut Criterion) {
|
||||
c.bench_function("bench_ser_naivedatetime_writer", |b| {
|
||||
let mut s: Vec<u8> = Vec::with_capacity(20);
|
||||
let dt: NaiveDateTime = "2000-01-01T00:00:00".parse().unwrap();
|
||||
b.iter(|| {
|
||||
let s = &mut s;
|
||||
s.clear();
|
||||
black_box(serde_json::to_writer(s, &dt)).unwrap();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, bench_ser_naivedatetime_writer, bench_ser_naivedatetime_string);
|
||||
criterion_main!(benches);
|
||||
1
third_party/rust/chrono/rustfmt.toml
vendored
1
third_party/rust/chrono/rustfmt.toml
vendored
@@ -1 +0,0 @@
|
||||
use_small_heuristics = "Max"
|
||||
293
third_party/rust/chrono/src/date.rs
vendored
293
third_party/rust/chrono/src/date.rs
vendored
@@ -2,73 +2,88 @@
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
//! ISO 8601 calendar date with time zone.
|
||||
#![allow(deprecated)]
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[cfg(feature = "alloc")]
|
||||
use core::borrow::Borrow;
|
||||
use core::cmp::Ordering;
|
||||
use core::ops::{Add, Sub};
|
||||
use core::ops::{Add, AddAssign, Sub, SubAssign};
|
||||
use core::{fmt, hash};
|
||||
use oldtime::Duration as OldDuration;
|
||||
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
use format::Locale;
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use format::{DelayedFormat, Item, StrftimeItems};
|
||||
use naive::{self, IsoWeek, NaiveDate, NaiveTime};
|
||||
use offset::{TimeZone, Utc};
|
||||
use DateTime;
|
||||
use {Datelike, Weekday};
|
||||
#[cfg(feature = "rkyv")]
|
||||
use rkyv::{Archive, Deserialize, Serialize};
|
||||
|
||||
#[cfg(all(feature = "unstable-locales", feature = "alloc"))]
|
||||
use crate::format::Locale;
|
||||
#[cfg(feature = "alloc")]
|
||||
use crate::format::{DelayedFormat, Item, StrftimeItems};
|
||||
use crate::naive::{IsoWeek, NaiveDate, NaiveTime};
|
||||
use crate::offset::{TimeZone, Utc};
|
||||
use crate::{DateTime, Datelike, TimeDelta, Weekday};
|
||||
|
||||
/// ISO 8601 calendar date with time zone.
|
||||
///
|
||||
/// This type should be considered ambiguous at best,
|
||||
/// due to the inherent lack of precision required for the time zone resolution.
|
||||
/// For serialization and deserialization uses, it is best to use `NaiveDate` instead.
|
||||
/// You almost certainly want to be using a [`NaiveDate`] instead of this type.
|
||||
///
|
||||
/// This type primarily exists to aid in the construction of DateTimes that
|
||||
/// have a timezone by way of the [`TimeZone`] datelike constructors (e.g.
|
||||
/// [`TimeZone::ymd`]).
|
||||
///
|
||||
/// This type should be considered ambiguous at best, due to the inherent lack
|
||||
/// of precision required for the time zone resolution.
|
||||
///
|
||||
/// There are some guarantees on the usage of `Date<Tz>`:
|
||||
///
|
||||
/// - If properly constructed via `TimeZone::ymd` and others without an error,
|
||||
/// - If properly constructed via [`TimeZone::ymd`] and others without an error,
|
||||
/// the corresponding local date should exist for at least a moment.
|
||||
/// (It may still have a gap from the offset changes.)
|
||||
///
|
||||
/// - The `TimeZone` is free to assign *any* `Offset` to the local date,
|
||||
/// as long as that offset did occur in given day.
|
||||
/// - The `TimeZone` is free to assign *any* [`Offset`](crate::offset::Offset) to the
|
||||
/// local date, as long as that offset did occur in given day.
|
||||
///
|
||||
/// For example, if `2015-03-08T01:59-08:00` is followed by `2015-03-08T03:00-07:00`,
|
||||
/// it may produce either `2015-03-08-08:00` or `2015-03-08-07:00`
|
||||
/// but *not* `2015-03-08+00:00` and others.
|
||||
///
|
||||
/// - Once constructed as a full `DateTime`,
|
||||
/// `DateTime::date` and other associated methods should return those for the original `Date`.
|
||||
/// For example, if `dt = tz.ymd(y,m,d).hms(h,n,s)` were valid, `dt.date() == tz.ymd(y,m,d)`.
|
||||
/// - Once constructed as a full `DateTime`, [`DateTime::date`] and other associated
|
||||
/// methods should return those for the original `Date`. For example, if `dt =
|
||||
/// tz.ymd_opt(y,m,d).unwrap().hms(h,n,s)` were valid, `dt.date() == tz.ymd_opt(y,m,d).unwrap()`.
|
||||
///
|
||||
/// - The date is timezone-agnostic up to one day (i.e. practically always),
|
||||
/// so the local date and UTC date should be equal for most cases
|
||||
/// even though the raw calculation between `NaiveDate` and `Duration` may not.
|
||||
/// even though the raw calculation between `NaiveDate` and `TimeDelta` may not.
|
||||
#[deprecated(since = "0.4.23", note = "Use `NaiveDate` or `DateTime<Tz>` instead")]
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))]
|
||||
pub struct Date<Tz: TimeZone> {
|
||||
date: NaiveDate,
|
||||
offset: Tz::Offset,
|
||||
}
|
||||
|
||||
/// The minimum possible `Date`.
|
||||
pub const MIN_DATE: Date<Utc> = Date { date: naive::MIN_DATE, offset: Utc };
|
||||
#[allow(deprecated)]
|
||||
#[deprecated(since = "0.4.20", note = "Use Date::MIN_UTC instead")]
|
||||
pub const MIN_DATE: Date<Utc> = Date::<Utc>::MIN_UTC;
|
||||
/// The maximum possible `Date`.
|
||||
pub const MAX_DATE: Date<Utc> = Date { date: naive::MAX_DATE, offset: Utc };
|
||||
#[allow(deprecated)]
|
||||
#[deprecated(since = "0.4.20", note = "Use Date::MAX_UTC instead")]
|
||||
pub const MAX_DATE: Date<Utc> = Date::<Utc>::MAX_UTC;
|
||||
|
||||
impl<Tz: TimeZone> Date<Tz> {
|
||||
/// Makes a new `Date` with given *UTC* date and offset.
|
||||
/// The local date should be constructed via the `TimeZone` trait.
|
||||
//
|
||||
// note: this constructor is purposely not named to `new` to discourage the direct usage.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn from_utc(date: NaiveDate, offset: Tz::Offset) -> Date<Tz> {
|
||||
Date { date: date, offset: offset }
|
||||
Date { date, offset }
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` from the current date and given `NaiveTime`.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Panics on invalid datetime.
|
||||
/// Returns `None` on invalid datetime.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn and_time(&self, time: NaiveTime) -> Option<DateTime<Tz>> {
|
||||
let localdt = self.naive_local().and_time(time);
|
||||
self.timezone().from_local_datetime(&localdt).single()
|
||||
@@ -78,7 +93,9 @@ impl<Tz: TimeZone> Date<Tz> {
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Panics on invalid hour, minute and/or second.
|
||||
#[deprecated(since = "0.4.23", note = "Use and_hms_opt() instead")]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn and_hms(&self, hour: u32, min: u32, sec: u32) -> DateTime<Tz> {
|
||||
self.and_hms_opt(hour, min, sec).expect("invalid time")
|
||||
}
|
||||
@@ -88,6 +105,7 @@ impl<Tz: TimeZone> Date<Tz> {
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute and/or second.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Option<DateTime<Tz>> {
|
||||
NaiveTime::from_hms_opt(hour, min, sec).and_then(|time| self.and_time(time))
|
||||
}
|
||||
@@ -97,7 +115,9 @@ impl<Tz: TimeZone> Date<Tz> {
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Panics on invalid hour, minute, second and/or millisecond.
|
||||
#[deprecated(since = "0.4.23", note = "Use and_hms_milli_opt() instead")]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn and_hms_milli(&self, hour: u32, min: u32, sec: u32, milli: u32) -> DateTime<Tz> {
|
||||
self.and_hms_milli_opt(hour, min, sec, milli).expect("invalid time")
|
||||
}
|
||||
@@ -108,6 +128,7 @@ impl<Tz: TimeZone> Date<Tz> {
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute, second and/or millisecond.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn and_hms_milli_opt(
|
||||
&self,
|
||||
hour: u32,
|
||||
@@ -123,7 +144,9 @@ impl<Tz: TimeZone> Date<Tz> {
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Panics on invalid hour, minute, second and/or microsecond.
|
||||
#[deprecated(since = "0.4.23", note = "Use and_hms_micro_opt() instead")]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn and_hms_micro(&self, hour: u32, min: u32, sec: u32, micro: u32) -> DateTime<Tz> {
|
||||
self.and_hms_micro_opt(hour, min, sec, micro).expect("invalid time")
|
||||
}
|
||||
@@ -134,6 +157,7 @@ impl<Tz: TimeZone> Date<Tz> {
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute, second and/or microsecond.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn and_hms_micro_opt(
|
||||
&self,
|
||||
hour: u32,
|
||||
@@ -149,7 +173,9 @@ impl<Tz: TimeZone> Date<Tz> {
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Panics on invalid hour, minute, second and/or nanosecond.
|
||||
#[deprecated(since = "0.4.23", note = "Use and_hms_nano_opt() instead")]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn and_hms_nano(&self, hour: u32, min: u32, sec: u32, nano: u32) -> DateTime<Tz> {
|
||||
self.and_hms_nano_opt(hour, min, sec, nano).expect("invalid time")
|
||||
}
|
||||
@@ -160,6 +186,7 @@ impl<Tz: TimeZone> Date<Tz> {
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute, second and/or nanosecond.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn and_hms_nano_opt(
|
||||
&self,
|
||||
hour: u32,
|
||||
@@ -173,7 +200,9 @@ impl<Tz: TimeZone> Date<Tz> {
|
||||
/// Makes a new `Date` for the next date.
|
||||
///
|
||||
/// Panics when `self` is the last representable date.
|
||||
#[deprecated(since = "0.4.23", note = "Use succ_opt() instead")]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn succ(&self) -> Date<Tz> {
|
||||
self.succ_opt().expect("out of bound")
|
||||
}
|
||||
@@ -182,6 +211,7 @@ impl<Tz: TimeZone> Date<Tz> {
|
||||
///
|
||||
/// Returns `None` when `self` is the last representable date.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn succ_opt(&self) -> Option<Date<Tz>> {
|
||||
self.date.succ_opt().map(|date| Date::from_utc(date, self.offset.clone()))
|
||||
}
|
||||
@@ -189,7 +219,9 @@ impl<Tz: TimeZone> Date<Tz> {
|
||||
/// Makes a new `Date` for the prior date.
|
||||
///
|
||||
/// Panics when `self` is the first representable date.
|
||||
#[deprecated(since = "0.4.23", note = "Use pred_opt() instead")]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn pred(&self) -> Date<Tz> {
|
||||
self.pred_opt().expect("out of bound")
|
||||
}
|
||||
@@ -198,18 +230,21 @@ impl<Tz: TimeZone> Date<Tz> {
|
||||
///
|
||||
/// Returns `None` when `self` is the first representable date.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn pred_opt(&self) -> Option<Date<Tz>> {
|
||||
self.date.pred_opt().map(|date| Date::from_utc(date, self.offset.clone()))
|
||||
}
|
||||
|
||||
/// Retrieves an associated offset from UTC.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn offset(&self) -> &Tz::Offset {
|
||||
&self.offset
|
||||
}
|
||||
|
||||
/// Retrieves an associated time zone.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn timezone(&self) -> Tz {
|
||||
TimeZone::from_offset(&self.offset)
|
||||
}
|
||||
@@ -217,40 +252,45 @@ impl<Tz: TimeZone> Date<Tz> {
|
||||
/// Changes the associated time zone.
|
||||
/// This does not change the actual `Date` (but will change the string representation).
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn with_timezone<Tz2: TimeZone>(&self, tz: &Tz2) -> Date<Tz2> {
|
||||
tz.from_utc_date(&self.date)
|
||||
}
|
||||
|
||||
/// Adds given `Duration` to the current date.
|
||||
/// Adds given `TimeDelta` to the current date.
|
||||
///
|
||||
/// Returns `None` when it will result in overflow.
|
||||
#[inline]
|
||||
pub fn checked_add_signed(self, rhs: OldDuration) -> Option<Date<Tz>> {
|
||||
let date = try_opt!(self.date.checked_add_signed(rhs));
|
||||
Some(Date { date: date, offset: self.offset })
|
||||
#[must_use]
|
||||
pub fn checked_add_signed(self, rhs: TimeDelta) -> Option<Date<Tz>> {
|
||||
let date = self.date.checked_add_signed(rhs)?;
|
||||
Some(Date { date, offset: self.offset })
|
||||
}
|
||||
|
||||
/// Subtracts given `Duration` from the current date.
|
||||
/// Subtracts given `TimeDelta` from the current date.
|
||||
///
|
||||
/// Returns `None` when it will result in overflow.
|
||||
#[inline]
|
||||
pub fn checked_sub_signed(self, rhs: OldDuration) -> Option<Date<Tz>> {
|
||||
let date = try_opt!(self.date.checked_sub_signed(rhs));
|
||||
Some(Date { date: date, offset: self.offset })
|
||||
#[must_use]
|
||||
pub fn checked_sub_signed(self, rhs: TimeDelta) -> Option<Date<Tz>> {
|
||||
let date = self.date.checked_sub_signed(rhs)?;
|
||||
Some(Date { date, offset: self.offset })
|
||||
}
|
||||
|
||||
/// Subtracts another `Date` from the current date.
|
||||
/// Returns a `Duration` of integral numbers.
|
||||
/// Returns a `TimeDelta` of integral numbers.
|
||||
///
|
||||
/// This does not overflow or underflow at all,
|
||||
/// as all possible output fits in the range of `Duration`.
|
||||
/// as all possible output fits in the range of `TimeDelta`.
|
||||
#[inline]
|
||||
pub fn signed_duration_since<Tz2: TimeZone>(self, rhs: Date<Tz2>) -> OldDuration {
|
||||
#[must_use]
|
||||
pub fn signed_duration_since<Tz2: TimeZone>(self, rhs: Date<Tz2>) -> TimeDelta {
|
||||
self.date.signed_duration_since(rhs.date)
|
||||
}
|
||||
|
||||
/// Returns a view to the naive UTC date.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn naive_utc(&self) -> NaiveDate {
|
||||
self.date
|
||||
}
|
||||
@@ -261,9 +301,21 @@ impl<Tz: TimeZone> Date<Tz> {
|
||||
/// because the offset is restricted to never exceed one day,
|
||||
/// but provided for the consistency.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn naive_local(&self) -> NaiveDate {
|
||||
self.date
|
||||
}
|
||||
|
||||
/// Returns the number of whole years from the given `base` until `self`.
|
||||
#[must_use]
|
||||
pub fn years_since(&self, base: Self) -> Option<u32> {
|
||||
self.date.years_since(base.date)
|
||||
}
|
||||
|
||||
/// The minimum possible `Date`.
|
||||
pub const MIN_UTC: Date<Utc> = Date { date: NaiveDate::MIN, offset: Utc };
|
||||
/// The maximum possible `Date`.
|
||||
pub const MAX_UTC: Date<Utc> = Date { date: NaiveDate::MAX, offset: Utc };
|
||||
}
|
||||
|
||||
/// Maps the local date to other date with given conversion function.
|
||||
@@ -279,8 +331,9 @@ where
|
||||
Tz::Offset: fmt::Display,
|
||||
{
|
||||
/// Formats the date with the specified formatting items.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[cfg(feature = "alloc")]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
|
||||
where
|
||||
I: Iterator<Item = B> + Clone,
|
||||
@@ -290,17 +343,19 @@ where
|
||||
}
|
||||
|
||||
/// Formats the date with the specified format string.
|
||||
/// See the [`format::strftime` module](./format/strftime/index.html)
|
||||
/// See the [`crate::format::strftime`] module
|
||||
/// on the supported escape sequences.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[cfg(feature = "alloc")]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
|
||||
self.format_with_items(StrftimeItems::new(fmt))
|
||||
}
|
||||
|
||||
/// Formats the date with the specified formatting items and locale.
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
#[cfg(all(feature = "unstable-locales", feature = "alloc"))]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn format_localized_with_items<'a, I, B>(
|
||||
&self,
|
||||
items: I,
|
||||
@@ -320,10 +375,11 @@ where
|
||||
}
|
||||
|
||||
/// Formats the date with the specified format string and locale.
|
||||
/// See the [`format::strftime` module](./format/strftime/index.html)
|
||||
/// See the [`crate::format::strftime`] module
|
||||
/// on the supported escape sequences.
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
#[cfg(all(feature = "unstable-locales", feature = "alloc"))]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn format_localized<'a>(
|
||||
&self,
|
||||
fmt: &'a str,
|
||||
@@ -421,7 +477,7 @@ impl<Tz: TimeZone> Eq for Date<Tz> {}
|
||||
|
||||
impl<Tz: TimeZone> PartialOrd for Date<Tz> {
|
||||
fn partial_cmp(&self, other: &Date<Tz>) -> Option<Ordering> {
|
||||
self.date.partial_cmp(&other.date)
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,36 +493,51 @@ impl<Tz: TimeZone> hash::Hash for Date<Tz> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> Add<OldDuration> for Date<Tz> {
|
||||
impl<Tz: TimeZone> Add<TimeDelta> for Date<Tz> {
|
||||
type Output = Date<Tz>;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: OldDuration) -> Date<Tz> {
|
||||
self.checked_add_signed(rhs).expect("`Date + Duration` overflowed")
|
||||
fn add(self, rhs: TimeDelta) -> Date<Tz> {
|
||||
self.checked_add_signed(rhs).expect("`Date + TimeDelta` overflowed")
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> Sub<OldDuration> for Date<Tz> {
|
||||
impl<Tz: TimeZone> AddAssign<TimeDelta> for Date<Tz> {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: TimeDelta) {
|
||||
self.date = self.date.checked_add_signed(rhs).expect("`Date + TimeDelta` overflowed");
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> Sub<TimeDelta> for Date<Tz> {
|
||||
type Output = Date<Tz>;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: OldDuration) -> Date<Tz> {
|
||||
self.checked_sub_signed(rhs).expect("`Date - Duration` overflowed")
|
||||
fn sub(self, rhs: TimeDelta) -> Date<Tz> {
|
||||
self.checked_sub_signed(rhs).expect("`Date - TimeDelta` overflowed")
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> SubAssign<TimeDelta> for Date<Tz> {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: TimeDelta) {
|
||||
self.date = self.date.checked_sub_signed(rhs).expect("`Date - TimeDelta` overflowed");
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> Sub<Date<Tz>> for Date<Tz> {
|
||||
type Output = OldDuration;
|
||||
type Output = TimeDelta;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: Date<Tz>) -> OldDuration {
|
||||
fn sub(self, rhs: Date<Tz>) -> TimeDelta {
|
||||
self.signed_duration_since(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> fmt::Debug for Date<Tz> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}{:?}", self.naive_local(), self.offset)
|
||||
self.naive_local().fmt(f)?;
|
||||
self.offset.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,6 +546,118 @@ where
|
||||
Tz::Offset: fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}{}", self.naive_local(), self.offset)
|
||||
self.naive_local().fmt(f)?;
|
||||
self.offset.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
// Note that implementation of Arbitrary cannot be automatically derived for Date<Tz>, due to
|
||||
// the nontrivial bound <Tz as TimeZone>::Offset: Arbitrary.
|
||||
#[cfg(all(feature = "arbitrary", feature = "std"))]
|
||||
impl<'a, Tz> arbitrary::Arbitrary<'a> for Date<Tz>
|
||||
where
|
||||
Tz: TimeZone,
|
||||
<Tz as TimeZone>::Offset: arbitrary::Arbitrary<'a>,
|
||||
{
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Date<Tz>> {
|
||||
let date = NaiveDate::arbitrary(u)?;
|
||||
let offset = <Tz as TimeZone>::Offset::arbitrary(u)?;
|
||||
Ok(Date::from_utc(date, offset))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Date;
|
||||
|
||||
use crate::{FixedOffset, NaiveDate, TimeDelta, Utc};
|
||||
|
||||
#[cfg(feature = "clock")]
|
||||
use crate::offset::{Local, TimeZone};
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "clock")]
|
||||
fn test_years_elapsed() {
|
||||
const WEEKS_PER_YEAR: f32 = 52.1775;
|
||||
|
||||
// This is always at least one year because 1 year = 52.1775 weeks.
|
||||
let one_year_ago = Utc::today() - TimeDelta::weeks((WEEKS_PER_YEAR * 1.5).ceil() as i64);
|
||||
// A bit more than 2 years.
|
||||
let two_year_ago = Utc::today() - TimeDelta::weeks((WEEKS_PER_YEAR * 2.5).ceil() as i64);
|
||||
|
||||
assert_eq!(Utc::today().years_since(one_year_ago), Some(1));
|
||||
assert_eq!(Utc::today().years_since(two_year_ago), Some(2));
|
||||
|
||||
// If the given DateTime is later than now, the function will always return 0.
|
||||
let future = Utc::today() + TimeDelta::weeks(12);
|
||||
assert_eq!(Utc::today().years_since(future), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_add_assign() {
|
||||
let naivedate = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap();
|
||||
let date = Date::<Utc>::from_utc(naivedate, Utc);
|
||||
let mut date_add = date;
|
||||
|
||||
date_add += TimeDelta::days(5);
|
||||
assert_eq!(date_add, date + TimeDelta::days(5));
|
||||
|
||||
let timezone = FixedOffset::east_opt(60 * 60).unwrap();
|
||||
let date = date.with_timezone(&timezone);
|
||||
let date_add = date_add.with_timezone(&timezone);
|
||||
|
||||
assert_eq!(date_add, date + TimeDelta::days(5));
|
||||
|
||||
let timezone = FixedOffset::west_opt(2 * 60 * 60).unwrap();
|
||||
let date = date.with_timezone(&timezone);
|
||||
let date_add = date_add.with_timezone(&timezone);
|
||||
|
||||
assert_eq!(date_add, date + TimeDelta::days(5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "clock")]
|
||||
fn test_date_add_assign_local() {
|
||||
let naivedate = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap();
|
||||
|
||||
let date = Local.from_utc_date(&naivedate);
|
||||
let mut date_add = date;
|
||||
|
||||
date_add += TimeDelta::days(5);
|
||||
assert_eq!(date_add, date + TimeDelta::days(5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_sub_assign() {
|
||||
let naivedate = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap();
|
||||
let date = Date::<Utc>::from_utc(naivedate, Utc);
|
||||
let mut date_sub = date;
|
||||
|
||||
date_sub -= TimeDelta::days(5);
|
||||
assert_eq!(date_sub, date - TimeDelta::days(5));
|
||||
|
||||
let timezone = FixedOffset::east_opt(60 * 60).unwrap();
|
||||
let date = date.with_timezone(&timezone);
|
||||
let date_sub = date_sub.with_timezone(&timezone);
|
||||
|
||||
assert_eq!(date_sub, date - TimeDelta::days(5));
|
||||
|
||||
let timezone = FixedOffset::west_opt(2 * 60 * 60).unwrap();
|
||||
let date = date.with_timezone(&timezone);
|
||||
let date_sub = date_sub.with_timezone(&timezone);
|
||||
|
||||
assert_eq!(date_sub, date - TimeDelta::days(5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "clock")]
|
||||
fn test_date_sub_assign_local() {
|
||||
let naivedate = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap();
|
||||
|
||||
let date = Local.from_utc_date(&naivedate);
|
||||
let mut date_sub = date;
|
||||
|
||||
date_sub -= TimeDelta::days(5);
|
||||
assert_eq!(date_sub, date - TimeDelta::days(5));
|
||||
}
|
||||
}
|
||||
|
||||
2589
third_party/rust/chrono/src/datetime.rs
vendored
2589
third_party/rust/chrono/src/datetime.rs
vendored
File diff suppressed because it is too large
Load Diff
1940
third_party/rust/chrono/src/datetime/mod.rs
vendored
Normal file
1940
third_party/rust/chrono/src/datetime/mod.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1359
third_party/rust/chrono/src/datetime/serde.rs
vendored
Normal file
1359
third_party/rust/chrono/src/datetime/serde.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1902
third_party/rust/chrono/src/datetime/tests.rs
vendored
Normal file
1902
third_party/rust/chrono/src/datetime/tests.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
41
third_party/rust/chrono/src/div.rs
vendored
41
third_party/rust/chrono/src/div.rs
vendored
@@ -1,41 +0,0 @@
|
||||
// This is a part of Chrono.
|
||||
// Portions Copyright 2013-2014 The Rust Project Developers.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
//! Integer division utilities. (Shamelessly copied from [num](https://github.com/rust-lang/num/))
|
||||
|
||||
// Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_,
|
||||
// December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf)
|
||||
|
||||
pub use num_integer::{div_floor, div_mod_floor, div_rem, mod_floor};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{div_mod_floor, mod_floor};
|
||||
|
||||
#[test]
|
||||
fn test_mod_floor() {
|
||||
assert_eq!(mod_floor(8, 3), 2);
|
||||
assert_eq!(mod_floor(8, -3), -1);
|
||||
assert_eq!(mod_floor(-8, 3), 1);
|
||||
assert_eq!(mod_floor(-8, -3), -2);
|
||||
|
||||
assert_eq!(mod_floor(1, 2), 1);
|
||||
assert_eq!(mod_floor(1, -2), -1);
|
||||
assert_eq!(mod_floor(-1, 2), 1);
|
||||
assert_eq!(mod_floor(-1, -2), -1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_mod_floor() {
|
||||
assert_eq!(div_mod_floor(8, 3), (2, 2));
|
||||
assert_eq!(div_mod_floor(8, -3), (-3, -1));
|
||||
assert_eq!(div_mod_floor(-8, 3), (-3, 1));
|
||||
assert_eq!(div_mod_floor(-8, -3), (2, -2));
|
||||
|
||||
assert_eq!(div_mod_floor(1, 2), (0, 1));
|
||||
assert_eq!(div_mod_floor(1, -2), (-1, -1));
|
||||
assert_eq!(div_mod_floor(-1, 2), (-1, 1));
|
||||
assert_eq!(div_mod_floor(-1, -2), (0, -1));
|
||||
}
|
||||
}
|
||||
945
third_party/rust/chrono/src/format/formatting.rs
vendored
Normal file
945
third_party/rust/chrono/src/format/formatting.rs
vendored
Normal file
@@ -0,0 +1,945 @@
|
||||
// This is a part of Chrono.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
//! Date and time formatting routines.
|
||||
|
||||
#[cfg(all(feature = "alloc", not(feature = "std"), not(test)))]
|
||||
use alloc::string::{String, ToString};
|
||||
#[cfg(feature = "alloc")]
|
||||
use core::borrow::Borrow;
|
||||
#[cfg(feature = "alloc")]
|
||||
use core::fmt::Display;
|
||||
use core::fmt::{self, Write};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use crate::offset::Offset;
|
||||
#[cfg(any(feature = "alloc", feature = "serde"))]
|
||||
use crate::{Datelike, FixedOffset, NaiveDateTime, Timelike};
|
||||
#[cfg(feature = "alloc")]
|
||||
use crate::{NaiveDate, NaiveTime, Weekday};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use super::locales;
|
||||
#[cfg(any(feature = "alloc", feature = "serde"))]
|
||||
use super::{Colons, OffsetFormat, OffsetPrecision, Pad};
|
||||
#[cfg(feature = "alloc")]
|
||||
use super::{Fixed, InternalFixed, InternalInternal, Item, Numeric};
|
||||
#[cfg(feature = "alloc")]
|
||||
use locales::*;
|
||||
|
||||
/// A *temporary* object which can be used as an argument to `format!` or others.
|
||||
/// This is normally constructed via `format` methods of each date and time type.
|
||||
#[cfg(feature = "alloc")]
|
||||
#[derive(Debug)]
|
||||
pub struct DelayedFormat<I> {
|
||||
/// The date view, if any.
|
||||
date: Option<NaiveDate>,
|
||||
/// The time view, if any.
|
||||
time: Option<NaiveTime>,
|
||||
/// The name and local-to-UTC difference for the offset (timezone), if any.
|
||||
off: Option<(String, FixedOffset)>,
|
||||
/// An iterator returning formatting items.
|
||||
items: I,
|
||||
/// Locale used for text.
|
||||
/// ZST if the `unstable-locales` feature is not enabled.
|
||||
locale: Locale,
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> {
|
||||
/// Makes a new `DelayedFormat` value out of local date and time.
|
||||
#[must_use]
|
||||
pub fn new(date: Option<NaiveDate>, time: Option<NaiveTime>, items: I) -> DelayedFormat<I> {
|
||||
DelayedFormat { date, time, off: None, items, locale: default_locale() }
|
||||
}
|
||||
|
||||
/// Makes a new `DelayedFormat` value out of local date and time and UTC offset.
|
||||
#[must_use]
|
||||
pub fn new_with_offset<Off>(
|
||||
date: Option<NaiveDate>,
|
||||
time: Option<NaiveTime>,
|
||||
offset: &Off,
|
||||
items: I,
|
||||
) -> DelayedFormat<I>
|
||||
where
|
||||
Off: Offset + Display,
|
||||
{
|
||||
let name_and_diff = (offset.to_string(), offset.fix());
|
||||
DelayedFormat { date, time, off: Some(name_and_diff), items, locale: default_locale() }
|
||||
}
|
||||
|
||||
/// Makes a new `DelayedFormat` value out of local date and time and locale.
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
#[must_use]
|
||||
pub fn new_with_locale(
|
||||
date: Option<NaiveDate>,
|
||||
time: Option<NaiveTime>,
|
||||
items: I,
|
||||
locale: Locale,
|
||||
) -> DelayedFormat<I> {
|
||||
DelayedFormat { date, time, off: None, items, locale }
|
||||
}
|
||||
|
||||
/// Makes a new `DelayedFormat` value out of local date and time, UTC offset and locale.
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
#[must_use]
|
||||
pub fn new_with_offset_and_locale<Off>(
|
||||
date: Option<NaiveDate>,
|
||||
time: Option<NaiveTime>,
|
||||
offset: &Off,
|
||||
items: I,
|
||||
locale: Locale,
|
||||
) -> DelayedFormat<I>
|
||||
where
|
||||
Off: Offset + Display,
|
||||
{
|
||||
let name_and_diff = (offset.to_string(), offset.fix());
|
||||
DelayedFormat { date, time, off: Some(name_and_diff), items, locale }
|
||||
}
|
||||
|
||||
/// Formats `DelayedFormat` into a `core::fmt::Write` instance.
|
||||
/// # Errors
|
||||
/// This function returns a `core::fmt::Error` if formatting into the `core::fmt::Write` instance fails.
|
||||
///
|
||||
/// # Example
|
||||
/// ### Writing to a String
|
||||
/// ```
|
||||
/// let dt = chrono::DateTime::from_timestamp(1643723400, 123456789).unwrap();
|
||||
/// let df = dt.format("%Y-%m-%d %H:%M:%S%.9f");
|
||||
/// let mut buffer = String::new();
|
||||
/// let _ = df.write_to(&mut buffer);
|
||||
/// ```
|
||||
pub fn write_to(&self, w: &mut impl Write) -> fmt::Result {
|
||||
for item in self.items.clone() {
|
||||
match *item.borrow() {
|
||||
Item::Literal(s) | Item::Space(s) => w.write_str(s),
|
||||
#[cfg(feature = "alloc")]
|
||||
Item::OwnedLiteral(ref s) | Item::OwnedSpace(ref s) => w.write_str(s),
|
||||
Item::Numeric(ref spec, pad) => self.format_numeric(w, spec, pad),
|
||||
Item::Fixed(ref spec) => self.format_fixed(w, spec),
|
||||
Item::Error => Err(fmt::Error),
|
||||
}?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
fn format_numeric(&self, w: &mut impl Write, spec: &Numeric, pad: Pad) -> fmt::Result {
|
||||
use self::Numeric::*;
|
||||
|
||||
fn write_one(w: &mut impl Write, v: u8) -> fmt::Result {
|
||||
w.write_char((b'0' + v) as char)
|
||||
}
|
||||
|
||||
fn write_two(w: &mut impl Write, v: u8, pad: Pad) -> fmt::Result {
|
||||
let ones = b'0' + v % 10;
|
||||
match (v / 10, pad) {
|
||||
(0, Pad::None) => {}
|
||||
(0, Pad::Space) => w.write_char(' ')?,
|
||||
(tens, _) => w.write_char((b'0' + tens) as char)?,
|
||||
}
|
||||
w.write_char(ones as char)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_year(w: &mut impl Write, year: i32, pad: Pad) -> fmt::Result {
|
||||
if (1000..=9999).contains(&year) {
|
||||
// fast path
|
||||
write_hundreds(w, (year / 100) as u8)?;
|
||||
write_hundreds(w, (year % 100) as u8)
|
||||
} else {
|
||||
write_n(w, 4, year as i64, pad, !(0..10_000).contains(&year))
|
||||
}
|
||||
}
|
||||
|
||||
fn write_n(
|
||||
w: &mut impl Write,
|
||||
n: usize,
|
||||
v: i64,
|
||||
pad: Pad,
|
||||
always_sign: bool,
|
||||
) -> fmt::Result {
|
||||
if always_sign {
|
||||
match pad {
|
||||
Pad::None => write!(w, "{:+}", v),
|
||||
Pad::Zero => write!(w, "{:+01$}", v, n + 1),
|
||||
Pad::Space => write!(w, "{:+1$}", v, n + 1),
|
||||
}
|
||||
} else {
|
||||
match pad {
|
||||
Pad::None => write!(w, "{}", v),
|
||||
Pad::Zero => write!(w, "{:01$}", v, n),
|
||||
Pad::Space => write!(w, "{:1$}", v, n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match (spec, self.date, self.time) {
|
||||
(Year, Some(d), _) => write_year(w, d.year(), pad),
|
||||
(YearDiv100, Some(d), _) => write_two(w, d.year().div_euclid(100) as u8, pad),
|
||||
(YearMod100, Some(d), _) => write_two(w, d.year().rem_euclid(100) as u8, pad),
|
||||
(IsoYear, Some(d), _) => write_year(w, d.iso_week().year(), pad),
|
||||
(IsoYearDiv100, Some(d), _) => {
|
||||
write_two(w, d.iso_week().year().div_euclid(100) as u8, pad)
|
||||
}
|
||||
(IsoYearMod100, Some(d), _) => {
|
||||
write_two(w, d.iso_week().year().rem_euclid(100) as u8, pad)
|
||||
}
|
||||
(Quarter, Some(d), _) => write_one(w, d.quarter() as u8),
|
||||
(Month, Some(d), _) => write_two(w, d.month() as u8, pad),
|
||||
(Day, Some(d), _) => write_two(w, d.day() as u8, pad),
|
||||
(WeekFromSun, Some(d), _) => write_two(w, d.weeks_from(Weekday::Sun) as u8, pad),
|
||||
(WeekFromMon, Some(d), _) => write_two(w, d.weeks_from(Weekday::Mon) as u8, pad),
|
||||
(IsoWeek, Some(d), _) => write_two(w, d.iso_week().week() as u8, pad),
|
||||
(NumDaysFromSun, Some(d), _) => write_one(w, d.weekday().num_days_from_sunday() as u8),
|
||||
(WeekdayFromMon, Some(d), _) => write_one(w, d.weekday().number_from_monday() as u8),
|
||||
(Ordinal, Some(d), _) => write_n(w, 3, d.ordinal() as i64, pad, false),
|
||||
(Hour, _, Some(t)) => write_two(w, t.hour() as u8, pad),
|
||||
(Hour12, _, Some(t)) => write_two(w, t.hour12().1 as u8, pad),
|
||||
(Minute, _, Some(t)) => write_two(w, t.minute() as u8, pad),
|
||||
(Second, _, Some(t)) => {
|
||||
write_two(w, (t.second() + t.nanosecond() / 1_000_000_000) as u8, pad)
|
||||
}
|
||||
(Nanosecond, _, Some(t)) => {
|
||||
write_n(w, 9, (t.nanosecond() % 1_000_000_000) as i64, pad, false)
|
||||
}
|
||||
(Timestamp, Some(d), Some(t)) => {
|
||||
let offset = self.off.as_ref().map(|(_, o)| i64::from(o.local_minus_utc()));
|
||||
let timestamp = d.and_time(t).and_utc().timestamp() - offset.unwrap_or(0);
|
||||
write_n(w, 9, timestamp, pad, false)
|
||||
}
|
||||
(Internal(_), _, _) => Ok(()), // for future expansion
|
||||
_ => Err(fmt::Error), // insufficient arguments for given format
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
fn format_fixed(&self, w: &mut impl Write, spec: &Fixed) -> fmt::Result {
|
||||
use Fixed::*;
|
||||
use InternalInternal::*;
|
||||
|
||||
match (spec, self.date, self.time, self.off.as_ref()) {
|
||||
(ShortMonthName, Some(d), _, _) => {
|
||||
w.write_str(short_months(self.locale)[d.month0() as usize])
|
||||
}
|
||||
(LongMonthName, Some(d), _, _) => {
|
||||
w.write_str(long_months(self.locale)[d.month0() as usize])
|
||||
}
|
||||
(ShortWeekdayName, Some(d), _, _) => w.write_str(
|
||||
short_weekdays(self.locale)[d.weekday().num_days_from_sunday() as usize],
|
||||
),
|
||||
(LongWeekdayName, Some(d), _, _) => {
|
||||
w.write_str(long_weekdays(self.locale)[d.weekday().num_days_from_sunday() as usize])
|
||||
}
|
||||
(LowerAmPm, _, Some(t), _) => {
|
||||
let ampm = if t.hour12().0 { am_pm(self.locale)[1] } else { am_pm(self.locale)[0] };
|
||||
for c in ampm.chars().flat_map(|c| c.to_lowercase()) {
|
||||
w.write_char(c)?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
(UpperAmPm, _, Some(t), _) => {
|
||||
let ampm = if t.hour12().0 { am_pm(self.locale)[1] } else { am_pm(self.locale)[0] };
|
||||
w.write_str(ampm)
|
||||
}
|
||||
(Nanosecond, _, Some(t), _) => {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
if nano == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
w.write_str(decimal_point(self.locale))?;
|
||||
if nano % 1_000_000 == 0 {
|
||||
write!(w, "{:03}", nano / 1_000_000)
|
||||
} else if nano % 1_000 == 0 {
|
||||
write!(w, "{:06}", nano / 1_000)
|
||||
} else {
|
||||
write!(w, "{:09}", nano)
|
||||
}
|
||||
}
|
||||
}
|
||||
(Nanosecond3, _, Some(t), _) => {
|
||||
w.write_str(decimal_point(self.locale))?;
|
||||
write!(w, "{:03}", t.nanosecond() / 1_000_000 % 1000)
|
||||
}
|
||||
(Nanosecond6, _, Some(t), _) => {
|
||||
w.write_str(decimal_point(self.locale))?;
|
||||
write!(w, "{:06}", t.nanosecond() / 1_000 % 1_000_000)
|
||||
}
|
||||
(Nanosecond9, _, Some(t), _) => {
|
||||
w.write_str(decimal_point(self.locale))?;
|
||||
write!(w, "{:09}", t.nanosecond() % 1_000_000_000)
|
||||
}
|
||||
(Internal(InternalFixed { val: Nanosecond3NoDot }), _, Some(t), _) => {
|
||||
write!(w, "{:03}", t.nanosecond() / 1_000_000 % 1_000)
|
||||
}
|
||||
(Internal(InternalFixed { val: Nanosecond6NoDot }), _, Some(t), _) => {
|
||||
write!(w, "{:06}", t.nanosecond() / 1_000 % 1_000_000)
|
||||
}
|
||||
(Internal(InternalFixed { val: Nanosecond9NoDot }), _, Some(t), _) => {
|
||||
write!(w, "{:09}", t.nanosecond() % 1_000_000_000)
|
||||
}
|
||||
(TimezoneName, _, _, Some((tz_name, _))) => write!(w, "{}", tz_name),
|
||||
(TimezoneOffset | TimezoneOffsetZ, _, _, Some((_, off))) => {
|
||||
let offset_format = OffsetFormat {
|
||||
precision: OffsetPrecision::Minutes,
|
||||
colons: Colons::Maybe,
|
||||
allow_zulu: *spec == TimezoneOffsetZ,
|
||||
padding: Pad::Zero,
|
||||
};
|
||||
offset_format.format(w, *off)
|
||||
}
|
||||
(TimezoneOffsetColon | TimezoneOffsetColonZ, _, _, Some((_, off))) => {
|
||||
let offset_format = OffsetFormat {
|
||||
precision: OffsetPrecision::Minutes,
|
||||
colons: Colons::Colon,
|
||||
allow_zulu: *spec == TimezoneOffsetColonZ,
|
||||
padding: Pad::Zero,
|
||||
};
|
||||
offset_format.format(w, *off)
|
||||
}
|
||||
(TimezoneOffsetDoubleColon, _, _, Some((_, off))) => {
|
||||
let offset_format = OffsetFormat {
|
||||
precision: OffsetPrecision::Seconds,
|
||||
colons: Colons::Colon,
|
||||
allow_zulu: false,
|
||||
padding: Pad::Zero,
|
||||
};
|
||||
offset_format.format(w, *off)
|
||||
}
|
||||
(TimezoneOffsetTripleColon, _, _, Some((_, off))) => {
|
||||
let offset_format = OffsetFormat {
|
||||
precision: OffsetPrecision::Hours,
|
||||
colons: Colons::None,
|
||||
allow_zulu: false,
|
||||
padding: Pad::Zero,
|
||||
};
|
||||
offset_format.format(w, *off)
|
||||
}
|
||||
(RFC2822, Some(d), Some(t), Some((_, off))) => {
|
||||
write_rfc2822(w, crate::NaiveDateTime::new(d, t), *off)
|
||||
}
|
||||
(RFC3339, Some(d), Some(t), Some((_, off))) => write_rfc3339(
|
||||
w,
|
||||
crate::NaiveDateTime::new(d, t),
|
||||
*off,
|
||||
SecondsFormat::AutoSi,
|
||||
false,
|
||||
),
|
||||
_ => Err(fmt::Error), // insufficient arguments for given format
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> Display for DelayedFormat<I> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut result = String::new();
|
||||
self.write_to(&mut result)?;
|
||||
f.pad(&result)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to format given arguments with given formatting items.
|
||||
/// Internally used by `DelayedFormat`.
|
||||
#[cfg(feature = "alloc")]
|
||||
#[deprecated(since = "0.4.32", note = "Use DelayedFormat::fmt or DelayedFormat::write_to instead")]
|
||||
pub fn format<'a, I, B>(
|
||||
w: &mut fmt::Formatter,
|
||||
date: Option<&NaiveDate>,
|
||||
time: Option<&NaiveTime>,
|
||||
off: Option<&(String, FixedOffset)>,
|
||||
items: I,
|
||||
) -> fmt::Result
|
||||
where
|
||||
I: Iterator<Item = B> + Clone,
|
||||
B: Borrow<Item<'a>>,
|
||||
{
|
||||
DelayedFormat {
|
||||
date: date.copied(),
|
||||
time: time.copied(),
|
||||
off: off.cloned(),
|
||||
items,
|
||||
locale: default_locale(),
|
||||
}
|
||||
.fmt(w)
|
||||
}
|
||||
|
||||
/// Formats single formatting item
|
||||
#[cfg(feature = "alloc")]
|
||||
#[deprecated(since = "0.4.32", note = "Use DelayedFormat::fmt or DelayedFormat::write_to instead")]
|
||||
pub fn format_item(
|
||||
w: &mut fmt::Formatter,
|
||||
date: Option<&NaiveDate>,
|
||||
time: Option<&NaiveTime>,
|
||||
off: Option<&(String, FixedOffset)>,
|
||||
item: &Item<'_>,
|
||||
) -> fmt::Result {
|
||||
DelayedFormat {
|
||||
date: date.copied(),
|
||||
time: time.copied(),
|
||||
off: off.cloned(),
|
||||
items: [item].into_iter(),
|
||||
locale: default_locale(),
|
||||
}
|
||||
.fmt(w)
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "serde"))]
|
||||
impl OffsetFormat {
|
||||
/// Writes an offset from UTC with the format defined by `self`.
|
||||
fn format(&self, w: &mut impl Write, off: FixedOffset) -> fmt::Result {
|
||||
let off = off.local_minus_utc();
|
||||
if self.allow_zulu && off == 0 {
|
||||
w.write_char('Z')?;
|
||||
return Ok(());
|
||||
}
|
||||
let (sign, off) = if off < 0 { ('-', -off) } else { ('+', off) };
|
||||
|
||||
let hours;
|
||||
let mut mins = 0;
|
||||
let mut secs = 0;
|
||||
let precision = match self.precision {
|
||||
OffsetPrecision::Hours => {
|
||||
// Minutes and seconds are simply truncated
|
||||
hours = (off / 3600) as u8;
|
||||
OffsetPrecision::Hours
|
||||
}
|
||||
OffsetPrecision::Minutes | OffsetPrecision::OptionalMinutes => {
|
||||
// Round seconds to the nearest minute.
|
||||
let minutes = (off + 30) / 60;
|
||||
mins = (minutes % 60) as u8;
|
||||
hours = (minutes / 60) as u8;
|
||||
if self.precision == OffsetPrecision::OptionalMinutes && mins == 0 {
|
||||
OffsetPrecision::Hours
|
||||
} else {
|
||||
OffsetPrecision::Minutes
|
||||
}
|
||||
}
|
||||
OffsetPrecision::Seconds
|
||||
| OffsetPrecision::OptionalSeconds
|
||||
| OffsetPrecision::OptionalMinutesAndSeconds => {
|
||||
let minutes = off / 60;
|
||||
secs = (off % 60) as u8;
|
||||
mins = (minutes % 60) as u8;
|
||||
hours = (minutes / 60) as u8;
|
||||
if self.precision != OffsetPrecision::Seconds && secs == 0 {
|
||||
if self.precision == OffsetPrecision::OptionalMinutesAndSeconds && mins == 0 {
|
||||
OffsetPrecision::Hours
|
||||
} else {
|
||||
OffsetPrecision::Minutes
|
||||
}
|
||||
} else {
|
||||
OffsetPrecision::Seconds
|
||||
}
|
||||
}
|
||||
};
|
||||
let colons = self.colons == Colons::Colon;
|
||||
|
||||
if hours < 10 {
|
||||
if self.padding == Pad::Space {
|
||||
w.write_char(' ')?;
|
||||
}
|
||||
w.write_char(sign)?;
|
||||
if self.padding == Pad::Zero {
|
||||
w.write_char('0')?;
|
||||
}
|
||||
w.write_char((b'0' + hours) as char)?;
|
||||
} else {
|
||||
w.write_char(sign)?;
|
||||
write_hundreds(w, hours)?;
|
||||
}
|
||||
if let OffsetPrecision::Minutes | OffsetPrecision::Seconds = precision {
|
||||
if colons {
|
||||
w.write_char(':')?;
|
||||
}
|
||||
write_hundreds(w, mins)?;
|
||||
}
|
||||
if let OffsetPrecision::Seconds = precision {
|
||||
if colons {
|
||||
w.write_char(':')?;
|
||||
}
|
||||
write_hundreds(w, secs)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Specific formatting options for seconds. This may be extended in the
|
||||
/// future, so exhaustive matching in external code is not recommended.
|
||||
///
|
||||
/// See the `TimeZone::to_rfc3339_opts` function for usage.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
||||
#[allow(clippy::manual_non_exhaustive)]
|
||||
pub enum SecondsFormat {
|
||||
/// Format whole seconds only, with no decimal point nor subseconds.
|
||||
Secs,
|
||||
|
||||
/// Use fixed 3 subsecond digits. This corresponds to [Fixed::Nanosecond3].
|
||||
Millis,
|
||||
|
||||
/// Use fixed 6 subsecond digits. This corresponds to [Fixed::Nanosecond6].
|
||||
Micros,
|
||||
|
||||
/// Use fixed 9 subsecond digits. This corresponds to [Fixed::Nanosecond9].
|
||||
Nanos,
|
||||
|
||||
/// Automatically select one of `Secs`, `Millis`, `Micros`, or `Nanos` to display all available
|
||||
/// non-zero sub-second digits. This corresponds to [Fixed::Nanosecond].
|
||||
AutoSi,
|
||||
|
||||
// Do not match against this.
|
||||
#[doc(hidden)]
|
||||
__NonExhaustive,
|
||||
}
|
||||
|
||||
/// Writes the date, time and offset to the string. same as `%Y-%m-%dT%H:%M:%S%.f%:z`
|
||||
#[inline]
|
||||
#[cfg(any(feature = "alloc", feature = "serde"))]
|
||||
pub(crate) fn write_rfc3339(
|
||||
w: &mut impl Write,
|
||||
dt: NaiveDateTime,
|
||||
off: FixedOffset,
|
||||
secform: SecondsFormat,
|
||||
use_z: bool,
|
||||
) -> fmt::Result {
|
||||
let year = dt.date().year();
|
||||
if (0..=9999).contains(&year) {
|
||||
write_hundreds(w, (year / 100) as u8)?;
|
||||
write_hundreds(w, (year % 100) as u8)?;
|
||||
} else {
|
||||
// ISO 8601 requires the explicit sign for out-of-range years
|
||||
write!(w, "{:+05}", year)?;
|
||||
}
|
||||
w.write_char('-')?;
|
||||
write_hundreds(w, dt.date().month() as u8)?;
|
||||
w.write_char('-')?;
|
||||
write_hundreds(w, dt.date().day() as u8)?;
|
||||
|
||||
w.write_char('T')?;
|
||||
|
||||
let (hour, min, mut sec) = dt.time().hms();
|
||||
let mut nano = dt.nanosecond();
|
||||
if nano >= 1_000_000_000 {
|
||||
sec += 1;
|
||||
nano -= 1_000_000_000;
|
||||
}
|
||||
write_hundreds(w, hour as u8)?;
|
||||
w.write_char(':')?;
|
||||
write_hundreds(w, min as u8)?;
|
||||
w.write_char(':')?;
|
||||
let sec = sec;
|
||||
write_hundreds(w, sec as u8)?;
|
||||
|
||||
match secform {
|
||||
SecondsFormat::Secs => {}
|
||||
SecondsFormat::Millis => write!(w, ".{:03}", nano / 1_000_000)?,
|
||||
SecondsFormat::Micros => write!(w, ".{:06}", nano / 1000)?,
|
||||
SecondsFormat::Nanos => write!(w, ".{:09}", nano)?,
|
||||
SecondsFormat::AutoSi => {
|
||||
if nano == 0 {
|
||||
} else if nano % 1_000_000 == 0 {
|
||||
write!(w, ".{:03}", nano / 1_000_000)?
|
||||
} else if nano % 1_000 == 0 {
|
||||
write!(w, ".{:06}", nano / 1_000)?
|
||||
} else {
|
||||
write!(w, ".{:09}", nano)?
|
||||
}
|
||||
}
|
||||
SecondsFormat::__NonExhaustive => unreachable!(),
|
||||
};
|
||||
|
||||
OffsetFormat {
|
||||
precision: OffsetPrecision::Minutes,
|
||||
colons: Colons::Colon,
|
||||
allow_zulu: use_z,
|
||||
padding: Pad::Zero,
|
||||
}
|
||||
.format(w, off)
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
/// write datetimes like `Tue, 1 Jul 2003 10:52:37 +0200`, same as `%a, %d %b %Y %H:%M:%S %z`
|
||||
pub(crate) fn write_rfc2822(
|
||||
w: &mut impl Write,
|
||||
dt: NaiveDateTime,
|
||||
off: FixedOffset,
|
||||
) -> fmt::Result {
|
||||
let year = dt.year();
|
||||
// RFC2822 is only defined on years 0 through 9999
|
||||
if !(0..=9999).contains(&year) {
|
||||
return Err(fmt::Error);
|
||||
}
|
||||
|
||||
let english = default_locale();
|
||||
|
||||
w.write_str(short_weekdays(english)[dt.weekday().num_days_from_sunday() as usize])?;
|
||||
w.write_str(", ")?;
|
||||
let day = dt.day();
|
||||
if day < 10 {
|
||||
w.write_char((b'0' + day as u8) as char)?;
|
||||
} else {
|
||||
write_hundreds(w, day as u8)?;
|
||||
}
|
||||
w.write_char(' ')?;
|
||||
w.write_str(short_months(english)[dt.month0() as usize])?;
|
||||
w.write_char(' ')?;
|
||||
write_hundreds(w, (year / 100) as u8)?;
|
||||
write_hundreds(w, (year % 100) as u8)?;
|
||||
w.write_char(' ')?;
|
||||
|
||||
let (hour, min, sec) = dt.time().hms();
|
||||
write_hundreds(w, hour as u8)?;
|
||||
w.write_char(':')?;
|
||||
write_hundreds(w, min as u8)?;
|
||||
w.write_char(':')?;
|
||||
let sec = sec + dt.nanosecond() / 1_000_000_000;
|
||||
write_hundreds(w, sec as u8)?;
|
||||
w.write_char(' ')?;
|
||||
OffsetFormat {
|
||||
precision: OffsetPrecision::Minutes,
|
||||
colons: Colons::None,
|
||||
allow_zulu: false,
|
||||
padding: Pad::Zero,
|
||||
}
|
||||
.format(w, off)
|
||||
}
|
||||
|
||||
/// Equivalent to `{:02}` formatting for n < 100.
|
||||
pub(crate) fn write_hundreds(w: &mut impl Write, n: u8) -> fmt::Result {
|
||||
if n >= 100 {
|
||||
return Err(fmt::Error);
|
||||
}
|
||||
|
||||
let tens = b'0' + n / 10;
|
||||
let ones = b'0' + n % 10;
|
||||
w.write_char(tens as char)?;
|
||||
w.write_char(ones as char)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "alloc")]
|
||||
mod tests {
|
||||
use super::{Colons, OffsetFormat, OffsetPrecision, Pad};
|
||||
use crate::FixedOffset;
|
||||
#[cfg(feature = "alloc")]
|
||||
use crate::{NaiveDate, NaiveTime, TimeZone, Timelike, Utc};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[test]
|
||||
fn test_delayed_write_to() {
|
||||
let dt = crate::DateTime::from_timestamp(1643723400, 123456789).unwrap();
|
||||
let df = dt.format("%Y-%m-%d %H:%M:%S%.9f");
|
||||
|
||||
let mut dt_str = String::new();
|
||||
|
||||
df.write_to(&mut dt_str).unwrap();
|
||||
assert_eq!(dt_str, "2022-02-01 13:50:00.123456789");
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", feature = "unstable-locales", feature = "alloc"))]
|
||||
#[test]
|
||||
fn test_with_locale_delayed_write_to() {
|
||||
use crate::DateTime;
|
||||
use crate::format::locales::Locale;
|
||||
|
||||
let dt = DateTime::from_timestamp(1643723400, 123456789).unwrap();
|
||||
let df = dt.format_localized("%A, %B %d, %Y", Locale::ja_JP);
|
||||
|
||||
let mut dt_str = String::new();
|
||||
|
||||
df.write_to(&mut dt_str).unwrap();
|
||||
|
||||
assert_eq!(dt_str, "火曜日, 2月 01, 2022");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "alloc")]
|
||||
fn test_date_format() {
|
||||
let d = NaiveDate::from_ymd_opt(2012, 3, 4).unwrap();
|
||||
assert_eq!(d.format("%Y,%C,%y,%G,%g").to_string(), "2012,20,12,2012,12");
|
||||
assert_eq!(d.format("%m,%b,%h,%B").to_string(), "03,Mar,Mar,March");
|
||||
assert_eq!(d.format("%q").to_string(), "1");
|
||||
assert_eq!(d.format("%d,%e").to_string(), "04, 4");
|
||||
assert_eq!(d.format("%U,%W,%V").to_string(), "10,09,09");
|
||||
assert_eq!(d.format("%a,%A,%w,%u").to_string(), "Sun,Sunday,0,7");
|
||||
assert_eq!(d.format("%j").to_string(), "064"); // since 2012 is a leap year
|
||||
assert_eq!(d.format("%D,%x").to_string(), "03/04/12,03/04/12");
|
||||
assert_eq!(d.format("%F").to_string(), "2012-03-04");
|
||||
assert_eq!(d.format("%v").to_string(), " 4-Mar-2012");
|
||||
assert_eq!(d.format("%t%n%%%n%t").to_string(), "\t\n%\n\t");
|
||||
|
||||
// non-four-digit years
|
||||
assert_eq!(
|
||||
NaiveDate::from_ymd_opt(12345, 1, 1).unwrap().format("%Y").to_string(),
|
||||
"+12345"
|
||||
);
|
||||
assert_eq!(NaiveDate::from_ymd_opt(1234, 1, 1).unwrap().format("%Y").to_string(), "1234");
|
||||
assert_eq!(NaiveDate::from_ymd_opt(123, 1, 1).unwrap().format("%Y").to_string(), "0123");
|
||||
assert_eq!(NaiveDate::from_ymd_opt(12, 1, 1).unwrap().format("%Y").to_string(), "0012");
|
||||
assert_eq!(NaiveDate::from_ymd_opt(1, 1, 1).unwrap().format("%Y").to_string(), "0001");
|
||||
assert_eq!(NaiveDate::from_ymd_opt(0, 1, 1).unwrap().format("%Y").to_string(), "0000");
|
||||
assert_eq!(NaiveDate::from_ymd_opt(-1, 1, 1).unwrap().format("%Y").to_string(), "-0001");
|
||||
assert_eq!(NaiveDate::from_ymd_opt(-12, 1, 1).unwrap().format("%Y").to_string(), "-0012");
|
||||
assert_eq!(NaiveDate::from_ymd_opt(-123, 1, 1).unwrap().format("%Y").to_string(), "-0123");
|
||||
assert_eq!(NaiveDate::from_ymd_opt(-1234, 1, 1).unwrap().format("%Y").to_string(), "-1234");
|
||||
assert_eq!(
|
||||
NaiveDate::from_ymd_opt(-12345, 1, 1).unwrap().format("%Y").to_string(),
|
||||
"-12345"
|
||||
);
|
||||
|
||||
// corner cases
|
||||
assert_eq!(
|
||||
NaiveDate::from_ymd_opt(2007, 12, 31).unwrap().format("%G,%g,%U,%W,%V").to_string(),
|
||||
"2008,08,52,53,01"
|
||||
);
|
||||
assert_eq!(
|
||||
NaiveDate::from_ymd_opt(2010, 1, 3).unwrap().format("%G,%g,%U,%W,%V").to_string(),
|
||||
"2009,09,01,00,53"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "alloc")]
|
||||
fn test_time_format() {
|
||||
let t = NaiveTime::from_hms_nano_opt(3, 5, 7, 98765432).unwrap();
|
||||
assert_eq!(t.format("%H,%k,%I,%l,%P,%p").to_string(), "03, 3,03, 3,am,AM");
|
||||
assert_eq!(t.format("%M").to_string(), "05");
|
||||
assert_eq!(t.format("%S,%f,%.f").to_string(), "07,098765432,.098765432");
|
||||
assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".098,.098765,.098765432");
|
||||
assert_eq!(t.format("%R").to_string(), "03:05");
|
||||
assert_eq!(t.format("%T,%X").to_string(), "03:05:07,03:05:07");
|
||||
assert_eq!(t.format("%r").to_string(), "03:05:07 AM");
|
||||
assert_eq!(t.format("%t%n%%%n%t").to_string(), "\t\n%\n\t");
|
||||
|
||||
let t = NaiveTime::from_hms_micro_opt(3, 5, 7, 432100).unwrap();
|
||||
assert_eq!(t.format("%S,%f,%.f").to_string(), "07,432100000,.432100");
|
||||
assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".432,.432100,.432100000");
|
||||
|
||||
let t = NaiveTime::from_hms_milli_opt(3, 5, 7, 210).unwrap();
|
||||
assert_eq!(t.format("%S,%f,%.f").to_string(), "07,210000000,.210");
|
||||
assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".210,.210000,.210000000");
|
||||
|
||||
let t = NaiveTime::from_hms_opt(3, 5, 7).unwrap();
|
||||
assert_eq!(t.format("%S,%f,%.f").to_string(), "07,000000000,");
|
||||
assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".000,.000000,.000000000");
|
||||
|
||||
// corner cases
|
||||
assert_eq!(
|
||||
NaiveTime::from_hms_opt(13, 57, 9).unwrap().format("%r").to_string(),
|
||||
"01:57:09 PM"
|
||||
);
|
||||
assert_eq!(
|
||||
NaiveTime::from_hms_milli_opt(23, 59, 59, 1_000).unwrap().format("%X").to_string(),
|
||||
"23:59:60"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "alloc")]
|
||||
fn test_datetime_format() {
|
||||
let dt =
|
||||
NaiveDate::from_ymd_opt(2010, 9, 8).unwrap().and_hms_milli_opt(7, 6, 54, 321).unwrap();
|
||||
assert_eq!(dt.format("%c").to_string(), "Wed Sep 8 07:06:54 2010");
|
||||
assert_eq!(dt.format("%s").to_string(), "1283929614");
|
||||
assert_eq!(dt.format("%t%n%%%n%t").to_string(), "\t\n%\n\t");
|
||||
|
||||
// a horror of leap second: coming near to you.
|
||||
let dt = NaiveDate::from_ymd_opt(2012, 6, 30)
|
||||
.unwrap()
|
||||
.and_hms_milli_opt(23, 59, 59, 1_000)
|
||||
.unwrap();
|
||||
assert_eq!(dt.format("%c").to_string(), "Sat Jun 30 23:59:60 2012");
|
||||
assert_eq!(dt.format("%s").to_string(), "1341100799"); // not 1341100800, it's intentional.
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "alloc")]
|
||||
fn test_datetime_format_alignment() {
|
||||
let datetime = Utc
|
||||
.with_ymd_and_hms(2007, 1, 2, 12, 34, 56)
|
||||
.unwrap()
|
||||
.with_nanosecond(123456789)
|
||||
.unwrap();
|
||||
|
||||
// Item::Literal, odd number of padding bytes.
|
||||
let percent = datetime.format("%%");
|
||||
assert_eq!(" %", format!("{:>4}", percent));
|
||||
assert_eq!("% ", format!("{:<4}", percent));
|
||||
assert_eq!(" % ", format!("{:^4}", percent));
|
||||
|
||||
// Item::Numeric, custom non-ASCII padding character
|
||||
let year = datetime.format("%Y");
|
||||
assert_eq!("——2007", format!("{:—>6}", year));
|
||||
assert_eq!("2007——", format!("{:—<6}", year));
|
||||
assert_eq!("—2007—", format!("{:—^6}", year));
|
||||
|
||||
// Item::Fixed
|
||||
let tz = datetime.format("%Z");
|
||||
assert_eq!(" UTC", format!("{:>5}", tz));
|
||||
assert_eq!("UTC ", format!("{:<5}", tz));
|
||||
assert_eq!(" UTC ", format!("{:^5}", tz));
|
||||
|
||||
// [Item::Numeric, Item::Space, Item::Literal, Item::Space, Item::Numeric]
|
||||
let ymd = datetime.format("%Y %B %d");
|
||||
assert_eq!(" 2007 January 02", format!("{:>17}", ymd));
|
||||
assert_eq!("2007 January 02 ", format!("{:<17}", ymd));
|
||||
assert_eq!(" 2007 January 02 ", format!("{:^17}", ymd));
|
||||
|
||||
// Truncated
|
||||
let time = datetime.format("%T%.6f");
|
||||
assert_eq!("12:34:56.1234", format!("{:.13}", time));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_offset_formatting() {
|
||||
fn check_all(precision: OffsetPrecision, expected: [[&str; 7]; 12]) {
|
||||
fn check(
|
||||
precision: OffsetPrecision,
|
||||
colons: Colons,
|
||||
padding: Pad,
|
||||
allow_zulu: bool,
|
||||
offsets: [FixedOffset; 7],
|
||||
expected: [&str; 7],
|
||||
) {
|
||||
let offset_format = OffsetFormat { precision, colons, allow_zulu, padding };
|
||||
for (offset, expected) in offsets.iter().zip(expected.iter()) {
|
||||
let mut output = String::new();
|
||||
offset_format.format(&mut output, *offset).unwrap();
|
||||
assert_eq!(&output, expected);
|
||||
}
|
||||
}
|
||||
// +03:45, -03:30, +11:00, -11:00:22, +02:34:26, -12:34:30, +00:00
|
||||
let offsets = [
|
||||
FixedOffset::east_opt(13_500).unwrap(),
|
||||
FixedOffset::east_opt(-12_600).unwrap(),
|
||||
FixedOffset::east_opt(39_600).unwrap(),
|
||||
FixedOffset::east_opt(-39_622).unwrap(),
|
||||
FixedOffset::east_opt(9266).unwrap(),
|
||||
FixedOffset::east_opt(-45270).unwrap(),
|
||||
FixedOffset::east_opt(0).unwrap(),
|
||||
];
|
||||
check(precision, Colons::Colon, Pad::Zero, false, offsets, expected[0]);
|
||||
check(precision, Colons::Colon, Pad::Zero, true, offsets, expected[1]);
|
||||
check(precision, Colons::Colon, Pad::Space, false, offsets, expected[2]);
|
||||
check(precision, Colons::Colon, Pad::Space, true, offsets, expected[3]);
|
||||
check(precision, Colons::Colon, Pad::None, false, offsets, expected[4]);
|
||||
check(precision, Colons::Colon, Pad::None, true, offsets, expected[5]);
|
||||
check(precision, Colons::None, Pad::Zero, false, offsets, expected[6]);
|
||||
check(precision, Colons::None, Pad::Zero, true, offsets, expected[7]);
|
||||
check(precision, Colons::None, Pad::Space, false, offsets, expected[8]);
|
||||
check(precision, Colons::None, Pad::Space, true, offsets, expected[9]);
|
||||
check(precision, Colons::None, Pad::None, false, offsets, expected[10]);
|
||||
check(precision, Colons::None, Pad::None, true, offsets, expected[11]);
|
||||
// `Colons::Maybe` should format the same as `Colons::None`
|
||||
check(precision, Colons::Maybe, Pad::Zero, false, offsets, expected[6]);
|
||||
check(precision, Colons::Maybe, Pad::Zero, true, offsets, expected[7]);
|
||||
check(precision, Colons::Maybe, Pad::Space, false, offsets, expected[8]);
|
||||
check(precision, Colons::Maybe, Pad::Space, true, offsets, expected[9]);
|
||||
check(precision, Colons::Maybe, Pad::None, false, offsets, expected[10]);
|
||||
check(precision, Colons::Maybe, Pad::None, true, offsets, expected[11]);
|
||||
}
|
||||
check_all(
|
||||
OffsetPrecision::Hours,
|
||||
[
|
||||
["+03", "-03", "+11", "-11", "+02", "-12", "+00"],
|
||||
["+03", "-03", "+11", "-11", "+02", "-12", "Z"],
|
||||
[" +3", " -3", "+11", "-11", " +2", "-12", " +0"],
|
||||
[" +3", " -3", "+11", "-11", " +2", "-12", "Z"],
|
||||
["+3", "-3", "+11", "-11", "+2", "-12", "+0"],
|
||||
["+3", "-3", "+11", "-11", "+2", "-12", "Z"],
|
||||
["+03", "-03", "+11", "-11", "+02", "-12", "+00"],
|
||||
["+03", "-03", "+11", "-11", "+02", "-12", "Z"],
|
||||
[" +3", " -3", "+11", "-11", " +2", "-12", " +0"],
|
||||
[" +3", " -3", "+11", "-11", " +2", "-12", "Z"],
|
||||
["+3", "-3", "+11", "-11", "+2", "-12", "+0"],
|
||||
["+3", "-3", "+11", "-11", "+2", "-12", "Z"],
|
||||
],
|
||||
);
|
||||
check_all(
|
||||
OffsetPrecision::Minutes,
|
||||
[
|
||||
["+03:45", "-03:30", "+11:00", "-11:00", "+02:34", "-12:35", "+00:00"],
|
||||
["+03:45", "-03:30", "+11:00", "-11:00", "+02:34", "-12:35", "Z"],
|
||||
[" +3:45", " -3:30", "+11:00", "-11:00", " +2:34", "-12:35", " +0:00"],
|
||||
[" +3:45", " -3:30", "+11:00", "-11:00", " +2:34", "-12:35", "Z"],
|
||||
["+3:45", "-3:30", "+11:00", "-11:00", "+2:34", "-12:35", "+0:00"],
|
||||
["+3:45", "-3:30", "+11:00", "-11:00", "+2:34", "-12:35", "Z"],
|
||||
["+0345", "-0330", "+1100", "-1100", "+0234", "-1235", "+0000"],
|
||||
["+0345", "-0330", "+1100", "-1100", "+0234", "-1235", "Z"],
|
||||
[" +345", " -330", "+1100", "-1100", " +234", "-1235", " +000"],
|
||||
[" +345", " -330", "+1100", "-1100", " +234", "-1235", "Z"],
|
||||
["+345", "-330", "+1100", "-1100", "+234", "-1235", "+000"],
|
||||
["+345", "-330", "+1100", "-1100", "+234", "-1235", "Z"],
|
||||
],
|
||||
);
|
||||
#[rustfmt::skip]
|
||||
check_all(
|
||||
OffsetPrecision::Seconds,
|
||||
[
|
||||
["+03:45:00", "-03:30:00", "+11:00:00", "-11:00:22", "+02:34:26", "-12:34:30", "+00:00:00"],
|
||||
["+03:45:00", "-03:30:00", "+11:00:00", "-11:00:22", "+02:34:26", "-12:34:30", "Z"],
|
||||
[" +3:45:00", " -3:30:00", "+11:00:00", "-11:00:22", " +2:34:26", "-12:34:30", " +0:00:00"],
|
||||
[" +3:45:00", " -3:30:00", "+11:00:00", "-11:00:22", " +2:34:26", "-12:34:30", "Z"],
|
||||
["+3:45:00", "-3:30:00", "+11:00:00", "-11:00:22", "+2:34:26", "-12:34:30", "+0:00:00"],
|
||||
["+3:45:00", "-3:30:00", "+11:00:00", "-11:00:22", "+2:34:26", "-12:34:30", "Z"],
|
||||
["+034500", "-033000", "+110000", "-110022", "+023426", "-123430", "+000000"],
|
||||
["+034500", "-033000", "+110000", "-110022", "+023426", "-123430", "Z"],
|
||||
[" +34500", " -33000", "+110000", "-110022", " +23426", "-123430", " +00000"],
|
||||
[" +34500", " -33000", "+110000", "-110022", " +23426", "-123430", "Z"],
|
||||
["+34500", "-33000", "+110000", "-110022", "+23426", "-123430", "+00000"],
|
||||
["+34500", "-33000", "+110000", "-110022", "+23426", "-123430", "Z"],
|
||||
],
|
||||
);
|
||||
check_all(
|
||||
OffsetPrecision::OptionalMinutes,
|
||||
[
|
||||
["+03:45", "-03:30", "+11", "-11", "+02:34", "-12:35", "+00"],
|
||||
["+03:45", "-03:30", "+11", "-11", "+02:34", "-12:35", "Z"],
|
||||
[" +3:45", " -3:30", "+11", "-11", " +2:34", "-12:35", " +0"],
|
||||
[" +3:45", " -3:30", "+11", "-11", " +2:34", "-12:35", "Z"],
|
||||
["+3:45", "-3:30", "+11", "-11", "+2:34", "-12:35", "+0"],
|
||||
["+3:45", "-3:30", "+11", "-11", "+2:34", "-12:35", "Z"],
|
||||
["+0345", "-0330", "+11", "-11", "+0234", "-1235", "+00"],
|
||||
["+0345", "-0330", "+11", "-11", "+0234", "-1235", "Z"],
|
||||
[" +345", " -330", "+11", "-11", " +234", "-1235", " +0"],
|
||||
[" +345", " -330", "+11", "-11", " +234", "-1235", "Z"],
|
||||
["+345", "-330", "+11", "-11", "+234", "-1235", "+0"],
|
||||
["+345", "-330", "+11", "-11", "+234", "-1235", "Z"],
|
||||
],
|
||||
);
|
||||
check_all(
|
||||
OffsetPrecision::OptionalSeconds,
|
||||
[
|
||||
["+03:45", "-03:30", "+11:00", "-11:00:22", "+02:34:26", "-12:34:30", "+00:00"],
|
||||
["+03:45", "-03:30", "+11:00", "-11:00:22", "+02:34:26", "-12:34:30", "Z"],
|
||||
[" +3:45", " -3:30", "+11:00", "-11:00:22", " +2:34:26", "-12:34:30", " +0:00"],
|
||||
[" +3:45", " -3:30", "+11:00", "-11:00:22", " +2:34:26", "-12:34:30", "Z"],
|
||||
["+3:45", "-3:30", "+11:00", "-11:00:22", "+2:34:26", "-12:34:30", "+0:00"],
|
||||
["+3:45", "-3:30", "+11:00", "-11:00:22", "+2:34:26", "-12:34:30", "Z"],
|
||||
["+0345", "-0330", "+1100", "-110022", "+023426", "-123430", "+0000"],
|
||||
["+0345", "-0330", "+1100", "-110022", "+023426", "-123430", "Z"],
|
||||
[" +345", " -330", "+1100", "-110022", " +23426", "-123430", " +000"],
|
||||
[" +345", " -330", "+1100", "-110022", " +23426", "-123430", "Z"],
|
||||
["+345", "-330", "+1100", "-110022", "+23426", "-123430", "+000"],
|
||||
["+345", "-330", "+1100", "-110022", "+23426", "-123430", "Z"],
|
||||
],
|
||||
);
|
||||
check_all(
|
||||
OffsetPrecision::OptionalMinutesAndSeconds,
|
||||
[
|
||||
["+03:45", "-03:30", "+11", "-11:00:22", "+02:34:26", "-12:34:30", "+00"],
|
||||
["+03:45", "-03:30", "+11", "-11:00:22", "+02:34:26", "-12:34:30", "Z"],
|
||||
[" +3:45", " -3:30", "+11", "-11:00:22", " +2:34:26", "-12:34:30", " +0"],
|
||||
[" +3:45", " -3:30", "+11", "-11:00:22", " +2:34:26", "-12:34:30", "Z"],
|
||||
["+3:45", "-3:30", "+11", "-11:00:22", "+2:34:26", "-12:34:30", "+0"],
|
||||
["+3:45", "-3:30", "+11", "-11:00:22", "+2:34:26", "-12:34:30", "Z"],
|
||||
["+0345", "-0330", "+11", "-110022", "+023426", "-123430", "+00"],
|
||||
["+0345", "-0330", "+11", "-110022", "+023426", "-123430", "Z"],
|
||||
[" +345", " -330", "+11", "-110022", " +23426", "-123430", " +0"],
|
||||
[" +345", " -330", "+11", "-110022", " +23426", "-123430", "Z"],
|
||||
["+345", "-330", "+11", "-110022", "+23426", "-123430", "+0"],
|
||||
["+345", "-330", "+11", "-110022", "+23426", "-123430", "Z"],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
126
third_party/rust/chrono/src/format/locales.rs
vendored
126
third_party/rust/chrono/src/format/locales.rs
vendored
@@ -1,33 +1,103 @@
|
||||
use pure_rust_locales::{locale_match, Locale};
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
mod localized {
|
||||
use pure_rust_locales::{Locale, locale_match};
|
||||
|
||||
pub(crate) fn short_months(locale: Locale) -> &'static [&'static str] {
|
||||
locale_match!(locale => LC_TIME::ABMON)
|
||||
pub(crate) const fn default_locale() -> Locale {
|
||||
Locale::POSIX
|
||||
}
|
||||
|
||||
pub(crate) const fn short_months(locale: Locale) -> &'static [&'static str] {
|
||||
locale_match!(locale => LC_TIME::ABMON)
|
||||
}
|
||||
|
||||
pub(crate) const fn long_months(locale: Locale) -> &'static [&'static str] {
|
||||
locale_match!(locale => LC_TIME::MON)
|
||||
}
|
||||
|
||||
pub(crate) const fn short_weekdays(locale: Locale) -> &'static [&'static str] {
|
||||
locale_match!(locale => LC_TIME::ABDAY)
|
||||
}
|
||||
|
||||
pub(crate) const fn long_weekdays(locale: Locale) -> &'static [&'static str] {
|
||||
locale_match!(locale => LC_TIME::DAY)
|
||||
}
|
||||
|
||||
pub(crate) const fn am_pm(locale: Locale) -> &'static [&'static str] {
|
||||
locale_match!(locale => LC_TIME::AM_PM)
|
||||
}
|
||||
|
||||
pub(crate) const fn decimal_point(locale: Locale) -> &'static str {
|
||||
locale_match!(locale => LC_NUMERIC::DECIMAL_POINT)
|
||||
}
|
||||
|
||||
pub(crate) const fn d_fmt(locale: Locale) -> &'static str {
|
||||
locale_match!(locale => LC_TIME::D_FMT)
|
||||
}
|
||||
|
||||
pub(crate) const fn d_t_fmt(locale: Locale) -> &'static str {
|
||||
locale_match!(locale => LC_TIME::D_T_FMT)
|
||||
}
|
||||
|
||||
pub(crate) const fn t_fmt(locale: Locale) -> &'static str {
|
||||
locale_match!(locale => LC_TIME::T_FMT)
|
||||
}
|
||||
|
||||
pub(crate) const fn t_fmt_ampm(locale: Locale) -> &'static str {
|
||||
locale_match!(locale => LC_TIME::T_FMT_AMPM)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn long_months(locale: Locale) -> &'static [&'static str] {
|
||||
locale_match!(locale => LC_TIME::MON)
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
pub(crate) use localized::*;
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
pub use pure_rust_locales::Locale;
|
||||
|
||||
#[cfg(not(feature = "unstable-locales"))]
|
||||
mod unlocalized {
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(crate) struct Locale;
|
||||
|
||||
pub(crate) const fn default_locale() -> Locale {
|
||||
Locale
|
||||
}
|
||||
|
||||
pub(crate) const fn short_months(_locale: Locale) -> &'static [&'static str] {
|
||||
&["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
|
||||
}
|
||||
|
||||
pub(crate) const fn long_months(_locale: Locale) -> &'static [&'static str] {
|
||||
&[
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
]
|
||||
}
|
||||
|
||||
pub(crate) const fn short_weekdays(_locale: Locale) -> &'static [&'static str] {
|
||||
&["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
|
||||
}
|
||||
|
||||
pub(crate) const fn long_weekdays(_locale: Locale) -> &'static [&'static str] {
|
||||
&["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
|
||||
}
|
||||
|
||||
pub(crate) const fn am_pm(_locale: Locale) -> &'static [&'static str] {
|
||||
&["AM", "PM"]
|
||||
}
|
||||
|
||||
pub(crate) const fn decimal_point(_locale: Locale) -> &'static str {
|
||||
"."
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn short_weekdays(locale: Locale) -> &'static [&'static str] {
|
||||
locale_match!(locale => LC_TIME::ABDAY)
|
||||
}
|
||||
|
||||
pub(crate) fn long_weekdays(locale: Locale) -> &'static [&'static str] {
|
||||
locale_match!(locale => LC_TIME::DAY)
|
||||
}
|
||||
|
||||
pub(crate) fn am_pm(locale: Locale) -> &'static [&'static str] {
|
||||
locale_match!(locale => LC_TIME::AM_PM)
|
||||
}
|
||||
|
||||
pub(crate) fn d_fmt(locale: Locale) -> &'static str {
|
||||
locale_match!(locale => LC_TIME::D_FMT)
|
||||
}
|
||||
|
||||
pub(crate) fn d_t_fmt(locale: Locale) -> &'static str {
|
||||
locale_match!(locale => LC_TIME::D_T_FMT)
|
||||
}
|
||||
|
||||
pub(crate) fn t_fmt(locale: Locale) -> &'static str {
|
||||
locale_match!(locale => LC_TIME::T_FMT)
|
||||
}
|
||||
#[cfg(not(feature = "unstable-locales"))]
|
||||
pub(crate) use unlocalized::*;
|
||||
|
||||
745
third_party/rust/chrono/src/format/mod.rs
vendored
745
third_party/rust/chrono/src/format/mod.rs
vendored
@@ -12,50 +12,70 @@
|
||||
//! which are just an [`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) of
|
||||
//! the [`Item`](./enum.Item.html) type.
|
||||
//! They are generated from more readable **format strings**;
|
||||
//! currently Chrono supports [one built-in syntax closely resembling
|
||||
//! C's `strftime` format](./strftime/index.html).
|
||||
//! currently Chrono supports a built-in syntax closely resembling
|
||||
//! C's `strftime` format. The available options can be found [here](./strftime/index.html).
|
||||
//!
|
||||
//! # Example
|
||||
//! ```
|
||||
//! # #[cfg(feature = "alloc")] {
|
||||
//! use chrono::{NaiveDateTime, TimeZone, Utc};
|
||||
//!
|
||||
//! let date_time = Utc.with_ymd_and_hms(2020, 11, 10, 0, 1, 32).unwrap();
|
||||
//!
|
||||
//! let formatted = format!("{}", date_time.format("%Y-%m-%d %H:%M:%S"));
|
||||
//! assert_eq!(formatted, "2020-11-10 00:01:32");
|
||||
//!
|
||||
//! let parsed = NaiveDateTime::parse_from_str(&formatted, "%Y-%m-%d %H:%M:%S")?.and_utc();
|
||||
//! assert_eq!(parsed, date_time);
|
||||
//! # }
|
||||
//! # Ok::<(), chrono::ParseError>(())
|
||||
//! ```
|
||||
|
||||
#![allow(ellipsis_inclusive_range_patterns)]
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg(all(feature = "alloc", not(feature = "std"), not(test)))]
|
||||
use alloc::boxed::Box;
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::string::{String, ToString};
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use core::borrow::Borrow;
|
||||
use core::fmt;
|
||||
use core::str::FromStr;
|
||||
#[cfg(any(feature = "std", test))]
|
||||
#[cfg(feature = "std")]
|
||||
use std::error::Error;
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use naive::{NaiveDate, NaiveTime};
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use offset::{FixedOffset, Offset};
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use {Datelike, Timelike};
|
||||
use {Month, ParseMonthError, ParseWeekdayError, Weekday};
|
||||
use crate::{Month, ParseMonthError, ParseWeekdayError, Weekday};
|
||||
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
mod formatting;
|
||||
mod parsed;
|
||||
|
||||
// due to the size of parsing routines, they are in separate modules.
|
||||
mod parse;
|
||||
pub(crate) mod scan;
|
||||
|
||||
pub mod strftime;
|
||||
|
||||
#[allow(unused)]
|
||||
// TODO: remove '#[allow(unused)]' once we use this module for parsing or something else that does
|
||||
// not require `alloc`.
|
||||
pub(crate) mod locales;
|
||||
|
||||
pub use self::parse::parse;
|
||||
pub use self::parsed::Parsed;
|
||||
pub use self::strftime::StrftimeItems;
|
||||
/// L10n locales.
|
||||
pub use formatting::SecondsFormat;
|
||||
pub(crate) use formatting::write_hundreds;
|
||||
#[cfg(feature = "alloc")]
|
||||
pub(crate) use formatting::write_rfc2822;
|
||||
#[cfg(any(feature = "alloc", feature = "serde"))]
|
||||
pub(crate) use formatting::write_rfc3339;
|
||||
#[cfg(feature = "alloc")]
|
||||
#[allow(deprecated)]
|
||||
pub use formatting::{DelayedFormat, format, format_item};
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
pub use pure_rust_locales::Locale;
|
||||
|
||||
#[cfg(not(feature = "unstable-locales"))]
|
||||
#[derive(Debug)]
|
||||
struct Locale;
|
||||
pub use locales::Locale;
|
||||
pub(crate) use parse::parse_rfc3339;
|
||||
pub use parse::{parse, parse_and_remainder};
|
||||
pub use parsed::Parsed;
|
||||
pub use strftime::StrftimeItems;
|
||||
|
||||
/// An uninhabited type used for `InternalNumeric` and `InternalFixed` below.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
enum Void {}
|
||||
|
||||
/// Padding characters for numeric items.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum Pad {
|
||||
/// No padding.
|
||||
None,
|
||||
@@ -78,10 +98,11 @@ pub enum Pad {
|
||||
/// It also trims the preceding whitespace if any.
|
||||
/// It cannot parse the negative number, so some date and time cannot be formatted then
|
||||
/// parsed with the same formatting items.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum Numeric {
|
||||
/// Full Gregorian year (FW=4, PW=∞).
|
||||
/// May accept years before 1 BCE or after 9999 CE, given an initial sign.
|
||||
/// May accept years before 1 BCE or after 9999 CE, given an initial sign (+/-).
|
||||
Year,
|
||||
/// Gregorian year divided by 100 (century number; FW=PW=2). Implies the non-negative year.
|
||||
YearDiv100,
|
||||
@@ -94,6 +115,8 @@ pub enum Numeric {
|
||||
IsoYearDiv100,
|
||||
/// Year in the ISO week date, modulo 100 (FW=PW=2). Cannot be negative.
|
||||
IsoYearMod100,
|
||||
/// Quarter (FW=PW=1).
|
||||
Quarter,
|
||||
/// Month (FW=PW=2).
|
||||
Month,
|
||||
/// Day of the month (FW=PW=2).
|
||||
@@ -134,24 +157,11 @@ pub enum Numeric {
|
||||
}
|
||||
|
||||
/// An opaque type representing numeric item types for internal uses only.
|
||||
#[derive(Clone, Eq, Hash, PartialEq)]
|
||||
pub struct InternalNumeric {
|
||||
_dummy: Void,
|
||||
}
|
||||
|
||||
impl Clone for InternalNumeric {
|
||||
fn clone(&self) -> Self {
|
||||
match self._dummy {}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for InternalNumeric {
|
||||
fn eq(&self, _other: &InternalNumeric) -> bool {
|
||||
match self._dummy {}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for InternalNumeric {}
|
||||
|
||||
impl fmt::Debug for InternalNumeric {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "<InternalNumeric>")
|
||||
@@ -162,7 +172,8 @@ impl fmt::Debug for InternalNumeric {
|
||||
///
|
||||
/// They have their own rules of formatting and parsing.
|
||||
/// Otherwise noted, they print in the specified cases but parse case-insensitively.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum Fixed {
|
||||
/// Abbreviated month names.
|
||||
///
|
||||
@@ -208,6 +219,18 @@ pub enum Fixed {
|
||||
/// The offset is limited from `-24:00` to `+24:00`,
|
||||
/// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
|
||||
TimezoneOffsetColon,
|
||||
/// Offset from the local time to UTC with seconds (`+09:00:00` or `-04:00:00` or `+00:00:00`).
|
||||
///
|
||||
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace.
|
||||
/// The offset is limited from `-24:00:00` to `+24:00:00`,
|
||||
/// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
|
||||
TimezoneOffsetDoubleColon,
|
||||
/// Offset from the local time to UTC without minutes (`+09` or `-04` or `+00`).
|
||||
///
|
||||
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace.
|
||||
/// The offset is limited from `-24` to `+24`,
|
||||
/// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
|
||||
TimezoneOffsetTripleColon,
|
||||
/// Offset from the local time to UTC (`+09:00` or `-04:00` or `Z`).
|
||||
///
|
||||
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace,
|
||||
@@ -234,12 +257,12 @@ pub enum Fixed {
|
||||
}
|
||||
|
||||
/// An opaque type representing fixed-format item types for internal uses only.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct InternalFixed {
|
||||
val: InternalInternal,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
enum InternalInternal {
|
||||
/// Same as [`TimezoneOffsetColonZ`](#variant.TimezoneOffsetColonZ), but
|
||||
/// allows missing minutes (per [ISO 8601][iso8601]).
|
||||
@@ -258,18 +281,63 @@ enum InternalInternal {
|
||||
Nanosecond9NoDot,
|
||||
}
|
||||
|
||||
/// Type for specifying the format of UTC offsets.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct OffsetFormat {
|
||||
/// See `OffsetPrecision`.
|
||||
pub precision: OffsetPrecision,
|
||||
/// Separator between hours, minutes and seconds.
|
||||
pub colons: Colons,
|
||||
/// Represent `+00:00` as `Z`.
|
||||
pub allow_zulu: bool,
|
||||
/// Pad the hour value to two digits.
|
||||
pub padding: Pad,
|
||||
}
|
||||
|
||||
/// The precision of an offset from UTC formatting item.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum OffsetPrecision {
|
||||
/// Format offset from UTC as only hours. Not recommended, it is not uncommon for timezones to
|
||||
/// have an offset of 30 minutes, 15 minutes, etc.
|
||||
/// Any minutes and seconds get truncated.
|
||||
Hours,
|
||||
/// Format offset from UTC as hours and minutes.
|
||||
/// Any seconds will be rounded to the nearest minute.
|
||||
Minutes,
|
||||
/// Format offset from UTC as hours, minutes and seconds.
|
||||
Seconds,
|
||||
/// Format offset from UTC as hours, and optionally with minutes.
|
||||
/// Any seconds will be rounded to the nearest minute.
|
||||
OptionalMinutes,
|
||||
/// Format offset from UTC as hours and minutes, and optionally seconds.
|
||||
OptionalSeconds,
|
||||
/// Format offset from UTC as hours and optionally minutes and seconds.
|
||||
OptionalMinutesAndSeconds,
|
||||
}
|
||||
|
||||
/// The separator between hours and minutes in an offset.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Colons {
|
||||
/// No separator
|
||||
None,
|
||||
/// Colon (`:`) as separator
|
||||
Colon,
|
||||
/// No separator when formatting, colon allowed when parsing.
|
||||
Maybe,
|
||||
}
|
||||
|
||||
/// A single formatting item. This is used for both formatting and parsing.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum Item<'a> {
|
||||
/// A literally printed and parsed text.
|
||||
Literal(&'a str),
|
||||
/// Same as `Literal` but with the string owned by the item.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[cfg(feature = "alloc")]
|
||||
OwnedLiteral(Box<str>),
|
||||
/// Whitespace. Prints literally but reads zero or more whitespace.
|
||||
Space(&'a str),
|
||||
/// Same as `Space` but with the string owned by the item.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[cfg(feature = "alloc")]
|
||||
OwnedSpace(Box<str>),
|
||||
/// Numeric item. Can be optionally padded to the maximal length (if any) when formatting;
|
||||
/// the parser simply ignores any padded whitespace and zeroes.
|
||||
@@ -280,49 +348,57 @@ pub enum Item<'a> {
|
||||
Error,
|
||||
}
|
||||
|
||||
macro_rules! lit {
|
||||
($x:expr) => {
|
||||
Item::Literal($x)
|
||||
};
|
||||
const fn num(numeric: Numeric) -> Item<'static> {
|
||||
Item::Numeric(numeric, Pad::None)
|
||||
}
|
||||
macro_rules! sp {
|
||||
($x:expr) => {
|
||||
Item::Space($x)
|
||||
};
|
||||
|
||||
const fn num0(numeric: Numeric) -> Item<'static> {
|
||||
Item::Numeric(numeric, Pad::Zero)
|
||||
}
|
||||
macro_rules! num {
|
||||
($x:ident) => {
|
||||
Item::Numeric(Numeric::$x, Pad::None)
|
||||
};
|
||||
|
||||
const fn nums(numeric: Numeric) -> Item<'static> {
|
||||
Item::Numeric(numeric, Pad::Space)
|
||||
}
|
||||
macro_rules! num0 {
|
||||
($x:ident) => {
|
||||
Item::Numeric(Numeric::$x, Pad::Zero)
|
||||
};
|
||||
|
||||
const fn fixed(fixed: Fixed) -> Item<'static> {
|
||||
Item::Fixed(fixed)
|
||||
}
|
||||
macro_rules! nums {
|
||||
($x:ident) => {
|
||||
Item::Numeric(Numeric::$x, Pad::Space)
|
||||
};
|
||||
|
||||
const fn internal_fixed(val: InternalInternal) -> Item<'static> {
|
||||
Item::Fixed(Fixed::Internal(InternalFixed { val }))
|
||||
}
|
||||
macro_rules! fix {
|
||||
($x:ident) => {
|
||||
Item::Fixed(Fixed::$x)
|
||||
};
|
||||
}
|
||||
macro_rules! internal_fix {
|
||||
($x:ident) => {
|
||||
Item::Fixed(Fixed::Internal(InternalFixed { val: InternalInternal::$x }))
|
||||
};
|
||||
|
||||
impl Item<'_> {
|
||||
/// Convert items that contain a reference to the format string into an owned variant.
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
pub fn to_owned(self) -> Item<'static> {
|
||||
match self {
|
||||
Item::Literal(s) => Item::OwnedLiteral(Box::from(s)),
|
||||
Item::Space(s) => Item::OwnedSpace(Box::from(s)),
|
||||
Item::Numeric(n, p) => Item::Numeric(n, p),
|
||||
Item::Fixed(f) => Item::Fixed(f),
|
||||
Item::OwnedLiteral(l) => Item::OwnedLiteral(l),
|
||||
Item::OwnedSpace(s) => Item::OwnedSpace(s),
|
||||
Item::Error => Item::Error,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An error from the `parse` function.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)]
|
||||
pub struct ParseError(ParseErrorKind);
|
||||
|
||||
impl ParseError {
|
||||
/// The category of parse error
|
||||
pub const fn kind(&self) -> ParseErrorKind {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// The category of parse error
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
enum ParseErrorKind {
|
||||
#[allow(clippy::manual_non_exhaustive)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)]
|
||||
pub enum ParseErrorKind {
|
||||
/// Given field is out of permitted range.
|
||||
OutOfRange,
|
||||
|
||||
@@ -348,8 +424,12 @@ enum ParseErrorKind {
|
||||
/// All formatting items have been read but there is a remaining input.
|
||||
TooLong,
|
||||
|
||||
/// There was an error on the formatting string, or there were non-supported formating items.
|
||||
/// There was an error on the formatting string, or there were non-supported formatting items.
|
||||
BadFormat,
|
||||
|
||||
// TODO: Change this to `#[non_exhaustive]` (on the enum) with the next breaking release.
|
||||
#[doc(hidden)]
|
||||
__Nonexhaustive,
|
||||
}
|
||||
|
||||
/// Same as `Result<T, ParseError>`.
|
||||
@@ -365,11 +445,12 @@ impl fmt::Display for ParseError {
|
||||
ParseErrorKind::TooShort => write!(f, "premature end of input"),
|
||||
ParseErrorKind::TooLong => write!(f, "trailing input"),
|
||||
ParseErrorKind::BadFormat => write!(f, "bad or unsupported format string"),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
#[cfg(feature = "std")]
|
||||
impl Error for ParseError {
|
||||
#[allow(deprecated)]
|
||||
fn description(&self) -> &str {
|
||||
@@ -378,465 +459,40 @@ impl Error for ParseError {
|
||||
}
|
||||
|
||||
// to be used in this module and submodules
|
||||
const OUT_OF_RANGE: ParseError = ParseError(ParseErrorKind::OutOfRange);
|
||||
pub(crate) const OUT_OF_RANGE: ParseError = ParseError(ParseErrorKind::OutOfRange);
|
||||
const IMPOSSIBLE: ParseError = ParseError(ParseErrorKind::Impossible);
|
||||
const NOT_ENOUGH: ParseError = ParseError(ParseErrorKind::NotEnough);
|
||||
const INVALID: ParseError = ParseError(ParseErrorKind::Invalid);
|
||||
const TOO_SHORT: ParseError = ParseError(ParseErrorKind::TooShort);
|
||||
const TOO_LONG: ParseError = ParseError(ParseErrorKind::TooLong);
|
||||
pub(crate) const TOO_LONG: ParseError = ParseError(ParseErrorKind::TooLong);
|
||||
const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat);
|
||||
|
||||
/// Formats single formatting item
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
pub fn format_item<'a>(
|
||||
w: &mut fmt::Formatter,
|
||||
date: Option<&NaiveDate>,
|
||||
time: Option<&NaiveTime>,
|
||||
off: Option<&(String, FixedOffset)>,
|
||||
item: &Item<'a>,
|
||||
) -> fmt::Result {
|
||||
let mut result = String::new();
|
||||
format_inner(&mut result, date, time, off, item, None)?;
|
||||
w.pad(&result)
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
fn format_inner<'a>(
|
||||
result: &mut String,
|
||||
date: Option<&NaiveDate>,
|
||||
time: Option<&NaiveTime>,
|
||||
off: Option<&(String, FixedOffset)>,
|
||||
item: &Item<'a>,
|
||||
_locale: Option<Locale>,
|
||||
) -> fmt::Result {
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
let (short_months, long_months, short_weekdays, long_weekdays, am_pm, am_pm_lowercase) = {
|
||||
let locale = _locale.unwrap_or(Locale::POSIX);
|
||||
let am_pm = locales::am_pm(locale);
|
||||
(
|
||||
locales::short_months(locale),
|
||||
locales::long_months(locale),
|
||||
locales::short_weekdays(locale),
|
||||
locales::long_weekdays(locale),
|
||||
am_pm,
|
||||
&[am_pm[0].to_lowercase(), am_pm[1].to_lowercase()],
|
||||
)
|
||||
};
|
||||
#[cfg(not(feature = "unstable-locales"))]
|
||||
let (short_months, long_months, short_weekdays, long_weekdays, am_pm, am_pm_lowercase) = {
|
||||
(
|
||||
&["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
|
||||
&[
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
],
|
||||
&["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
||||
&["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
|
||||
&["AM", "PM"],
|
||||
&["am", "pm"],
|
||||
)
|
||||
};
|
||||
|
||||
use core::fmt::Write;
|
||||
use div::{div_floor, mod_floor};
|
||||
|
||||
match *item {
|
||||
Item::Literal(s) | Item::Space(s) => result.push_str(s),
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
Item::OwnedLiteral(ref s) | Item::OwnedSpace(ref s) => result.push_str(s),
|
||||
|
||||
Item::Numeric(ref spec, ref pad) => {
|
||||
use self::Numeric::*;
|
||||
|
||||
let week_from_sun = |d: &NaiveDate| {
|
||||
(d.ordinal() as i32 - d.weekday().num_days_from_sunday() as i32 + 7) / 7
|
||||
};
|
||||
let week_from_mon = |d: &NaiveDate| {
|
||||
(d.ordinal() as i32 - d.weekday().num_days_from_monday() as i32 + 7) / 7
|
||||
};
|
||||
|
||||
let (width, v) = match *spec {
|
||||
Year => (4, date.map(|d| i64::from(d.year()))),
|
||||
YearDiv100 => (2, date.map(|d| div_floor(i64::from(d.year()), 100))),
|
||||
YearMod100 => (2, date.map(|d| mod_floor(i64::from(d.year()), 100))),
|
||||
IsoYear => (4, date.map(|d| i64::from(d.iso_week().year()))),
|
||||
IsoYearDiv100 => (2, date.map(|d| div_floor(i64::from(d.iso_week().year()), 100))),
|
||||
IsoYearMod100 => (2, date.map(|d| mod_floor(i64::from(d.iso_week().year()), 100))),
|
||||
Month => (2, date.map(|d| i64::from(d.month()))),
|
||||
Day => (2, date.map(|d| i64::from(d.day()))),
|
||||
WeekFromSun => (2, date.map(|d| i64::from(week_from_sun(d)))),
|
||||
WeekFromMon => (2, date.map(|d| i64::from(week_from_mon(d)))),
|
||||
IsoWeek => (2, date.map(|d| i64::from(d.iso_week().week()))),
|
||||
NumDaysFromSun => (1, date.map(|d| i64::from(d.weekday().num_days_from_sunday()))),
|
||||
WeekdayFromMon => (1, date.map(|d| i64::from(d.weekday().number_from_monday()))),
|
||||
Ordinal => (3, date.map(|d| i64::from(d.ordinal()))),
|
||||
Hour => (2, time.map(|t| i64::from(t.hour()))),
|
||||
Hour12 => (2, time.map(|t| i64::from(t.hour12().1))),
|
||||
Minute => (2, time.map(|t| i64::from(t.minute()))),
|
||||
Second => (2, time.map(|t| i64::from(t.second() + t.nanosecond() / 1_000_000_000))),
|
||||
Nanosecond => (9, time.map(|t| i64::from(t.nanosecond() % 1_000_000_000))),
|
||||
Timestamp => (
|
||||
1,
|
||||
match (date, time, off) {
|
||||
(Some(d), Some(t), None) => Some(d.and_time(*t).timestamp()),
|
||||
(Some(d), Some(t), Some(&(_, off))) => {
|
||||
Some((d.and_time(*t) - off).timestamp())
|
||||
}
|
||||
(_, _, _) => None,
|
||||
},
|
||||
),
|
||||
|
||||
// for the future expansion
|
||||
Internal(ref int) => match int._dummy {},
|
||||
};
|
||||
|
||||
if let Some(v) = v {
|
||||
if (spec == &Year || spec == &IsoYear) && !(0 <= v && v < 10_000) {
|
||||
// non-four-digit years require an explicit sign as per ISO 8601
|
||||
match *pad {
|
||||
Pad::None => write!(result, "{:+}", v),
|
||||
Pad::Zero => write!(result, "{:+01$}", v, width + 1),
|
||||
Pad::Space => write!(result, "{:+1$}", v, width + 1),
|
||||
}
|
||||
} else {
|
||||
match *pad {
|
||||
Pad::None => write!(result, "{}", v),
|
||||
Pad::Zero => write!(result, "{:01$}", v, width),
|
||||
Pad::Space => write!(result, "{:1$}", v, width),
|
||||
}
|
||||
}?
|
||||
} else {
|
||||
return Err(fmt::Error); // insufficient arguments for given format
|
||||
}
|
||||
}
|
||||
|
||||
Item::Fixed(ref spec) => {
|
||||
use self::Fixed::*;
|
||||
|
||||
/// Prints an offset from UTC in the format of `+HHMM` or `+HH:MM`.
|
||||
/// `Z` instead of `+00[:]00` is allowed when `allow_zulu` is true.
|
||||
fn write_local_minus_utc(
|
||||
result: &mut String,
|
||||
off: FixedOffset,
|
||||
allow_zulu: bool,
|
||||
use_colon: bool,
|
||||
) -> fmt::Result {
|
||||
let off = off.local_minus_utc();
|
||||
if !allow_zulu || off != 0 {
|
||||
let (sign, off) = if off < 0 { ('-', -off) } else { ('+', off) };
|
||||
if use_colon {
|
||||
write!(result, "{}{:02}:{:02}", sign, off / 3600, off / 60 % 60)
|
||||
} else {
|
||||
write!(result, "{}{:02}{:02}", sign, off / 3600, off / 60 % 60)
|
||||
}
|
||||
} else {
|
||||
result.push_str("Z");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let ret =
|
||||
match *spec {
|
||||
ShortMonthName => date.map(|d| {
|
||||
result.push_str(short_months[d.month0() as usize]);
|
||||
Ok(())
|
||||
}),
|
||||
LongMonthName => date.map(|d| {
|
||||
result.push_str(long_months[d.month0() as usize]);
|
||||
Ok(())
|
||||
}),
|
||||
ShortWeekdayName => date.map(|d| {
|
||||
result
|
||||
.push_str(short_weekdays[d.weekday().num_days_from_sunday() as usize]);
|
||||
Ok(())
|
||||
}),
|
||||
LongWeekdayName => date.map(|d| {
|
||||
result.push_str(long_weekdays[d.weekday().num_days_from_sunday() as usize]);
|
||||
Ok(())
|
||||
}),
|
||||
LowerAmPm => time.map(|t| {
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(useless_asref))]
|
||||
{
|
||||
result.push_str(if t.hour12().0 {
|
||||
am_pm_lowercase[1].as_ref()
|
||||
} else {
|
||||
am_pm_lowercase[0].as_ref()
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}),
|
||||
UpperAmPm => time.map(|t| {
|
||||
result.push_str(if t.hour12().0 { am_pm[1] } else { am_pm[0] });
|
||||
Ok(())
|
||||
}),
|
||||
Nanosecond => time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
if nano == 0 {
|
||||
Ok(())
|
||||
} else if nano % 1_000_000 == 0 {
|
||||
write!(result, ".{:03}", nano / 1_000_000)
|
||||
} else if nano % 1_000 == 0 {
|
||||
write!(result, ".{:06}", nano / 1_000)
|
||||
} else {
|
||||
write!(result, ".{:09}", nano)
|
||||
}
|
||||
}),
|
||||
Nanosecond3 => time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, ".{:03}", nano / 1_000_000)
|
||||
}),
|
||||
Nanosecond6 => time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, ".{:06}", nano / 1_000)
|
||||
}),
|
||||
Nanosecond9 => time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, ".{:09}", nano)
|
||||
}),
|
||||
Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => time
|
||||
.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, "{:03}", nano / 1_000_000)
|
||||
}),
|
||||
Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => time
|
||||
.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, "{:06}", nano / 1_000)
|
||||
}),
|
||||
Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => time
|
||||
.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, "{:09}", nano)
|
||||
}),
|
||||
TimezoneName => off.map(|&(ref name, _)| {
|
||||
result.push_str(name);
|
||||
Ok(())
|
||||
}),
|
||||
TimezoneOffsetColon => {
|
||||
off.map(|&(_, off)| write_local_minus_utc(result, off, false, true))
|
||||
}
|
||||
TimezoneOffsetColonZ => {
|
||||
off.map(|&(_, off)| write_local_minus_utc(result, off, true, true))
|
||||
}
|
||||
TimezoneOffset => {
|
||||
off.map(|&(_, off)| write_local_minus_utc(result, off, false, false))
|
||||
}
|
||||
TimezoneOffsetZ => {
|
||||
off.map(|&(_, off)| write_local_minus_utc(result, off, true, false))
|
||||
}
|
||||
Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive }) => {
|
||||
panic!("Do not try to write %#z it is undefined")
|
||||
}
|
||||
RFC2822 =>
|
||||
// same as `%a, %d %b %Y %H:%M:%S %z`
|
||||
{
|
||||
if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
|
||||
let sec = t.second() + t.nanosecond() / 1_000_000_000;
|
||||
write!(
|
||||
result,
|
||||
"{}, {:02} {} {:04} {:02}:{:02}:{:02} ",
|
||||
short_weekdays[d.weekday().num_days_from_sunday() as usize],
|
||||
d.day(),
|
||||
short_months[d.month0() as usize],
|
||||
d.year(),
|
||||
t.hour(),
|
||||
t.minute(),
|
||||
sec
|
||||
)?;
|
||||
Some(write_local_minus_utc(result, off, false, false))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
RFC3339 =>
|
||||
// same as `%Y-%m-%dT%H:%M:%S%.f%:z`
|
||||
{
|
||||
if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
|
||||
// reuse `Debug` impls which already print ISO 8601 format.
|
||||
// this is faster in this way.
|
||||
write!(result, "{:?}T{:?}", d, t)?;
|
||||
Some(write_local_minus_utc(result, off, false, true))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match ret {
|
||||
Some(ret) => ret?,
|
||||
None => return Err(fmt::Error), // insufficient arguments for given format
|
||||
}
|
||||
}
|
||||
|
||||
Item::Error => return Err(fmt::Error),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tries to format given arguments with given formatting items.
|
||||
/// Internally used by `DelayedFormat`.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
pub fn format<'a, I, B>(
|
||||
w: &mut fmt::Formatter,
|
||||
date: Option<&NaiveDate>,
|
||||
time: Option<&NaiveTime>,
|
||||
off: Option<&(String, FixedOffset)>,
|
||||
items: I,
|
||||
) -> fmt::Result
|
||||
where
|
||||
I: Iterator<Item = B> + Clone,
|
||||
B: Borrow<Item<'a>>,
|
||||
{
|
||||
let mut result = String::new();
|
||||
for item in items {
|
||||
format_inner(&mut result, date, time, off, item.borrow(), None)?;
|
||||
}
|
||||
w.pad(&result)
|
||||
}
|
||||
|
||||
mod parsed;
|
||||
|
||||
// due to the size of parsing routines, they are in separate modules.
|
||||
mod parse;
|
||||
mod scan;
|
||||
|
||||
pub mod strftime;
|
||||
|
||||
/// A *temporary* object which can be used as an argument to `format!` or others.
|
||||
/// This is normally constructed via `format` methods of each date and time type.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[derive(Debug)]
|
||||
pub struct DelayedFormat<I> {
|
||||
/// The date view, if any.
|
||||
date: Option<NaiveDate>,
|
||||
/// The time view, if any.
|
||||
time: Option<NaiveTime>,
|
||||
/// The name and local-to-UTC difference for the offset (timezone), if any.
|
||||
off: Option<(String, FixedOffset)>,
|
||||
/// An iterator returning formatting items.
|
||||
items: I,
|
||||
/// Locale used for text.
|
||||
locale: Option<Locale>,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> {
|
||||
/// Makes a new `DelayedFormat` value out of local date and time.
|
||||
pub fn new(date: Option<NaiveDate>, time: Option<NaiveTime>, items: I) -> DelayedFormat<I> {
|
||||
DelayedFormat { date: date, time: time, off: None, items: items, locale: None }
|
||||
}
|
||||
|
||||
/// Makes a new `DelayedFormat` value out of local date and time and UTC offset.
|
||||
pub fn new_with_offset<Off>(
|
||||
date: Option<NaiveDate>,
|
||||
time: Option<NaiveTime>,
|
||||
offset: &Off,
|
||||
items: I,
|
||||
) -> DelayedFormat<I>
|
||||
where
|
||||
Off: Offset + fmt::Display,
|
||||
{
|
||||
let name_and_diff = (offset.to_string(), offset.fix());
|
||||
DelayedFormat {
|
||||
date: date,
|
||||
time: time,
|
||||
off: Some(name_and_diff),
|
||||
items: items,
|
||||
locale: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `DelayedFormat` value out of local date and time and locale.
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
pub fn new_with_locale(
|
||||
date: Option<NaiveDate>,
|
||||
time: Option<NaiveTime>,
|
||||
items: I,
|
||||
locale: Locale,
|
||||
) -> DelayedFormat<I> {
|
||||
DelayedFormat { date: date, time: time, off: None, items: items, locale: Some(locale) }
|
||||
}
|
||||
|
||||
/// Makes a new `DelayedFormat` value out of local date and time, UTC offset and locale.
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
pub fn new_with_offset_and_locale<Off>(
|
||||
date: Option<NaiveDate>,
|
||||
time: Option<NaiveTime>,
|
||||
offset: &Off,
|
||||
items: I,
|
||||
locale: Locale,
|
||||
) -> DelayedFormat<I>
|
||||
where
|
||||
Off: Offset + fmt::Display,
|
||||
{
|
||||
let name_and_diff = (offset.to_string(), offset.fix());
|
||||
DelayedFormat {
|
||||
date: date,
|
||||
time: time,
|
||||
off: Some(name_and_diff),
|
||||
items: items,
|
||||
locale: Some(locale),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> fmt::Display for DelayedFormat<I> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
{
|
||||
if let Some(locale) = self.locale {
|
||||
return format_localized(
|
||||
f,
|
||||
self.date.as_ref(),
|
||||
self.time.as_ref(),
|
||||
self.off.as_ref(),
|
||||
self.items.clone(),
|
||||
locale,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
format(f, self.date.as_ref(), self.time.as_ref(), self.off.as_ref(), self.items.clone())
|
||||
}
|
||||
}
|
||||
|
||||
// this implementation is here only because we need some private code from `scan`
|
||||
|
||||
/// Parsing a `str` into a `Weekday` uses the format [`%W`](./format/strftime/index.html).
|
||||
/// Parsing a `str` into a `Weekday` uses the format [`%A`](./format/strftime/index.html).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// ```
|
||||
/// use chrono::Weekday;
|
||||
///
|
||||
/// assert_eq!("Sunday".parse::<Weekday>(), Ok(Weekday::Sun));
|
||||
/// assert!("any day".parse::<Weekday>().is_err());
|
||||
/// ~~~~
|
||||
/// ```
|
||||
///
|
||||
/// The parsing is case-insensitive.
|
||||
///
|
||||
/// ~~~~
|
||||
/// ```
|
||||
/// # use chrono::Weekday;
|
||||
/// assert_eq!("mON".parse::<Weekday>(), Ok(Weekday::Mon));
|
||||
/// ~~~~
|
||||
/// ```
|
||||
///
|
||||
/// Only the shortest form (e.g. `sun`) and the longest form (e.g. `sunday`) is accepted.
|
||||
///
|
||||
/// ~~~~
|
||||
/// ```
|
||||
/// # use chrono::Weekday;
|
||||
/// assert!("thurs".parse::<Weekday>().is_err());
|
||||
/// ~~~~
|
||||
/// ```
|
||||
impl FromStr for Weekday {
|
||||
type Err = ParseWeekdayError;
|
||||
|
||||
@@ -849,68 +505,31 @@ impl FromStr for Weekday {
|
||||
}
|
||||
}
|
||||
|
||||
/// Formats single formatting item
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
pub fn format_item_localized<'a>(
|
||||
w: &mut fmt::Formatter,
|
||||
date: Option<&NaiveDate>,
|
||||
time: Option<&NaiveTime>,
|
||||
off: Option<&(String, FixedOffset)>,
|
||||
item: &Item<'a>,
|
||||
locale: Locale,
|
||||
) -> fmt::Result {
|
||||
let mut result = String::new();
|
||||
format_inner(&mut result, date, time, off, item, Some(locale))?;
|
||||
w.pad(&result)
|
||||
}
|
||||
|
||||
/// Tries to format given arguments with given formatting items.
|
||||
/// Internally used by `DelayedFormat`.
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
pub fn format_localized<'a, I, B>(
|
||||
w: &mut fmt::Formatter,
|
||||
date: Option<&NaiveDate>,
|
||||
time: Option<&NaiveTime>,
|
||||
off: Option<&(String, FixedOffset)>,
|
||||
items: I,
|
||||
locale: Locale,
|
||||
) -> fmt::Result
|
||||
where
|
||||
I: Iterator<Item = B> + Clone,
|
||||
B: Borrow<Item<'a>>,
|
||||
{
|
||||
let mut result = String::new();
|
||||
for item in items {
|
||||
format_inner(&mut result, date, time, off, item.borrow(), Some(locale))?;
|
||||
}
|
||||
w.pad(&result)
|
||||
}
|
||||
|
||||
/// Parsing a `str` into a `Month` uses the format [`%W`](./format/strftime/index.html).
|
||||
/// Parsing a `str` into a `Month` uses the format [`%B`](./format/strftime/index.html).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// ```
|
||||
/// use chrono::Month;
|
||||
///
|
||||
/// assert_eq!("January".parse::<Month>(), Ok(Month::January));
|
||||
/// assert!("any day".parse::<Month>().is_err());
|
||||
/// ~~~~
|
||||
/// ```
|
||||
///
|
||||
/// The parsing is case-insensitive.
|
||||
///
|
||||
/// ~~~~
|
||||
/// ```
|
||||
/// # use chrono::Month;
|
||||
/// assert_eq!("fEbruARy".parse::<Month>(), Ok(Month::February));
|
||||
/// ~~~~
|
||||
/// ```
|
||||
///
|
||||
/// Only the shortest form (e.g. `jan`) and the longest form (e.g. `january`) is accepted.
|
||||
///
|
||||
/// ~~~~
|
||||
/// ```
|
||||
/// # use chrono::Month;
|
||||
/// assert!("septem".parse::<Month>().is_err());
|
||||
/// assert!("Augustin".parse::<Month>().is_err());
|
||||
/// ~~~~
|
||||
/// ```
|
||||
impl FromStr for Month {
|
||||
type Err = ParseMonthError;
|
||||
|
||||
|
||||
1916
third_party/rust/chrono/src/format/parse.rs
vendored
1916
third_party/rust/chrono/src/format/parse.rs
vendored
File diff suppressed because it is too large
Load Diff
1277
third_party/rust/chrono/src/format/parsed.rs
vendored
1277
third_party/rust/chrono/src/format/parsed.rs
vendored
File diff suppressed because it is too large
Load Diff
350
third_party/rust/chrono/src/format/scan.rs
vendored
350
third_party/rust/chrono/src/format/scan.rs
vendored
@@ -5,28 +5,8 @@
|
||||
* Various scanning routines for the parser.
|
||||
*/
|
||||
|
||||
#![allow(deprecated)]
|
||||
|
||||
use super::{ParseResult, INVALID, OUT_OF_RANGE, TOO_SHORT};
|
||||
use Weekday;
|
||||
|
||||
/// Returns true when two slices are equal case-insensitively (in ASCII).
|
||||
/// Assumes that the `pattern` is already converted to lower case.
|
||||
fn equals(s: &str, pattern: &str) -> bool {
|
||||
let mut xs = s.as_bytes().iter().map(|&c| match c {
|
||||
b'A'...b'Z' => c + 32,
|
||||
_ => c,
|
||||
});
|
||||
let mut ys = pattern.as_bytes().iter().cloned();
|
||||
loop {
|
||||
match (xs.next(), ys.next()) {
|
||||
(None, None) => return true,
|
||||
(None, _) | (_, None) => return false,
|
||||
(Some(x), Some(y)) if x != y => return false,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
use super::{INVALID, OUT_OF_RANGE, ParseResult, TOO_SHORT};
|
||||
use crate::Weekday;
|
||||
|
||||
/// Tries to parse the non-negative number from `min` to `max` digits.
|
||||
///
|
||||
@@ -34,7 +14,7 @@ fn equals(s: &str, pattern: &str) -> bool {
|
||||
/// More than `max` digits are consumed up to the first `max` digits.
|
||||
/// Any number that does not fit in `i64` is an error.
|
||||
#[inline]
|
||||
pub fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> {
|
||||
pub(super) fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> {
|
||||
assert!(min <= max);
|
||||
|
||||
// We are only interested in ascii numbers, so we can work with the `str` as bytes. We stop on
|
||||
@@ -48,7 +28,7 @@ pub fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> {
|
||||
let mut n = 0i64;
|
||||
for (i, c) in bytes.iter().take(max).cloned().enumerate() {
|
||||
// cloned() = copied()
|
||||
if c < b'0' || b'9' < c {
|
||||
if !c.is_ascii_digit() {
|
||||
if i < min {
|
||||
return Err(INVALID);
|
||||
} else {
|
||||
@@ -62,12 +42,12 @@ pub fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> {
|
||||
};
|
||||
}
|
||||
|
||||
Ok((&s[::core::cmp::min(max, bytes.len())..], n))
|
||||
Ok((&s[core::cmp::min(max, bytes.len())..], n))
|
||||
}
|
||||
|
||||
/// Tries to consume at least one digits as a fractional second.
|
||||
/// Returns the number of whole nanoseconds (0--999,999,999).
|
||||
pub fn nanosecond(s: &str) -> ParseResult<(&str, i64)> {
|
||||
pub(super) fn nanosecond(s: &str) -> ParseResult<(&str, i64)> {
|
||||
// record the number of digits consumed for later scaling.
|
||||
let origlen = s.len();
|
||||
let (s, v) = number(s, 1, 9)?;
|
||||
@@ -79,14 +59,14 @@ pub fn nanosecond(s: &str) -> ParseResult<(&str, i64)> {
|
||||
let v = v.checked_mul(SCALE[consumed]).ok_or(OUT_OF_RANGE)?;
|
||||
|
||||
// if there are more than 9 digits, skip next digits.
|
||||
let s = s.trim_left_matches(|c: char| '0' <= c && c <= '9');
|
||||
let s = s.trim_start_matches(|c: char| c.is_ascii_digit());
|
||||
|
||||
Ok((s, v))
|
||||
}
|
||||
|
||||
/// Tries to consume a fixed number of digits as a fractional second.
|
||||
/// Returns the number of whole nanoseconds (0--999,999,999).
|
||||
pub fn nanosecond_fixed(s: &str, digits: usize) -> ParseResult<(&str, i64)> {
|
||||
pub(super) fn nanosecond_fixed(s: &str, digits: usize) -> ParseResult<(&str, i64)> {
|
||||
// record the number of digits consumed for later scaling.
|
||||
let (s, v) = number(s, digits, digits)?;
|
||||
|
||||
@@ -99,7 +79,7 @@ pub fn nanosecond_fixed(s: &str, digits: usize) -> ParseResult<(&str, i64)> {
|
||||
}
|
||||
|
||||
/// Tries to parse the month index (0 through 11) with the first three ASCII letters.
|
||||
pub fn short_month0(s: &str) -> ParseResult<(&str, u8)> {
|
||||
pub(super) fn short_month0(s: &str) -> ParseResult<(&str, u8)> {
|
||||
if s.len() < 3 {
|
||||
return Err(TOO_SHORT);
|
||||
}
|
||||
@@ -123,7 +103,7 @@ pub fn short_month0(s: &str) -> ParseResult<(&str, u8)> {
|
||||
}
|
||||
|
||||
/// Tries to parse the weekday with the first three ASCII letters.
|
||||
pub fn short_weekday(s: &str) -> ParseResult<(&str, Weekday)> {
|
||||
pub(super) fn short_weekday(s: &str) -> ParseResult<(&str, Weekday)> {
|
||||
if s.len() < 3 {
|
||||
return Err(TOO_SHORT);
|
||||
}
|
||||
@@ -143,16 +123,18 @@ pub fn short_weekday(s: &str) -> ParseResult<(&str, Weekday)> {
|
||||
|
||||
/// Tries to parse the month index (0 through 11) with short or long month names.
|
||||
/// It prefers long month names to short month names when both are possible.
|
||||
pub fn short_or_long_month0(s: &str) -> ParseResult<(&str, u8)> {
|
||||
pub(super) fn short_or_long_month0(s: &str) -> ParseResult<(&str, u8)> {
|
||||
// lowercased month names, minus first three chars
|
||||
static LONG_MONTH_SUFFIXES: [&'static str; 12] =
|
||||
["uary", "ruary", "ch", "il", "", "e", "y", "ust", "tember", "ober", "ember", "ember"];
|
||||
static LONG_MONTH_SUFFIXES: [&[u8]; 12] = [
|
||||
b"uary", b"ruary", b"ch", b"il", b"", b"e", b"y", b"ust", b"tember", b"ober", b"ember",
|
||||
b"ember",
|
||||
];
|
||||
|
||||
let (mut s, month0) = short_month0(s)?;
|
||||
|
||||
// tries to consume the suffix if possible
|
||||
let suffix = LONG_MONTH_SUFFIXES[month0 as usize];
|
||||
if s.len() >= suffix.len() && equals(&s[..suffix.len()], suffix) {
|
||||
if s.len() >= suffix.len() && s.as_bytes()[..suffix.len()].eq_ignore_ascii_case(suffix) {
|
||||
s = &s[suffix.len()..];
|
||||
}
|
||||
|
||||
@@ -161,16 +143,16 @@ pub fn short_or_long_month0(s: &str) -> ParseResult<(&str, u8)> {
|
||||
|
||||
/// Tries to parse the weekday with short or long weekday names.
|
||||
/// It prefers long weekday names to short weekday names when both are possible.
|
||||
pub fn short_or_long_weekday(s: &str) -> ParseResult<(&str, Weekday)> {
|
||||
pub(super) fn short_or_long_weekday(s: &str) -> ParseResult<(&str, Weekday)> {
|
||||
// lowercased weekday names, minus first three chars
|
||||
static LONG_WEEKDAY_SUFFIXES: [&'static str; 7] =
|
||||
["day", "sday", "nesday", "rsday", "day", "urday", "day"];
|
||||
static LONG_WEEKDAY_SUFFIXES: [&[u8]; 7] =
|
||||
[b"day", b"sday", b"nesday", b"rsday", b"day", b"urday", b"day"];
|
||||
|
||||
let (mut s, weekday) = short_weekday(s)?;
|
||||
|
||||
// tries to consume the suffix if possible
|
||||
let suffix = LONG_WEEKDAY_SUFFIXES[weekday.num_days_from_monday() as usize];
|
||||
if s.len() >= suffix.len() && equals(&s[..suffix.len()], suffix) {
|
||||
if s.len() >= suffix.len() && s.as_bytes()[..suffix.len()].eq_ignore_ascii_case(suffix) {
|
||||
s = &s[suffix.len()..];
|
||||
}
|
||||
|
||||
@@ -178,7 +160,7 @@ pub fn short_or_long_weekday(s: &str) -> ParseResult<(&str, Weekday)> {
|
||||
}
|
||||
|
||||
/// Tries to consume exactly one given character.
|
||||
pub fn char(s: &str, c1: u8) -> ParseResult<&str> {
|
||||
pub(super) fn char(s: &str, c1: u8) -> ParseResult<&str> {
|
||||
match s.as_bytes().first() {
|
||||
Some(&c) if c == c1 => Ok(&s[1..]),
|
||||
Some(_) => Err(INVALID),
|
||||
@@ -187,8 +169,8 @@ pub fn char(s: &str, c1: u8) -> ParseResult<&str> {
|
||||
}
|
||||
|
||||
/// Tries to consume one or more whitespace.
|
||||
pub fn space(s: &str) -> ParseResult<&str> {
|
||||
let s_ = s.trim_left();
|
||||
pub(super) fn space(s: &str) -> ParseResult<&str> {
|
||||
let s_ = s.trim_start();
|
||||
if s_.len() < s.len() {
|
||||
Ok(s_)
|
||||
} else if s.is_empty() {
|
||||
@@ -199,48 +181,73 @@ pub fn space(s: &str) -> ParseResult<&str> {
|
||||
}
|
||||
|
||||
/// Consumes any number (including zero) of colon or spaces.
|
||||
pub fn colon_or_space(s: &str) -> ParseResult<&str> {
|
||||
Ok(s.trim_left_matches(|c: char| c == ':' || c.is_whitespace()))
|
||||
pub(crate) fn colon_or_space(s: &str) -> ParseResult<&str> {
|
||||
Ok(s.trim_start_matches(|c: char| c == ':' || c.is_whitespace()))
|
||||
}
|
||||
|
||||
/// Tries to parse `[-+]\d\d` continued by `\d\d`. Return an offset in seconds if possible.
|
||||
/// Parse a timezone from `s` and return the offset in seconds.
|
||||
///
|
||||
/// The additional `colon` may be used to parse a mandatory or optional `:`
|
||||
/// between hours and minutes, and should return either a new suffix or `Err` when parsing fails.
|
||||
pub fn timezone_offset<F>(s: &str, consume_colon: F) -> ParseResult<(&str, i32)>
|
||||
where
|
||||
F: FnMut(&str) -> ParseResult<&str>,
|
||||
{
|
||||
timezone_offset_internal(s, consume_colon, false)
|
||||
}
|
||||
|
||||
fn timezone_offset_internal<F>(
|
||||
/// The `consume_colon` function is used to parse a mandatory or optional `:`
|
||||
/// separator between hours offset and minutes offset.
|
||||
///
|
||||
/// The `allow_missing_minutes` flag allows the timezone minutes offset to be
|
||||
/// missing from `s`.
|
||||
///
|
||||
/// The `allow_tz_minus_sign` flag allows the timezone offset negative character
|
||||
/// to also be `−` MINUS SIGN (U+2212) in addition to the typical
|
||||
/// ASCII-compatible `-` HYPHEN-MINUS (U+2D).
|
||||
/// This is part of [RFC 3339 & ISO 8601].
|
||||
///
|
||||
/// [RFC 3339 & ISO 8601]: https://en.wikipedia.org/w/index.php?title=ISO_8601&oldid=1114309368#Time_offsets_from_UTC
|
||||
pub(crate) fn timezone_offset<F>(
|
||||
mut s: &str,
|
||||
mut consume_colon: F,
|
||||
allow_zulu: bool,
|
||||
allow_missing_minutes: bool,
|
||||
allow_tz_minus_sign: bool,
|
||||
) -> ParseResult<(&str, i32)>
|
||||
where
|
||||
F: FnMut(&str) -> ParseResult<&str>,
|
||||
{
|
||||
fn digits(s: &str) -> ParseResult<(u8, u8)> {
|
||||
let b = s.as_bytes();
|
||||
if b.len() < 2 {
|
||||
Err(TOO_SHORT)
|
||||
} else {
|
||||
Ok((b[0], b[1]))
|
||||
if allow_zulu {
|
||||
if let Some(&b'Z' | &b'z') = s.as_bytes().first() {
|
||||
return Ok((&s[1..], 0));
|
||||
}
|
||||
}
|
||||
let negative = match s.as_bytes().first() {
|
||||
Some(&b'+') => false,
|
||||
Some(&b'-') => true,
|
||||
|
||||
const fn digits(s: &str) -> ParseResult<(u8, u8)> {
|
||||
let b = s.as_bytes();
|
||||
if b.len() < 2 { Err(TOO_SHORT) } else { Ok((b[0], b[1])) }
|
||||
}
|
||||
let negative = match s.chars().next() {
|
||||
Some('+') => {
|
||||
// PLUS SIGN (U+2B)
|
||||
s = &s['+'.len_utf8()..];
|
||||
|
||||
false
|
||||
}
|
||||
Some('-') => {
|
||||
// HYPHEN-MINUS (U+2D)
|
||||
s = &s['-'.len_utf8()..];
|
||||
|
||||
true
|
||||
}
|
||||
Some('−') => {
|
||||
// MINUS SIGN (U+2212)
|
||||
if !allow_tz_minus_sign {
|
||||
return Err(INVALID);
|
||||
}
|
||||
s = &s['−'.len_utf8()..];
|
||||
|
||||
true
|
||||
}
|
||||
Some(_) => return Err(INVALID),
|
||||
None => return Err(TOO_SHORT),
|
||||
};
|
||||
s = &s[1..];
|
||||
|
||||
// hours (00--99)
|
||||
let hours = match digits(s)? {
|
||||
(h1 @ b'0'...b'9', h2 @ b'0'...b'9') => i32::from((h1 - b'0') * 10 + (h2 - b'0')),
|
||||
(h1 @ b'0'..=b'9', h2 @ b'0'..=b'9') => i32::from((h1 - b'0') * 10 + (h2 - b'0')),
|
||||
_ => return Err(INVALID),
|
||||
};
|
||||
s = &s[2..];
|
||||
@@ -252,8 +259,8 @@ where
|
||||
// if the next two items are digits then we have to add minutes
|
||||
let minutes = if let Ok(ds) = digits(s) {
|
||||
match ds {
|
||||
(m1 @ b'0'...b'5', m2 @ b'0'...b'9') => i32::from((m1 - b'0') * 10 + (m2 - b'0')),
|
||||
(b'6'...b'9', b'0'...b'9') => return Err(OUT_OF_RANGE),
|
||||
(m1 @ b'0'..=b'5', m2 @ b'0'..=b'9') => i32::from((m1 - b'0') * 10 + (m2 - b'0')),
|
||||
(b'6'..=b'9', b'0'..=b'9') => return Err(OUT_OF_RANGE),
|
||||
_ => return Err(INVALID),
|
||||
}
|
||||
} else if allow_missing_minutes {
|
||||
@@ -263,7 +270,7 @@ where
|
||||
};
|
||||
s = match s.len() {
|
||||
len if len >= 2 => &s[2..],
|
||||
len if len == 0 => s,
|
||||
0 => s,
|
||||
_ => return Err(TOO_SHORT),
|
||||
};
|
||||
|
||||
@@ -271,80 +278,155 @@ where
|
||||
Ok((s, if negative { -seconds } else { seconds }))
|
||||
}
|
||||
|
||||
/// Same as `timezone_offset` but also allows for `z`/`Z` which is the same as `+00:00`.
|
||||
pub fn timezone_offset_zulu<F>(s: &str, colon: F) -> ParseResult<(&str, i32)>
|
||||
where
|
||||
F: FnMut(&str) -> ParseResult<&str>,
|
||||
{
|
||||
let bytes = s.as_bytes();
|
||||
match bytes.first() {
|
||||
Some(&b'z') | Some(&b'Z') => Ok((&s[1..], 0)),
|
||||
Some(&b'u') | Some(&b'U') => {
|
||||
if bytes.len() >= 3 {
|
||||
let (b, c) = (bytes[1], bytes[2]);
|
||||
match (b | 32, c | 32) {
|
||||
(b't', b'c') => Ok((&s[3..], 0)),
|
||||
_ => Err(INVALID),
|
||||
}
|
||||
} else {
|
||||
Err(INVALID)
|
||||
}
|
||||
}
|
||||
_ => timezone_offset(s, colon),
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as `timezone_offset` but also allows for `z`/`Z` which is the same as
|
||||
/// `+00:00`, and allows missing minutes entirely.
|
||||
pub fn timezone_offset_permissive<F>(s: &str, colon: F) -> ParseResult<(&str, i32)>
|
||||
where
|
||||
F: FnMut(&str) -> ParseResult<&str>,
|
||||
{
|
||||
match s.as_bytes().first() {
|
||||
Some(&b'z') | Some(&b'Z') => Ok((&s[1..], 0)),
|
||||
_ => timezone_offset_internal(s, colon, true),
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as `timezone_offset` but also allows for RFC 2822 legacy timezones.
|
||||
/// May return `None` which indicates an insufficient offset data (i.e. `-0000`).
|
||||
pub fn timezone_offset_2822(s: &str) -> ParseResult<(&str, Option<i32>)> {
|
||||
/// See [RFC 2822 Section 4.3].
|
||||
///
|
||||
/// [RFC 2822 Section 4.3]: https://tools.ietf.org/html/rfc2822#section-4.3
|
||||
pub(super) fn timezone_offset_2822(s: &str) -> ParseResult<(&str, i32)> {
|
||||
// tries to parse legacy time zone names
|
||||
let upto = s
|
||||
.as_bytes()
|
||||
.iter()
|
||||
.position(|&c| match c {
|
||||
b'a'...b'z' | b'A'...b'Z' => false,
|
||||
_ => true,
|
||||
})
|
||||
.unwrap_or_else(|| s.len());
|
||||
let upto = s.as_bytes().iter().position(|&c| !c.is_ascii_alphabetic()).unwrap_or(s.len());
|
||||
if upto > 0 {
|
||||
let name = &s[..upto];
|
||||
let name = &s.as_bytes()[..upto];
|
||||
let s = &s[upto..];
|
||||
let offset_hours = |o| Ok((s, Some(o * 3600)));
|
||||
if equals(name, "gmt") || equals(name, "ut") {
|
||||
offset_hours(0)
|
||||
} else if equals(name, "edt") {
|
||||
offset_hours(-4)
|
||||
} else if equals(name, "est") || equals(name, "cdt") {
|
||||
offset_hours(-5)
|
||||
} else if equals(name, "cst") || equals(name, "mdt") {
|
||||
offset_hours(-6)
|
||||
} else if equals(name, "mst") || equals(name, "pdt") {
|
||||
offset_hours(-7)
|
||||
} else if equals(name, "pst") {
|
||||
offset_hours(-8)
|
||||
} else {
|
||||
Ok((s, None)) // recommended by RFC 2822: consume but treat it as -0000
|
||||
let offset_hours = |o| Ok((s, o * 3600));
|
||||
// RFC 2822 requires support for some named North America timezones, a small subset of all
|
||||
// named timezones.
|
||||
if name.eq_ignore_ascii_case(b"gmt")
|
||||
|| name.eq_ignore_ascii_case(b"ut")
|
||||
|| name.eq_ignore_ascii_case(b"z")
|
||||
{
|
||||
return offset_hours(0);
|
||||
} else if name.eq_ignore_ascii_case(b"edt") {
|
||||
return offset_hours(-4);
|
||||
} else if name.eq_ignore_ascii_case(b"est") || name.eq_ignore_ascii_case(b"cdt") {
|
||||
return offset_hours(-5);
|
||||
} else if name.eq_ignore_ascii_case(b"cst") || name.eq_ignore_ascii_case(b"mdt") {
|
||||
return offset_hours(-6);
|
||||
} else if name.eq_ignore_ascii_case(b"mst") || name.eq_ignore_ascii_case(b"pdt") {
|
||||
return offset_hours(-7);
|
||||
} else if name.eq_ignore_ascii_case(b"pst") {
|
||||
return offset_hours(-8);
|
||||
} else if name.len() == 1 {
|
||||
if let b'a'..=b'i' | b'k'..=b'y' | b'A'..=b'I' | b'K'..=b'Y' = name[0] {
|
||||
// recommended by RFC 2822: consume but treat it as -0000
|
||||
return Ok((s, 0));
|
||||
}
|
||||
}
|
||||
Err(INVALID)
|
||||
} else {
|
||||
let (s_, offset) = timezone_offset(s, |s| Ok(s))?;
|
||||
Ok((s_, Some(offset)))
|
||||
timezone_offset(s, |s| Ok(s), false, false, false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to consume everyting until next whitespace-like symbol.
|
||||
/// Does not provide any offset information from the consumed data.
|
||||
pub fn timezone_name_skip(s: &str) -> ParseResult<(&str, ())> {
|
||||
Ok((s.trim_left_matches(|c: char| !c.is_whitespace()), ()))
|
||||
/// Tries to consume an RFC2822 comment including preceding ` `.
|
||||
///
|
||||
/// Returns the remaining string after the closing parenthesis.
|
||||
pub(super) fn comment_2822(s: &str) -> ParseResult<(&str, ())> {
|
||||
use CommentState::*;
|
||||
|
||||
let s = s.trim_start();
|
||||
|
||||
let mut state = Start;
|
||||
for (i, c) in s.bytes().enumerate() {
|
||||
state = match (state, c) {
|
||||
(Start, b'(') => Next(1),
|
||||
(Next(1), b')') => return Ok((&s[i + 1..], ())),
|
||||
(Next(depth), b'\\') => Escape(depth),
|
||||
(Next(depth), b'(') => Next(depth + 1),
|
||||
(Next(depth), b')') => Next(depth - 1),
|
||||
(Next(depth), _) | (Escape(depth), _) => Next(depth),
|
||||
_ => return Err(INVALID),
|
||||
};
|
||||
}
|
||||
|
||||
Err(TOO_SHORT)
|
||||
}
|
||||
|
||||
enum CommentState {
|
||||
Start,
|
||||
Next(usize),
|
||||
Escape(usize),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
comment_2822, nanosecond, nanosecond_fixed, short_or_long_month0, short_or_long_weekday,
|
||||
timezone_offset_2822,
|
||||
};
|
||||
use crate::Weekday;
|
||||
use crate::format::{INVALID, TOO_SHORT};
|
||||
|
||||
#[test]
|
||||
fn test_rfc2822_comments() {
|
||||
let testdata = [
|
||||
("", Err(TOO_SHORT)),
|
||||
(" ", Err(TOO_SHORT)),
|
||||
("x", Err(INVALID)),
|
||||
("(", Err(TOO_SHORT)),
|
||||
("()", Ok("")),
|
||||
(" \r\n\t()", Ok("")),
|
||||
("() ", Ok(" ")),
|
||||
("()z", Ok("z")),
|
||||
("(x)", Ok("")),
|
||||
("(())", Ok("")),
|
||||
("((()))", Ok("")),
|
||||
("(x(x(x)x)x)", Ok("")),
|
||||
("( x ( x ( x ) x ) x )", Ok("")),
|
||||
(r"(\)", Err(TOO_SHORT)),
|
||||
(r"(\()", Ok("")),
|
||||
(r"(\))", Ok("")),
|
||||
(r"(\\)", Ok("")),
|
||||
("(()())", Ok("")),
|
||||
("( x ( x ) x ( x ) x )", Ok("")),
|
||||
];
|
||||
|
||||
for (test_in, expected) in testdata.iter() {
|
||||
let actual = comment_2822(test_in).map(|(s, _)| s);
|
||||
assert_eq!(
|
||||
*expected, actual,
|
||||
"{:?} expected to produce {:?}, but produced {:?}.",
|
||||
test_in, expected, actual
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timezone_offset_2822() {
|
||||
assert_eq!(timezone_offset_2822("cSt").unwrap(), ("", -21600));
|
||||
assert_eq!(timezone_offset_2822("pSt").unwrap(), ("", -28800));
|
||||
assert_eq!(timezone_offset_2822("mSt").unwrap(), ("", -25200));
|
||||
assert_eq!(timezone_offset_2822("-1551").unwrap(), ("", -57060));
|
||||
assert_eq!(timezone_offset_2822("Gp"), Err(INVALID));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_short_or_long_month0() {
|
||||
assert_eq!(short_or_long_month0("JUn").unwrap(), ("", 5));
|
||||
assert_eq!(short_or_long_month0("mAy").unwrap(), ("", 4));
|
||||
assert_eq!(short_or_long_month0("AuG").unwrap(), ("", 7));
|
||||
assert_eq!(short_or_long_month0("Aprâ").unwrap(), ("â", 3));
|
||||
assert_eq!(short_or_long_month0("JUl").unwrap(), ("", 6));
|
||||
assert_eq!(short_or_long_month0("mAr").unwrap(), ("", 2));
|
||||
assert_eq!(short_or_long_month0("Jan").unwrap(), ("", 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_short_or_long_weekday() {
|
||||
assert_eq!(short_or_long_weekday("sAtu").unwrap(), ("u", Weekday::Sat));
|
||||
assert_eq!(short_or_long_weekday("thu").unwrap(), ("", Weekday::Thu));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nanosecond_fixed() {
|
||||
assert_eq!(nanosecond_fixed("", 0usize).unwrap(), ("", 0));
|
||||
assert!(nanosecond_fixed("", 1usize).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nanosecond() {
|
||||
assert_eq!(nanosecond("2Ù").unwrap(), ("Ù", 200000000));
|
||||
assert_eq!(nanosecond("8").unwrap(), ("", 800000000));
|
||||
}
|
||||
}
|
||||
|
||||
1319
third_party/rust/chrono/src/format/strftime.rs
vendored
1319
third_party/rust/chrono/src/format/strftime.rs
vendored
File diff suppressed because it is too large
Load Diff
1883
third_party/rust/chrono/src/lib.rs
vendored
1883
third_party/rust/chrono/src/lib.rs
vendored
File diff suppressed because it is too large
Load Diff
472
third_party/rust/chrono/src/month.rs
vendored
Normal file
472
third_party/rust/chrono/src/month.rs
vendored
Normal file
@@ -0,0 +1,472 @@
|
||||
use core::fmt;
|
||||
|
||||
#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))]
|
||||
use rkyv::{Archive, Deserialize, Serialize};
|
||||
|
||||
use crate::OutOfRange;
|
||||
use crate::naive::NaiveDate;
|
||||
|
||||
/// The month of the year.
|
||||
///
|
||||
/// This enum is just a convenience implementation.
|
||||
/// The month in dates created by DateLike objects does not return this enum.
|
||||
///
|
||||
/// It is possible to convert from a date to a month independently
|
||||
/// ```
|
||||
/// use chrono::prelude::*;
|
||||
/// let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap();
|
||||
/// // `2019-10-28T09:10:11Z`
|
||||
/// let month = Month::try_from(u8::try_from(date.month()).unwrap()).ok();
|
||||
/// assert_eq!(month, Some(Month::October))
|
||||
/// ```
|
||||
/// Or from a Month to an integer usable by dates
|
||||
/// ```
|
||||
/// # use chrono::prelude::*;
|
||||
/// let month = Month::January;
|
||||
/// let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap();
|
||||
/// assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28));
|
||||
/// ```
|
||||
/// Allows mapping from and to month, from 1-January to 12-December.
|
||||
/// Can be Serialized/Deserialized with serde
|
||||
// Actual implementation is zero-indexed, API intended as 1-indexed for more intuitive behavior.
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash, PartialOrd, Ord)]
|
||||
#[cfg_attr(
|
||||
any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"),
|
||||
derive(Archive, Deserialize, Serialize),
|
||||
archive(compare(PartialEq, PartialOrd)),
|
||||
archive_attr(derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash))
|
||||
)]
|
||||
#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
|
||||
#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))]
|
||||
pub enum Month {
|
||||
/// January
|
||||
January = 0,
|
||||
/// February
|
||||
February = 1,
|
||||
/// March
|
||||
March = 2,
|
||||
/// April
|
||||
April = 3,
|
||||
/// May
|
||||
May = 4,
|
||||
/// June
|
||||
June = 5,
|
||||
/// July
|
||||
July = 6,
|
||||
/// August
|
||||
August = 7,
|
||||
/// September
|
||||
September = 8,
|
||||
/// October
|
||||
October = 9,
|
||||
/// November
|
||||
November = 10,
|
||||
/// December
|
||||
December = 11,
|
||||
}
|
||||
|
||||
impl Month {
|
||||
/// The next month.
|
||||
///
|
||||
/// `m`: | `January` | `February` | `...` | `December`
|
||||
/// ----------- | --------- | ---------- | --- | ---------
|
||||
/// `m.succ()`: | `February` | `March` | `...` | `January`
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn succ(&self) -> Month {
|
||||
match *self {
|
||||
Month::January => Month::February,
|
||||
Month::February => Month::March,
|
||||
Month::March => Month::April,
|
||||
Month::April => Month::May,
|
||||
Month::May => Month::June,
|
||||
Month::June => Month::July,
|
||||
Month::July => Month::August,
|
||||
Month::August => Month::September,
|
||||
Month::September => Month::October,
|
||||
Month::October => Month::November,
|
||||
Month::November => Month::December,
|
||||
Month::December => Month::January,
|
||||
}
|
||||
}
|
||||
|
||||
/// The previous month.
|
||||
///
|
||||
/// `m`: | `January` | `February` | `...` | `December`
|
||||
/// ----------- | --------- | ---------- | --- | ---------
|
||||
/// `m.pred()`: | `December` | `January` | `...` | `November`
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn pred(&self) -> Month {
|
||||
match *self {
|
||||
Month::January => Month::December,
|
||||
Month::February => Month::January,
|
||||
Month::March => Month::February,
|
||||
Month::April => Month::March,
|
||||
Month::May => Month::April,
|
||||
Month::June => Month::May,
|
||||
Month::July => Month::June,
|
||||
Month::August => Month::July,
|
||||
Month::September => Month::August,
|
||||
Month::October => Month::September,
|
||||
Month::November => Month::October,
|
||||
Month::December => Month::November,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a month-of-year number starting from January = 1.
|
||||
///
|
||||
/// `m`: | `January` | `February` | `...` | `December`
|
||||
/// -------------------------| --------- | ---------- | --- | -----
|
||||
/// `m.number_from_month()`: | 1 | 2 | `...` | 12
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn number_from_month(&self) -> u32 {
|
||||
match *self {
|
||||
Month::January => 1,
|
||||
Month::February => 2,
|
||||
Month::March => 3,
|
||||
Month::April => 4,
|
||||
Month::May => 5,
|
||||
Month::June => 6,
|
||||
Month::July => 7,
|
||||
Month::August => 8,
|
||||
Month::September => 9,
|
||||
Month::October => 10,
|
||||
Month::November => 11,
|
||||
Month::December => 12,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the name of the month
|
||||
///
|
||||
/// ```
|
||||
/// use chrono::Month;
|
||||
///
|
||||
/// assert_eq!(Month::January.name(), "January")
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub const fn name(&self) -> &'static str {
|
||||
match *self {
|
||||
Month::January => "January",
|
||||
Month::February => "February",
|
||||
Month::March => "March",
|
||||
Month::April => "April",
|
||||
Month::May => "May",
|
||||
Month::June => "June",
|
||||
Month::July => "July",
|
||||
Month::August => "August",
|
||||
Month::September => "September",
|
||||
Month::October => "October",
|
||||
Month::November => "November",
|
||||
Month::December => "December",
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the length in days of the month
|
||||
///
|
||||
/// Yields `None` if `year` is out of range for `NaiveDate`.
|
||||
pub fn num_days(&self, year: i32) -> Option<u8> {
|
||||
Some(match *self {
|
||||
Month::January => 31,
|
||||
Month::February => match NaiveDate::from_ymd_opt(year, 2, 1)?.leap_year() {
|
||||
true => 29,
|
||||
false => 28,
|
||||
},
|
||||
Month::March => 31,
|
||||
Month::April => 30,
|
||||
Month::May => 31,
|
||||
Month::June => 30,
|
||||
Month::July => 31,
|
||||
Month::August => 31,
|
||||
Month::September => 30,
|
||||
Month::October => 31,
|
||||
Month::November => 30,
|
||||
Month::December => 31,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for Month {
|
||||
type Error = OutOfRange;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
1 => Ok(Month::January),
|
||||
2 => Ok(Month::February),
|
||||
3 => Ok(Month::March),
|
||||
4 => Ok(Month::April),
|
||||
5 => Ok(Month::May),
|
||||
6 => Ok(Month::June),
|
||||
7 => Ok(Month::July),
|
||||
8 => Ok(Month::August),
|
||||
9 => Ok(Month::September),
|
||||
10 => Ok(Month::October),
|
||||
11 => Ok(Month::November),
|
||||
12 => Ok(Month::December),
|
||||
_ => Err(OutOfRange::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl num_traits::FromPrimitive for Month {
|
||||
/// Returns an `Option<Month>` from a i64, assuming a 1-index, January = 1.
|
||||
///
|
||||
/// `Month::from_i64(n: i64)`: | `1` | `2` | ... | `12`
|
||||
/// ---------------------------| -------------------- | --------------------- | ... | -----
|
||||
/// ``: | Some(Month::January) | Some(Month::February) | ... | Some(Month::December)
|
||||
#[inline]
|
||||
fn from_u64(n: u64) -> Option<Month> {
|
||||
Self::from_u32(n as u32)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_i64(n: i64) -> Option<Month> {
|
||||
Self::from_u32(n as u32)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_u32(n: u32) -> Option<Month> {
|
||||
match n {
|
||||
1 => Some(Month::January),
|
||||
2 => Some(Month::February),
|
||||
3 => Some(Month::March),
|
||||
4 => Some(Month::April),
|
||||
5 => Some(Month::May),
|
||||
6 => Some(Month::June),
|
||||
7 => Some(Month::July),
|
||||
8 => Some(Month::August),
|
||||
9 => Some(Month::September),
|
||||
10 => Some(Month::October),
|
||||
11 => Some(Month::November),
|
||||
12 => Some(Month::December),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A duration in calendar months
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||
#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))]
|
||||
pub struct Months(pub(crate) u32);
|
||||
|
||||
impl Months {
|
||||
/// Construct a new `Months` from a number of months
|
||||
pub const fn new(num: u32) -> Self {
|
||||
Self(num)
|
||||
}
|
||||
|
||||
/// Returns the total number of months in the `Months` instance.
|
||||
#[inline]
|
||||
pub const fn as_u32(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// An error resulting from reading `<Month>` value with `FromStr`.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct ParseMonthError {
|
||||
pub(crate) _dummy: (),
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::error::Error for ParseMonthError {}
|
||||
|
||||
impl fmt::Display for ParseMonthError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "ParseMonthError {{ .. }}")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ParseMonthError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "ParseMonthError {{ .. }}")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
mod month_serde {
|
||||
use super::Month;
|
||||
use serde::{de, ser};
|
||||
|
||||
use core::fmt;
|
||||
|
||||
impl ser::Serialize for Month {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ser::Serializer,
|
||||
{
|
||||
serializer.collect_str(self.name())
|
||||
}
|
||||
}
|
||||
|
||||
struct MonthVisitor;
|
||||
|
||||
impl de::Visitor<'_> for MonthVisitor {
|
||||
type Value = Month;
|
||||
|
||||
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("Month")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
value.parse().map_err(|_| E::custom("short (3-letter) or full month names expected"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> de::Deserialize<'de> for Month {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_str(MonthVisitor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Month;
|
||||
use crate::{Datelike, Months, OutOfRange, TimeZone, Utc};
|
||||
|
||||
#[test]
|
||||
fn test_month_enum_try_from() {
|
||||
assert_eq!(Month::try_from(1), Ok(Month::January));
|
||||
assert_eq!(Month::try_from(2), Ok(Month::February));
|
||||
assert_eq!(Month::try_from(12), Ok(Month::December));
|
||||
assert_eq!(Month::try_from(13), Err(OutOfRange::new()));
|
||||
|
||||
let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap();
|
||||
assert_eq!(Month::try_from(date.month() as u8), Ok(Month::October));
|
||||
|
||||
let month = Month::January;
|
||||
let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap();
|
||||
assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_month_enum_primitive_parse() {
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
let jan_opt = Month::from_u32(1);
|
||||
let feb_opt = Month::from_u64(2);
|
||||
let dec_opt = Month::from_i64(12);
|
||||
let no_month = Month::from_u32(13);
|
||||
assert_eq!(jan_opt, Some(Month::January));
|
||||
assert_eq!(feb_opt, Some(Month::February));
|
||||
assert_eq!(dec_opt, Some(Month::December));
|
||||
assert_eq!(no_month, None);
|
||||
|
||||
let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap();
|
||||
assert_eq!(Month::from_u32(date.month()), Some(Month::October));
|
||||
|
||||
let month = Month::January;
|
||||
let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap();
|
||||
assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_month_enum_succ_pred() {
|
||||
assert_eq!(Month::January.succ(), Month::February);
|
||||
assert_eq!(Month::December.succ(), Month::January);
|
||||
assert_eq!(Month::January.pred(), Month::December);
|
||||
assert_eq!(Month::February.pred(), Month::January);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_month_partial_ord() {
|
||||
assert!(Month::January <= Month::January);
|
||||
assert!(Month::January < Month::February);
|
||||
assert!(Month::January < Month::December);
|
||||
assert!(Month::July >= Month::May);
|
||||
assert!(Month::September > Month::March);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_months_as_u32() {
|
||||
assert_eq!(Months::new(0).as_u32(), 0);
|
||||
assert_eq!(Months::new(1).as_u32(), 1);
|
||||
assert_eq!(Months::new(u32::MAX).as_u32(), u32::MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "serde")]
|
||||
fn test_serde_serialize() {
|
||||
use Month::*;
|
||||
use serde_json::to_string;
|
||||
|
||||
let cases: Vec<(Month, &str)> = vec![
|
||||
(January, "\"January\""),
|
||||
(February, "\"February\""),
|
||||
(March, "\"March\""),
|
||||
(April, "\"April\""),
|
||||
(May, "\"May\""),
|
||||
(June, "\"June\""),
|
||||
(July, "\"July\""),
|
||||
(August, "\"August\""),
|
||||
(September, "\"September\""),
|
||||
(October, "\"October\""),
|
||||
(November, "\"November\""),
|
||||
(December, "\"December\""),
|
||||
];
|
||||
|
||||
for (month, expected_str) in cases {
|
||||
let string = to_string(&month).unwrap();
|
||||
assert_eq!(string, expected_str);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "serde")]
|
||||
fn test_serde_deserialize() {
|
||||
use Month::*;
|
||||
use serde_json::from_str;
|
||||
|
||||
let cases: Vec<(&str, Month)> = vec![
|
||||
("\"january\"", January),
|
||||
("\"jan\"", January),
|
||||
("\"FeB\"", February),
|
||||
("\"MAR\"", March),
|
||||
("\"mar\"", March),
|
||||
("\"april\"", April),
|
||||
("\"may\"", May),
|
||||
("\"june\"", June),
|
||||
("\"JULY\"", July),
|
||||
("\"august\"", August),
|
||||
("\"september\"", September),
|
||||
("\"October\"", October),
|
||||
("\"November\"", November),
|
||||
("\"DECEmbEr\"", December),
|
||||
];
|
||||
|
||||
for (string, expected_month) in cases {
|
||||
let month = from_str::<Month>(string).unwrap();
|
||||
assert_eq!(month, expected_month);
|
||||
}
|
||||
|
||||
let errors: Vec<&str> =
|
||||
vec!["\"not a month\"", "\"ja\"", "\"Dece\"", "Dec", "\"Augustin\""];
|
||||
|
||||
for string in errors {
|
||||
from_str::<Month>(string).unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "rkyv-validation")]
|
||||
fn test_rkyv_validation() {
|
||||
let month = Month::January;
|
||||
let bytes = rkyv::to_bytes::<_, 1>(&month).unwrap();
|
||||
assert_eq!(rkyv::from_bytes::<Month>(&bytes).unwrap(), month);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn num_days() {
|
||||
assert_eq!(Month::January.num_days(2020), Some(31));
|
||||
assert_eq!(Month::February.num_days(2020), Some(29));
|
||||
assert_eq!(Month::February.num_days(2019), Some(28));
|
||||
}
|
||||
}
|
||||
2392
third_party/rust/chrono/src/naive/date.rs
vendored
2392
third_party/rust/chrono/src/naive/date.rs
vendored
File diff suppressed because it is too large
Load Diff
2534
third_party/rust/chrono/src/naive/date/mod.rs
vendored
Normal file
2534
third_party/rust/chrono/src/naive/date/mod.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
879
third_party/rust/chrono/src/naive/date/tests.rs
vendored
Normal file
879
third_party/rust/chrono/src/naive/date/tests.rs
vendored
Normal file
@@ -0,0 +1,879 @@
|
||||
use super::{Days, MAX_YEAR, MIN_YEAR, Months, NaiveDate};
|
||||
use crate::naive::internals::{A, AG, B, BA, C, CB, D, DC, E, ED, F, FE, G, GF, YearFlags};
|
||||
use crate::{Datelike, TimeDelta, Weekday};
|
||||
|
||||
// as it is hard to verify year flags in `NaiveDate::MIN` and `NaiveDate::MAX`,
|
||||
// we use a separate run-time test.
|
||||
#[test]
|
||||
fn test_date_bounds() {
|
||||
let calculated_min = NaiveDate::from_ymd_opt(MIN_YEAR, 1, 1).unwrap();
|
||||
let calculated_max = NaiveDate::from_ymd_opt(MAX_YEAR, 12, 31).unwrap();
|
||||
assert!(
|
||||
NaiveDate::MIN == calculated_min,
|
||||
"`NaiveDate::MIN` should have year flag {:?}",
|
||||
calculated_min.year_flags()
|
||||
);
|
||||
assert!(
|
||||
NaiveDate::MAX == calculated_max,
|
||||
"`NaiveDate::MAX` should have year flag {:?} and ordinal {}",
|
||||
calculated_max.year_flags(),
|
||||
calculated_max.ordinal()
|
||||
);
|
||||
|
||||
// let's also check that the entire range do not exceed 2^44 seconds
|
||||
// (sometimes used for bounding `TimeDelta` against overflow)
|
||||
let maxsecs = NaiveDate::MAX.signed_duration_since(NaiveDate::MIN).num_seconds();
|
||||
let maxsecs = maxsecs + 86401; // also take care of DateTime
|
||||
assert!(
|
||||
maxsecs < (1 << MAX_BITS),
|
||||
"The entire `NaiveDate` range somehow exceeds 2^{} seconds",
|
||||
MAX_BITS
|
||||
);
|
||||
|
||||
const BEFORE_MIN: NaiveDate = NaiveDate::BEFORE_MIN;
|
||||
assert_eq!(BEFORE_MIN.year_flags(), YearFlags::from_year(BEFORE_MIN.year()));
|
||||
assert_eq!((BEFORE_MIN.month(), BEFORE_MIN.day()), (12, 31));
|
||||
|
||||
const AFTER_MAX: NaiveDate = NaiveDate::AFTER_MAX;
|
||||
assert_eq!(AFTER_MAX.year_flags(), YearFlags::from_year(AFTER_MAX.year()));
|
||||
assert_eq!((AFTER_MAX.month(), AFTER_MAX.day()), (1, 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_months() {
|
||||
// identity
|
||||
assert_eq!(
|
||||
NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_add_months(Months::new(0)),
|
||||
Some(NaiveDate::from_ymd_opt(2022, 8, 3).unwrap())
|
||||
);
|
||||
|
||||
// add with months exceeding `i32::MAX`
|
||||
assert_eq!(
|
||||
NaiveDate::from_ymd_opt(2022, 8, 3)
|
||||
.unwrap()
|
||||
.checked_add_months(Months::new(i32::MAX as u32 + 1)),
|
||||
None
|
||||
);
|
||||
|
||||
// sub with months exceeding `i32::MIN`
|
||||
assert_eq!(
|
||||
NaiveDate::from_ymd_opt(2022, 8, 3)
|
||||
.unwrap()
|
||||
.checked_sub_months(Months::new(i32::MIN.unsigned_abs() + 1)),
|
||||
None
|
||||
);
|
||||
|
||||
// add overflowing year
|
||||
assert_eq!(NaiveDate::MAX.checked_add_months(Months::new(1)), None);
|
||||
|
||||
// add underflowing year
|
||||
assert_eq!(NaiveDate::MIN.checked_sub_months(Months::new(1)), None);
|
||||
|
||||
// sub crossing year 0 boundary
|
||||
assert_eq!(
|
||||
NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_sub_months(Months::new(2050 * 12)),
|
||||
Some(NaiveDate::from_ymd_opt(-28, 8, 3).unwrap())
|
||||
);
|
||||
|
||||
// add crossing year boundary
|
||||
assert_eq!(
|
||||
NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_add_months(Months::new(6)),
|
||||
Some(NaiveDate::from_ymd_opt(2023, 2, 3).unwrap())
|
||||
);
|
||||
|
||||
// sub crossing year boundary
|
||||
assert_eq!(
|
||||
NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_sub_months(Months::new(10)),
|
||||
Some(NaiveDate::from_ymd_opt(2021, 10, 3).unwrap())
|
||||
);
|
||||
|
||||
// add clamping day, non-leap year
|
||||
assert_eq!(
|
||||
NaiveDate::from_ymd_opt(2022, 1, 29).unwrap().checked_add_months(Months::new(1)),
|
||||
Some(NaiveDate::from_ymd_opt(2022, 2, 28).unwrap())
|
||||
);
|
||||
|
||||
// add to leap day
|
||||
assert_eq!(
|
||||
NaiveDate::from_ymd_opt(2022, 10, 29).unwrap().checked_add_months(Months::new(16)),
|
||||
Some(NaiveDate::from_ymd_opt(2024, 2, 29).unwrap())
|
||||
);
|
||||
|
||||
// add into december
|
||||
assert_eq!(
|
||||
NaiveDate::from_ymd_opt(2022, 10, 31).unwrap().checked_add_months(Months::new(2)),
|
||||
Some(NaiveDate::from_ymd_opt(2022, 12, 31).unwrap())
|
||||
);
|
||||
|
||||
// sub into december
|
||||
assert_eq!(
|
||||
NaiveDate::from_ymd_opt(2022, 10, 31).unwrap().checked_sub_months(Months::new(10)),
|
||||
Some(NaiveDate::from_ymd_opt(2021, 12, 31).unwrap())
|
||||
);
|
||||
|
||||
// add into january
|
||||
assert_eq!(
|
||||
NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_add_months(Months::new(5)),
|
||||
Some(NaiveDate::from_ymd_opt(2023, 1, 3).unwrap())
|
||||
);
|
||||
|
||||
// sub into january
|
||||
assert_eq!(
|
||||
NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_sub_months(Months::new(7)),
|
||||
Some(NaiveDate::from_ymd_opt(2022, 1, 3).unwrap())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_readme_doomsday() {
|
||||
for y in NaiveDate::MIN.year()..=NaiveDate::MAX.year() {
|
||||
// even months
|
||||
let d4 = NaiveDate::from_ymd_opt(y, 4, 4).unwrap();
|
||||
let d6 = NaiveDate::from_ymd_opt(y, 6, 6).unwrap();
|
||||
let d8 = NaiveDate::from_ymd_opt(y, 8, 8).unwrap();
|
||||
let d10 = NaiveDate::from_ymd_opt(y, 10, 10).unwrap();
|
||||
let d12 = NaiveDate::from_ymd_opt(y, 12, 12).unwrap();
|
||||
|
||||
// nine to five, seven-eleven
|
||||
let d59 = NaiveDate::from_ymd_opt(y, 5, 9).unwrap();
|
||||
let d95 = NaiveDate::from_ymd_opt(y, 9, 5).unwrap();
|
||||
let d711 = NaiveDate::from_ymd_opt(y, 7, 11).unwrap();
|
||||
let d117 = NaiveDate::from_ymd_opt(y, 11, 7).unwrap();
|
||||
|
||||
// "March 0"
|
||||
let d30 = NaiveDate::from_ymd_opt(y, 3, 1).unwrap().pred_opt().unwrap();
|
||||
|
||||
let weekday = d30.weekday();
|
||||
let other_dates = [d4, d6, d8, d10, d12, d59, d95, d711, d117];
|
||||
assert!(other_dates.iter().all(|d| d.weekday() == weekday));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_from_ymd() {
|
||||
let from_ymd = NaiveDate::from_ymd_opt;
|
||||
|
||||
assert!(from_ymd(2012, 0, 1).is_none());
|
||||
assert!(from_ymd(2012, 1, 1).is_some());
|
||||
assert!(from_ymd(2012, 2, 29).is_some());
|
||||
assert!(from_ymd(2014, 2, 29).is_none());
|
||||
assert!(from_ymd(2014, 3, 0).is_none());
|
||||
assert!(from_ymd(2014, 3, 1).is_some());
|
||||
assert!(from_ymd(2014, 3, 31).is_some());
|
||||
assert!(from_ymd(2014, 3, 32).is_none());
|
||||
assert!(from_ymd(2014, 12, 31).is_some());
|
||||
assert!(from_ymd(2014, 13, 1).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_from_yo() {
|
||||
let from_yo = NaiveDate::from_yo_opt;
|
||||
let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap();
|
||||
|
||||
assert_eq!(from_yo(2012, 0), None);
|
||||
assert_eq!(from_yo(2012, 1), Some(ymd(2012, 1, 1)));
|
||||
assert_eq!(from_yo(2012, 2), Some(ymd(2012, 1, 2)));
|
||||
assert_eq!(from_yo(2012, 32), Some(ymd(2012, 2, 1)));
|
||||
assert_eq!(from_yo(2012, 60), Some(ymd(2012, 2, 29)));
|
||||
assert_eq!(from_yo(2012, 61), Some(ymd(2012, 3, 1)));
|
||||
assert_eq!(from_yo(2012, 100), Some(ymd(2012, 4, 9)));
|
||||
assert_eq!(from_yo(2012, 200), Some(ymd(2012, 7, 18)));
|
||||
assert_eq!(from_yo(2012, 300), Some(ymd(2012, 10, 26)));
|
||||
assert_eq!(from_yo(2012, 366), Some(ymd(2012, 12, 31)));
|
||||
assert_eq!(from_yo(2012, 367), None);
|
||||
assert_eq!(from_yo(2012, (1 << 28) | 60), None);
|
||||
|
||||
assert_eq!(from_yo(2014, 0), None);
|
||||
assert_eq!(from_yo(2014, 1), Some(ymd(2014, 1, 1)));
|
||||
assert_eq!(from_yo(2014, 2), Some(ymd(2014, 1, 2)));
|
||||
assert_eq!(from_yo(2014, 32), Some(ymd(2014, 2, 1)));
|
||||
assert_eq!(from_yo(2014, 59), Some(ymd(2014, 2, 28)));
|
||||
assert_eq!(from_yo(2014, 60), Some(ymd(2014, 3, 1)));
|
||||
assert_eq!(from_yo(2014, 100), Some(ymd(2014, 4, 10)));
|
||||
assert_eq!(from_yo(2014, 200), Some(ymd(2014, 7, 19)));
|
||||
assert_eq!(from_yo(2014, 300), Some(ymd(2014, 10, 27)));
|
||||
assert_eq!(from_yo(2014, 365), Some(ymd(2014, 12, 31)));
|
||||
assert_eq!(from_yo(2014, 366), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_from_isoywd() {
|
||||
let from_isoywd = NaiveDate::from_isoywd_opt;
|
||||
let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap();
|
||||
|
||||
assert_eq!(from_isoywd(2004, 0, Weekday::Sun), None);
|
||||
assert_eq!(from_isoywd(2004, 1, Weekday::Mon), Some(ymd(2003, 12, 29)));
|
||||
assert_eq!(from_isoywd(2004, 1, Weekday::Sun), Some(ymd(2004, 1, 4)));
|
||||
assert_eq!(from_isoywd(2004, 2, Weekday::Mon), Some(ymd(2004, 1, 5)));
|
||||
assert_eq!(from_isoywd(2004, 2, Weekday::Sun), Some(ymd(2004, 1, 11)));
|
||||
assert_eq!(from_isoywd(2004, 52, Weekday::Mon), Some(ymd(2004, 12, 20)));
|
||||
assert_eq!(from_isoywd(2004, 52, Weekday::Sun), Some(ymd(2004, 12, 26)));
|
||||
assert_eq!(from_isoywd(2004, 53, Weekday::Mon), Some(ymd(2004, 12, 27)));
|
||||
assert_eq!(from_isoywd(2004, 53, Weekday::Sun), Some(ymd(2005, 1, 2)));
|
||||
assert_eq!(from_isoywd(2004, 54, Weekday::Mon), None);
|
||||
|
||||
assert_eq!(from_isoywd(2011, 0, Weekday::Sun), None);
|
||||
assert_eq!(from_isoywd(2011, 1, Weekday::Mon), Some(ymd(2011, 1, 3)));
|
||||
assert_eq!(from_isoywd(2011, 1, Weekday::Sun), Some(ymd(2011, 1, 9)));
|
||||
assert_eq!(from_isoywd(2011, 2, Weekday::Mon), Some(ymd(2011, 1, 10)));
|
||||
assert_eq!(from_isoywd(2011, 2, Weekday::Sun), Some(ymd(2011, 1, 16)));
|
||||
|
||||
assert_eq!(from_isoywd(2018, 51, Weekday::Mon), Some(ymd(2018, 12, 17)));
|
||||
assert_eq!(from_isoywd(2018, 51, Weekday::Sun), Some(ymd(2018, 12, 23)));
|
||||
assert_eq!(from_isoywd(2018, 52, Weekday::Mon), Some(ymd(2018, 12, 24)));
|
||||
assert_eq!(from_isoywd(2018, 52, Weekday::Sun), Some(ymd(2018, 12, 30)));
|
||||
assert_eq!(from_isoywd(2018, 53, Weekday::Mon), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_from_isoywd_and_iso_week() {
|
||||
for year in 2000..2401 {
|
||||
for week in 1..54 {
|
||||
for &weekday in [
|
||||
Weekday::Mon,
|
||||
Weekday::Tue,
|
||||
Weekday::Wed,
|
||||
Weekday::Thu,
|
||||
Weekday::Fri,
|
||||
Weekday::Sat,
|
||||
Weekday::Sun,
|
||||
]
|
||||
.iter()
|
||||
{
|
||||
let d = NaiveDate::from_isoywd_opt(year, week, weekday);
|
||||
if let Some(d) = d {
|
||||
assert_eq!(d.weekday(), weekday);
|
||||
let w = d.iso_week();
|
||||
assert_eq!(w.year(), year);
|
||||
assert_eq!(w.week(), week);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for year in 2000..2401 {
|
||||
for month in 1..13 {
|
||||
for day in 1..32 {
|
||||
let d = NaiveDate::from_ymd_opt(year, month, day);
|
||||
if let Some(d) = d {
|
||||
let w = d.iso_week();
|
||||
let d_ = NaiveDate::from_isoywd_opt(w.year(), w.week(), d.weekday());
|
||||
assert_eq!(d, d_.unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_from_num_days_from_ce() {
|
||||
let from_ndays_from_ce = NaiveDate::from_num_days_from_ce_opt;
|
||||
assert_eq!(from_ndays_from_ce(1), Some(NaiveDate::from_ymd_opt(1, 1, 1).unwrap()));
|
||||
assert_eq!(from_ndays_from_ce(2), Some(NaiveDate::from_ymd_opt(1, 1, 2).unwrap()));
|
||||
assert_eq!(from_ndays_from_ce(31), Some(NaiveDate::from_ymd_opt(1, 1, 31).unwrap()));
|
||||
assert_eq!(from_ndays_from_ce(32), Some(NaiveDate::from_ymd_opt(1, 2, 1).unwrap()));
|
||||
assert_eq!(from_ndays_from_ce(59), Some(NaiveDate::from_ymd_opt(1, 2, 28).unwrap()));
|
||||
assert_eq!(from_ndays_from_ce(60), Some(NaiveDate::from_ymd_opt(1, 3, 1).unwrap()));
|
||||
assert_eq!(from_ndays_from_ce(365), Some(NaiveDate::from_ymd_opt(1, 12, 31).unwrap()));
|
||||
assert_eq!(from_ndays_from_ce(365 + 1), Some(NaiveDate::from_ymd_opt(2, 1, 1).unwrap()));
|
||||
assert_eq!(from_ndays_from_ce(365 * 2 + 1), Some(NaiveDate::from_ymd_opt(3, 1, 1).unwrap()));
|
||||
assert_eq!(from_ndays_from_ce(365 * 3 + 1), Some(NaiveDate::from_ymd_opt(4, 1, 1).unwrap()));
|
||||
assert_eq!(from_ndays_from_ce(365 * 4 + 2), Some(NaiveDate::from_ymd_opt(5, 1, 1).unwrap()));
|
||||
assert_eq!(from_ndays_from_ce(146097 + 1), Some(NaiveDate::from_ymd_opt(401, 1, 1).unwrap()));
|
||||
assert_eq!(
|
||||
from_ndays_from_ce(146097 * 5 + 1),
|
||||
Some(NaiveDate::from_ymd_opt(2001, 1, 1).unwrap())
|
||||
);
|
||||
assert_eq!(from_ndays_from_ce(719163), Some(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()));
|
||||
assert_eq!(from_ndays_from_ce(0), Some(NaiveDate::from_ymd_opt(0, 12, 31).unwrap())); // 1 BCE
|
||||
assert_eq!(from_ndays_from_ce(-365), Some(NaiveDate::from_ymd_opt(0, 1, 1).unwrap()));
|
||||
assert_eq!(from_ndays_from_ce(-366), Some(NaiveDate::from_ymd_opt(-1, 12, 31).unwrap())); // 2 BCE
|
||||
|
||||
for days in (-9999..10001).map(|x| x * 100) {
|
||||
assert_eq!(from_ndays_from_ce(days).map(|d| d.num_days_from_ce()), Some(days));
|
||||
}
|
||||
|
||||
assert_eq!(from_ndays_from_ce(NaiveDate::MIN.num_days_from_ce()), Some(NaiveDate::MIN));
|
||||
assert_eq!(from_ndays_from_ce(NaiveDate::MIN.num_days_from_ce() - 1), None);
|
||||
assert_eq!(from_ndays_from_ce(NaiveDate::MAX.num_days_from_ce()), Some(NaiveDate::MAX));
|
||||
assert_eq!(from_ndays_from_ce(NaiveDate::MAX.num_days_from_ce() + 1), None);
|
||||
|
||||
assert_eq!(from_ndays_from_ce(i32::MIN), None);
|
||||
assert_eq!(from_ndays_from_ce(i32::MAX), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_from_weekday_of_month_opt() {
|
||||
let ymwd = NaiveDate::from_weekday_of_month_opt;
|
||||
assert_eq!(ymwd(2018, 8, Weekday::Tue, 0), None);
|
||||
assert_eq!(ymwd(2018, 8, Weekday::Wed, 1), Some(NaiveDate::from_ymd_opt(2018, 8, 1).unwrap()));
|
||||
assert_eq!(ymwd(2018, 8, Weekday::Thu, 1), Some(NaiveDate::from_ymd_opt(2018, 8, 2).unwrap()));
|
||||
assert_eq!(ymwd(2018, 8, Weekday::Sun, 1), Some(NaiveDate::from_ymd_opt(2018, 8, 5).unwrap()));
|
||||
assert_eq!(ymwd(2018, 8, Weekday::Mon, 1), Some(NaiveDate::from_ymd_opt(2018, 8, 6).unwrap()));
|
||||
assert_eq!(ymwd(2018, 8, Weekday::Tue, 1), Some(NaiveDate::from_ymd_opt(2018, 8, 7).unwrap()));
|
||||
assert_eq!(ymwd(2018, 8, Weekday::Wed, 2), Some(NaiveDate::from_ymd_opt(2018, 8, 8).unwrap()));
|
||||
assert_eq!(ymwd(2018, 8, Weekday::Sun, 2), Some(NaiveDate::from_ymd_opt(2018, 8, 12).unwrap()));
|
||||
assert_eq!(ymwd(2018, 8, Weekday::Thu, 3), Some(NaiveDate::from_ymd_opt(2018, 8, 16).unwrap()));
|
||||
assert_eq!(ymwd(2018, 8, Weekday::Thu, 4), Some(NaiveDate::from_ymd_opt(2018, 8, 23).unwrap()));
|
||||
assert_eq!(ymwd(2018, 8, Weekday::Thu, 5), Some(NaiveDate::from_ymd_opt(2018, 8, 30).unwrap()));
|
||||
assert_eq!(ymwd(2018, 8, Weekday::Fri, 5), Some(NaiveDate::from_ymd_opt(2018, 8, 31).unwrap()));
|
||||
assert_eq!(ymwd(2018, 8, Weekday::Sat, 5), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_fields() {
|
||||
fn check(year: i32, month: u32, day: u32, ordinal: u32) {
|
||||
let d1 = NaiveDate::from_ymd_opt(year, month, day).unwrap();
|
||||
assert_eq!(d1.year(), year);
|
||||
assert_eq!(d1.month(), month);
|
||||
assert_eq!(d1.day(), day);
|
||||
assert_eq!(d1.ordinal(), ordinal);
|
||||
|
||||
let d2 = NaiveDate::from_yo_opt(year, ordinal).unwrap();
|
||||
assert_eq!(d2.year(), year);
|
||||
assert_eq!(d2.month(), month);
|
||||
assert_eq!(d2.day(), day);
|
||||
assert_eq!(d2.ordinal(), ordinal);
|
||||
|
||||
assert_eq!(d1, d2);
|
||||
}
|
||||
|
||||
check(2012, 1, 1, 1);
|
||||
check(2012, 1, 2, 2);
|
||||
check(2012, 2, 1, 32);
|
||||
check(2012, 2, 29, 60);
|
||||
check(2012, 3, 1, 61);
|
||||
check(2012, 4, 9, 100);
|
||||
check(2012, 7, 18, 200);
|
||||
check(2012, 10, 26, 300);
|
||||
check(2012, 12, 31, 366);
|
||||
|
||||
check(2014, 1, 1, 1);
|
||||
check(2014, 1, 2, 2);
|
||||
check(2014, 2, 1, 32);
|
||||
check(2014, 2, 28, 59);
|
||||
check(2014, 3, 1, 60);
|
||||
check(2014, 4, 10, 100);
|
||||
check(2014, 7, 19, 200);
|
||||
check(2014, 10, 27, 300);
|
||||
check(2014, 12, 31, 365);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_weekday() {
|
||||
assert_eq!(NaiveDate::from_ymd_opt(1582, 10, 15).unwrap().weekday(), Weekday::Fri);
|
||||
// May 20, 1875 = ISO 8601 reference date
|
||||
assert_eq!(NaiveDate::from_ymd_opt(1875, 5, 20).unwrap().weekday(), Weekday::Thu);
|
||||
assert_eq!(NaiveDate::from_ymd_opt(2000, 1, 1).unwrap().weekday(), Weekday::Sat);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_with_fields() {
|
||||
let d = NaiveDate::from_ymd_opt(2000, 2, 29).unwrap();
|
||||
assert_eq!(d.with_year(-400), Some(NaiveDate::from_ymd_opt(-400, 2, 29).unwrap()));
|
||||
assert_eq!(d.with_year(-100), None);
|
||||
assert_eq!(d.with_year(1600), Some(NaiveDate::from_ymd_opt(1600, 2, 29).unwrap()));
|
||||
assert_eq!(d.with_year(1900), None);
|
||||
assert_eq!(d.with_year(2000), Some(NaiveDate::from_ymd_opt(2000, 2, 29).unwrap()));
|
||||
assert_eq!(d.with_year(2001), None);
|
||||
assert_eq!(d.with_year(2004), Some(NaiveDate::from_ymd_opt(2004, 2, 29).unwrap()));
|
||||
assert_eq!(d.with_year(i32::MAX), None);
|
||||
|
||||
let d = NaiveDate::from_ymd_opt(2000, 4, 30).unwrap();
|
||||
assert_eq!(d.with_month(0), None);
|
||||
assert_eq!(d.with_month(1), Some(NaiveDate::from_ymd_opt(2000, 1, 30).unwrap()));
|
||||
assert_eq!(d.with_month(2), None);
|
||||
assert_eq!(d.with_month(3), Some(NaiveDate::from_ymd_opt(2000, 3, 30).unwrap()));
|
||||
assert_eq!(d.with_month(4), Some(NaiveDate::from_ymd_opt(2000, 4, 30).unwrap()));
|
||||
assert_eq!(d.with_month(12), Some(NaiveDate::from_ymd_opt(2000, 12, 30).unwrap()));
|
||||
assert_eq!(d.with_month(13), None);
|
||||
assert_eq!(d.with_month(u32::MAX), None);
|
||||
|
||||
let d = NaiveDate::from_ymd_opt(2000, 2, 8).unwrap();
|
||||
assert_eq!(d.with_day(0), None);
|
||||
assert_eq!(d.with_day(1), Some(NaiveDate::from_ymd_opt(2000, 2, 1).unwrap()));
|
||||
assert_eq!(d.with_day(29), Some(NaiveDate::from_ymd_opt(2000, 2, 29).unwrap()));
|
||||
assert_eq!(d.with_day(30), None);
|
||||
assert_eq!(d.with_day(u32::MAX), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_with_ordinal() {
|
||||
let d = NaiveDate::from_ymd_opt(2000, 5, 5).unwrap();
|
||||
assert_eq!(d.with_ordinal(0), None);
|
||||
assert_eq!(d.with_ordinal(1), Some(NaiveDate::from_ymd_opt(2000, 1, 1).unwrap()));
|
||||
assert_eq!(d.with_ordinal(60), Some(NaiveDate::from_ymd_opt(2000, 2, 29).unwrap()));
|
||||
assert_eq!(d.with_ordinal(61), Some(NaiveDate::from_ymd_opt(2000, 3, 1).unwrap()));
|
||||
assert_eq!(d.with_ordinal(366), Some(NaiveDate::from_ymd_opt(2000, 12, 31).unwrap()));
|
||||
assert_eq!(d.with_ordinal(367), None);
|
||||
assert_eq!(d.with_ordinal((1 << 28) | 60), None);
|
||||
let d = NaiveDate::from_ymd_opt(1999, 5, 5).unwrap();
|
||||
assert_eq!(d.with_ordinal(366), None);
|
||||
assert_eq!(d.with_ordinal(u32::MAX), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_num_days_from_ce() {
|
||||
assert_eq!(NaiveDate::from_ymd_opt(1, 1, 1).unwrap().num_days_from_ce(), 1);
|
||||
|
||||
for year in -9999..10001 {
|
||||
assert_eq!(
|
||||
NaiveDate::from_ymd_opt(year, 1, 1).unwrap().num_days_from_ce(),
|
||||
NaiveDate::from_ymd_opt(year - 1, 12, 31).unwrap().num_days_from_ce() + 1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_succ() {
|
||||
let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap();
|
||||
assert_eq!(ymd(2014, 5, 6).succ_opt(), Some(ymd(2014, 5, 7)));
|
||||
assert_eq!(ymd(2014, 5, 31).succ_opt(), Some(ymd(2014, 6, 1)));
|
||||
assert_eq!(ymd(2014, 12, 31).succ_opt(), Some(ymd(2015, 1, 1)));
|
||||
assert_eq!(ymd(2016, 2, 28).succ_opt(), Some(ymd(2016, 2, 29)));
|
||||
assert_eq!(ymd(NaiveDate::MAX.year(), 12, 31).succ_opt(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_pred() {
|
||||
let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap();
|
||||
assert_eq!(ymd(2016, 3, 1).pred_opt(), Some(ymd(2016, 2, 29)));
|
||||
assert_eq!(ymd(2015, 1, 1).pred_opt(), Some(ymd(2014, 12, 31)));
|
||||
assert_eq!(ymd(2014, 6, 1).pred_opt(), Some(ymd(2014, 5, 31)));
|
||||
assert_eq!(ymd(2014, 5, 7).pred_opt(), Some(ymd(2014, 5, 6)));
|
||||
assert_eq!(ymd(NaiveDate::MIN.year(), 1, 1).pred_opt(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_checked_add_signed() {
|
||||
fn check(lhs: Option<NaiveDate>, delta: TimeDelta, rhs: Option<NaiveDate>) {
|
||||
assert_eq!(lhs.unwrap().checked_add_signed(delta), rhs);
|
||||
assert_eq!(lhs.unwrap().checked_sub_signed(-delta), rhs);
|
||||
}
|
||||
let ymd = NaiveDate::from_ymd_opt;
|
||||
|
||||
check(ymd(2014, 1, 1), TimeDelta::zero(), ymd(2014, 1, 1));
|
||||
check(ymd(2014, 1, 1), TimeDelta::try_seconds(86399).unwrap(), ymd(2014, 1, 1));
|
||||
// always round towards zero
|
||||
check(ymd(2014, 1, 1), TimeDelta::try_seconds(-86399).unwrap(), ymd(2014, 1, 1));
|
||||
check(ymd(2014, 1, 1), TimeDelta::try_days(1).unwrap(), ymd(2014, 1, 2));
|
||||
check(ymd(2014, 1, 1), TimeDelta::try_days(-1).unwrap(), ymd(2013, 12, 31));
|
||||
check(ymd(2014, 1, 1), TimeDelta::try_days(364).unwrap(), ymd(2014, 12, 31));
|
||||
check(ymd(2014, 1, 1), TimeDelta::try_days(365 * 4 + 1).unwrap(), ymd(2018, 1, 1));
|
||||
check(ymd(2014, 1, 1), TimeDelta::try_days(365 * 400 + 97).unwrap(), ymd(2414, 1, 1));
|
||||
|
||||
check(ymd(-7, 1, 1), TimeDelta::try_days(365 * 12 + 3).unwrap(), ymd(5, 1, 1));
|
||||
|
||||
// overflow check
|
||||
check(
|
||||
ymd(0, 1, 1),
|
||||
TimeDelta::try_days(MAX_DAYS_FROM_YEAR_0 as i64).unwrap(),
|
||||
ymd(MAX_YEAR, 12, 31),
|
||||
);
|
||||
check(ymd(0, 1, 1), TimeDelta::try_days(MAX_DAYS_FROM_YEAR_0 as i64 + 1).unwrap(), None);
|
||||
check(ymd(0, 1, 1), TimeDelta::MAX, None);
|
||||
check(
|
||||
ymd(0, 1, 1),
|
||||
TimeDelta::try_days(MIN_DAYS_FROM_YEAR_0 as i64).unwrap(),
|
||||
ymd(MIN_YEAR, 1, 1),
|
||||
);
|
||||
check(ymd(0, 1, 1), TimeDelta::try_days(MIN_DAYS_FROM_YEAR_0 as i64 - 1).unwrap(), None);
|
||||
check(ymd(0, 1, 1), TimeDelta::MIN, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_signed_duration_since() {
|
||||
fn check(lhs: Option<NaiveDate>, rhs: Option<NaiveDate>, delta: TimeDelta) {
|
||||
assert_eq!(lhs.unwrap().signed_duration_since(rhs.unwrap()), delta);
|
||||
assert_eq!(rhs.unwrap().signed_duration_since(lhs.unwrap()), -delta);
|
||||
}
|
||||
let ymd = NaiveDate::from_ymd_opt;
|
||||
|
||||
check(ymd(2014, 1, 1), ymd(2014, 1, 1), TimeDelta::zero());
|
||||
check(ymd(2014, 1, 2), ymd(2014, 1, 1), TimeDelta::try_days(1).unwrap());
|
||||
check(ymd(2014, 12, 31), ymd(2014, 1, 1), TimeDelta::try_days(364).unwrap());
|
||||
check(ymd(2015, 1, 3), ymd(2014, 1, 1), TimeDelta::try_days(365 + 2).unwrap());
|
||||
check(ymd(2018, 1, 1), ymd(2014, 1, 1), TimeDelta::try_days(365 * 4 + 1).unwrap());
|
||||
check(ymd(2414, 1, 1), ymd(2014, 1, 1), TimeDelta::try_days(365 * 400 + 97).unwrap());
|
||||
|
||||
check(
|
||||
ymd(MAX_YEAR, 12, 31),
|
||||
ymd(0, 1, 1),
|
||||
TimeDelta::try_days(MAX_DAYS_FROM_YEAR_0 as i64).unwrap(),
|
||||
);
|
||||
check(
|
||||
ymd(MIN_YEAR, 1, 1),
|
||||
ymd(0, 1, 1),
|
||||
TimeDelta::try_days(MIN_DAYS_FROM_YEAR_0 as i64).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_add_days() {
|
||||
fn check(lhs: Option<NaiveDate>, days: Days, rhs: Option<NaiveDate>) {
|
||||
assert_eq!(lhs.unwrap().checked_add_days(days), rhs);
|
||||
}
|
||||
let ymd = NaiveDate::from_ymd_opt;
|
||||
|
||||
check(ymd(2014, 1, 1), Days::new(0), ymd(2014, 1, 1));
|
||||
// always round towards zero
|
||||
check(ymd(2014, 1, 1), Days::new(1), ymd(2014, 1, 2));
|
||||
check(ymd(2014, 1, 1), Days::new(364), ymd(2014, 12, 31));
|
||||
check(ymd(2014, 1, 1), Days::new(365 * 4 + 1), ymd(2018, 1, 1));
|
||||
check(ymd(2014, 1, 1), Days::new(365 * 400 + 97), ymd(2414, 1, 1));
|
||||
|
||||
check(ymd(-7, 1, 1), Days::new(365 * 12 + 3), ymd(5, 1, 1));
|
||||
|
||||
// overflow check
|
||||
check(ymd(0, 1, 1), Days::new(MAX_DAYS_FROM_YEAR_0.try_into().unwrap()), ymd(MAX_YEAR, 12, 31));
|
||||
check(ymd(0, 1, 1), Days::new(u64::try_from(MAX_DAYS_FROM_YEAR_0).unwrap() + 1), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_sub_days() {
|
||||
fn check(lhs: Option<NaiveDate>, days: Days, rhs: Option<NaiveDate>) {
|
||||
assert_eq!(lhs.unwrap().checked_sub_days(days), rhs);
|
||||
}
|
||||
let ymd = NaiveDate::from_ymd_opt;
|
||||
|
||||
check(ymd(2014, 1, 1), Days::new(0), ymd(2014, 1, 1));
|
||||
check(ymd(2014, 1, 2), Days::new(1), ymd(2014, 1, 1));
|
||||
check(ymd(2014, 12, 31), Days::new(364), ymd(2014, 1, 1));
|
||||
check(ymd(2015, 1, 3), Days::new(365 + 2), ymd(2014, 1, 1));
|
||||
check(ymd(2018, 1, 1), Days::new(365 * 4 + 1), ymd(2014, 1, 1));
|
||||
check(ymd(2414, 1, 1), Days::new(365 * 400 + 97), ymd(2014, 1, 1));
|
||||
|
||||
check(ymd(MAX_YEAR, 12, 31), Days::new(MAX_DAYS_FROM_YEAR_0.try_into().unwrap()), ymd(0, 1, 1));
|
||||
check(
|
||||
ymd(0, 1, 1),
|
||||
Days::new((-MIN_DAYS_FROM_YEAR_0).try_into().unwrap()),
|
||||
ymd(MIN_YEAR, 1, 1),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_addassignment() {
|
||||
let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap();
|
||||
let mut date = ymd(2016, 10, 1);
|
||||
date += TimeDelta::try_days(10).unwrap();
|
||||
assert_eq!(date, ymd(2016, 10, 11));
|
||||
date += TimeDelta::try_days(30).unwrap();
|
||||
assert_eq!(date, ymd(2016, 11, 10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_subassignment() {
|
||||
let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap();
|
||||
let mut date = ymd(2016, 10, 11);
|
||||
date -= TimeDelta::try_days(10).unwrap();
|
||||
assert_eq!(date, ymd(2016, 10, 1));
|
||||
date -= TimeDelta::try_days(2).unwrap();
|
||||
assert_eq!(date, ymd(2016, 9, 29));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_fmt() {
|
||||
assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(2012, 3, 4).unwrap()), "2012-03-04");
|
||||
assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(0, 3, 4).unwrap()), "0000-03-04");
|
||||
assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(-307, 3, 4).unwrap()), "-0307-03-04");
|
||||
assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(12345, 3, 4).unwrap()), "+12345-03-04");
|
||||
|
||||
assert_eq!(NaiveDate::from_ymd_opt(2012, 3, 4).unwrap().to_string(), "2012-03-04");
|
||||
assert_eq!(NaiveDate::from_ymd_opt(0, 3, 4).unwrap().to_string(), "0000-03-04");
|
||||
assert_eq!(NaiveDate::from_ymd_opt(-307, 3, 4).unwrap().to_string(), "-0307-03-04");
|
||||
assert_eq!(NaiveDate::from_ymd_opt(12345, 3, 4).unwrap().to_string(), "+12345-03-04");
|
||||
|
||||
// the format specifier should have no effect on `NaiveTime`
|
||||
assert_eq!(format!("{:+30?}", NaiveDate::from_ymd_opt(1234, 5, 6).unwrap()), "1234-05-06");
|
||||
assert_eq!(format!("{:30?}", NaiveDate::from_ymd_opt(12345, 6, 7).unwrap()), "+12345-06-07");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_from_str() {
|
||||
// valid cases
|
||||
let valid = [
|
||||
"-0000000123456-1-2",
|
||||
" -123456 - 1 - 2 ",
|
||||
"-12345-1-2",
|
||||
"-1234-12-31",
|
||||
"-7-6-5",
|
||||
"350-2-28",
|
||||
"360-02-29",
|
||||
"0360-02-29",
|
||||
"2015-2 -18",
|
||||
"2015-02-18",
|
||||
"+70-2-18",
|
||||
"+70000-2-18",
|
||||
"+00007-2-18",
|
||||
];
|
||||
for &s in &valid {
|
||||
eprintln!("test_date_from_str valid {:?}", s);
|
||||
let d = match s.parse::<NaiveDate>() {
|
||||
Ok(d) => d,
|
||||
Err(e) => panic!("parsing `{}` has failed: {}", s, e),
|
||||
};
|
||||
eprintln!("d {:?} (NaiveDate)", d);
|
||||
let s_ = format!("{:?}", d);
|
||||
eprintln!("s_ {:?}", s_);
|
||||
// `s` and `s_` may differ, but `s.parse()` and `s_.parse()` must be same
|
||||
let d_ = match s_.parse::<NaiveDate>() {
|
||||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
panic!("`{}` is parsed into `{:?}`, but reparsing that has failed: {}", s, d, e)
|
||||
}
|
||||
};
|
||||
eprintln!("d_ {:?} (NaiveDate)", d_);
|
||||
assert!(
|
||||
d == d_,
|
||||
"`{}` is parsed into `{:?}`, but reparsed result \
|
||||
`{:?}` does not match",
|
||||
s,
|
||||
d,
|
||||
d_
|
||||
);
|
||||
}
|
||||
|
||||
// some invalid cases
|
||||
// since `ParseErrorKind` is private, all we can do is to check if there was an error
|
||||
let invalid = [
|
||||
"", // empty
|
||||
"x", // invalid
|
||||
"Fri, 09 Aug 2013 GMT", // valid date, wrong format
|
||||
"Sat Jun 30 2012", // valid date, wrong format
|
||||
"1441497364.649", // valid datetime, wrong format
|
||||
"+1441497364.649", // valid datetime, wrong format
|
||||
"+1441497364", // valid datetime, wrong format
|
||||
"2014/02/03", // valid date, wrong format
|
||||
"2014", // datetime missing data
|
||||
"2014-01", // datetime missing data
|
||||
"2014-01-00", // invalid day
|
||||
"2014-11-32", // invalid day
|
||||
"2014-13-01", // invalid month
|
||||
"2014-13-57", // invalid month, day
|
||||
"9999999-9-9", // invalid year (out of bounds)
|
||||
];
|
||||
for &s in &invalid {
|
||||
eprintln!("test_date_from_str invalid {:?}", s);
|
||||
assert!(s.parse::<NaiveDate>().is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_parse_from_str() {
|
||||
let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap();
|
||||
assert_eq!(
|
||||
NaiveDate::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"),
|
||||
Ok(ymd(2014, 5, 7))
|
||||
); // ignore time and offset
|
||||
assert_eq!(
|
||||
NaiveDate::parse_from_str("2015-W06-1=2015-033 Q1", "%G-W%V-%u = %Y-%j Q%q"),
|
||||
Ok(ymd(2015, 2, 2))
|
||||
);
|
||||
assert_eq!(NaiveDate::parse_from_str("Fri, 09 Aug 13", "%a, %d %b %y"), Ok(ymd(2013, 8, 9)));
|
||||
assert!(NaiveDate::parse_from_str("Sat, 09 Aug 2013", "%a, %d %b %Y").is_err());
|
||||
assert!(NaiveDate::parse_from_str("2014-57", "%Y-%m-%d").is_err());
|
||||
assert!(NaiveDate::parse_from_str("2014", "%Y").is_err()); // insufficient
|
||||
|
||||
assert!(NaiveDate::parse_from_str("2014-5-7 Q3", "%Y-%m-%d Q%q").is_err()); // mismatched quarter
|
||||
|
||||
assert_eq!(
|
||||
NaiveDate::parse_from_str("2020-01-0", "%Y-%W-%w").ok(),
|
||||
NaiveDate::from_ymd_opt(2020, 1, 12),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
NaiveDate::parse_from_str("2019-01-0", "%Y-%W-%w").ok(),
|
||||
NaiveDate::from_ymd_opt(2019, 1, 13),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_day_iterator_limit() {
|
||||
assert_eq!(NaiveDate::from_ymd_opt(MAX_YEAR, 12, 29).unwrap().iter_days().take(4).count(), 2);
|
||||
assert_eq!(
|
||||
NaiveDate::from_ymd_opt(MIN_YEAR, 1, 3).unwrap().iter_days().rev().take(4).count(),
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_week_iterator_limit() {
|
||||
assert_eq!(NaiveDate::from_ymd_opt(MAX_YEAR, 12, 12).unwrap().iter_weeks().take(4).count(), 2);
|
||||
assert_eq!(
|
||||
NaiveDate::from_ymd_opt(MIN_YEAR, 1, 15).unwrap().iter_weeks().rev().take(4).count(),
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_weeks_from() {
|
||||
// tests per: https://github.com/chronotope/chrono/issues/961
|
||||
// these internally use `weeks_from` via the parsing infrastructure
|
||||
assert_eq!(
|
||||
NaiveDate::parse_from_str("2020-01-0", "%Y-%W-%w").ok(),
|
||||
NaiveDate::from_ymd_opt(2020, 1, 12),
|
||||
);
|
||||
assert_eq!(
|
||||
NaiveDate::parse_from_str("2019-01-0", "%Y-%W-%w").ok(),
|
||||
NaiveDate::from_ymd_opt(2019, 1, 13),
|
||||
);
|
||||
|
||||
// direct tests
|
||||
for (y, starts_on) in &[
|
||||
(2019, Weekday::Tue),
|
||||
(2020, Weekday::Wed),
|
||||
(2021, Weekday::Fri),
|
||||
(2022, Weekday::Sat),
|
||||
(2023, Weekday::Sun),
|
||||
(2024, Weekday::Mon),
|
||||
(2025, Weekday::Wed),
|
||||
(2026, Weekday::Thu),
|
||||
] {
|
||||
for day in &[
|
||||
Weekday::Mon,
|
||||
Weekday::Tue,
|
||||
Weekday::Wed,
|
||||
Weekday::Thu,
|
||||
Weekday::Fri,
|
||||
Weekday::Sat,
|
||||
Weekday::Sun,
|
||||
] {
|
||||
assert_eq!(
|
||||
NaiveDate::from_ymd_opt(*y, 1, 1).map(|d| d.weeks_from(*day)),
|
||||
Some(if day == starts_on { 1 } else { 0 })
|
||||
);
|
||||
|
||||
// last day must always be in week 52 or 53
|
||||
assert!(
|
||||
[52, 53].contains(&NaiveDate::from_ymd_opt(*y, 12, 31).unwrap().weeks_from(*day)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let base = NaiveDate::from_ymd_opt(2019, 1, 1).unwrap();
|
||||
|
||||
// 400 years covers all year types
|
||||
for day in &[
|
||||
Weekday::Mon,
|
||||
Weekday::Tue,
|
||||
Weekday::Wed,
|
||||
Weekday::Thu,
|
||||
Weekday::Fri,
|
||||
Weekday::Sat,
|
||||
Weekday::Sun,
|
||||
] {
|
||||
// must always be below 54
|
||||
for dplus in 1..(400 * 366) {
|
||||
assert!((base + Days::new(dplus)).weeks_from(*day) < 54)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_0_overflow() {
|
||||
let dt = NaiveDate::from_ymd_opt(2023, 4, 18).unwrap();
|
||||
assert!(dt.with_month0(4294967295).is_none());
|
||||
assert!(dt.with_day0(4294967295).is_none());
|
||||
assert!(dt.with_ordinal0(4294967295).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_leap_year() {
|
||||
for year in 0..=MAX_YEAR {
|
||||
let date = NaiveDate::from_ymd_opt(year, 1, 1).unwrap();
|
||||
let is_leap = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
|
||||
assert_eq!(date.leap_year(), is_leap);
|
||||
assert_eq!(date.leap_year(), date.with_ordinal(366).is_some());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_yearflags() {
|
||||
for (year, year_flags, _) in YEAR_FLAGS {
|
||||
assert_eq!(NaiveDate::from_yo_opt(year, 1).unwrap().year_flags(), year_flags);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_weekday_with_yearflags() {
|
||||
for (year, year_flags, first_weekday) in YEAR_FLAGS {
|
||||
let first_day_of_year = NaiveDate::from_yo_opt(year, 1).unwrap();
|
||||
dbg!(year);
|
||||
assert_eq!(first_day_of_year.year_flags(), year_flags);
|
||||
assert_eq!(first_day_of_year.weekday(), first_weekday);
|
||||
|
||||
let mut prev = first_day_of_year.weekday();
|
||||
for ordinal in 2u32..=year_flags.ndays() {
|
||||
let date = NaiveDate::from_yo_opt(year, ordinal).unwrap();
|
||||
let expected = prev.succ();
|
||||
assert_eq!(date.weekday(), expected);
|
||||
prev = expected;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_isoweekdate_with_yearflags() {
|
||||
for (year, year_flags, _) in YEAR_FLAGS {
|
||||
// January 4 should be in the first week
|
||||
let jan4 = NaiveDate::from_ymd_opt(year, 1, 4).unwrap();
|
||||
let iso_week = jan4.iso_week();
|
||||
assert_eq!(jan4.year_flags(), year_flags);
|
||||
assert_eq!(iso_week.week(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_to_mdf_to_date() {
|
||||
for (year, year_flags, _) in YEAR_FLAGS {
|
||||
for ordinal in 1..=year_flags.ndays() {
|
||||
let date = NaiveDate::from_yo_opt(year, ordinal).unwrap();
|
||||
assert_eq!(date, NaiveDate::from_mdf(date.year(), date.mdf()).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Used for testing some methods with all combinations of `YearFlags`.
|
||||
// (year, flags, first weekday of year)
|
||||
const YEAR_FLAGS: [(i32, YearFlags, Weekday); 14] = [
|
||||
(2006, A, Weekday::Sun),
|
||||
(2005, B, Weekday::Sat),
|
||||
(2010, C, Weekday::Fri),
|
||||
(2009, D, Weekday::Thu),
|
||||
(2003, E, Weekday::Wed),
|
||||
(2002, F, Weekday::Tue),
|
||||
(2001, G, Weekday::Mon),
|
||||
(2012, AG, Weekday::Sun),
|
||||
(2000, BA, Weekday::Sat),
|
||||
(2016, CB, Weekday::Fri),
|
||||
(2004, DC, Weekday::Thu),
|
||||
(2020, ED, Weekday::Wed),
|
||||
(2008, FE, Weekday::Tue),
|
||||
(2024, GF, Weekday::Mon),
|
||||
];
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "rkyv-validation")]
|
||||
fn test_rkyv_validation() {
|
||||
let date_min = NaiveDate::MIN;
|
||||
let bytes = rkyv::to_bytes::<_, 4>(&date_min).unwrap();
|
||||
assert_eq!(rkyv::from_bytes::<NaiveDate>(&bytes).unwrap(), date_min);
|
||||
|
||||
let date_max = NaiveDate::MAX;
|
||||
let bytes = rkyv::to_bytes::<_, 4>(&date_max).unwrap();
|
||||
assert_eq!(rkyv::from_bytes::<NaiveDate>(&bytes).unwrap(), date_max);
|
||||
}
|
||||
|
||||
// MAX_YEAR-12-31 minus 0000-01-01
|
||||
// = (MAX_YEAR-12-31 minus 0000-12-31) + (0000-12-31 - 0000-01-01)
|
||||
// = MAX_YEAR * 365 + (# of leap years from 0001 to MAX_YEAR) + 365
|
||||
// = (MAX_YEAR + 1) * 365 + (# of leap years from 0001 to MAX_YEAR)
|
||||
const MAX_DAYS_FROM_YEAR_0: i32 =
|
||||
(MAX_YEAR + 1) * 365 + MAX_YEAR / 4 - MAX_YEAR / 100 + MAX_YEAR / 400;
|
||||
|
||||
// MIN_YEAR-01-01 minus 0000-01-01
|
||||
// = MIN_YEAR * 365 + (# of leap years from MIN_YEAR to 0000)
|
||||
const MIN_DAYS_FROM_YEAR_0: i32 = MIN_YEAR * 365 + MIN_YEAR / 4 - MIN_YEAR / 100 + MIN_YEAR / 400;
|
||||
|
||||
// only used for testing, but duplicated in naive::datetime
|
||||
const MAX_BITS: usize = 44;
|
||||
2507
third_party/rust/chrono/src/naive/datetime.rs
vendored
2507
third_party/rust/chrono/src/naive/datetime.rs
vendored
File diff suppressed because it is too large
Load Diff
2151
third_party/rust/chrono/src/naive/datetime/mod.rs
vendored
Normal file
2151
third_party/rust/chrono/src/naive/datetime/mod.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1304
third_party/rust/chrono/src/naive/datetime/serde.rs
vendored
Normal file
1304
third_party/rust/chrono/src/naive/datetime/serde.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
408
third_party/rust/chrono/src/naive/datetime/tests.rs
vendored
Normal file
408
third_party/rust/chrono/src/naive/datetime/tests.rs
vendored
Normal file
@@ -0,0 +1,408 @@
|
||||
use super::NaiveDateTime;
|
||||
use crate::{Datelike, FixedOffset, MappedLocalTime, NaiveDate, TimeDelta, Utc};
|
||||
|
||||
#[test]
|
||||
fn test_datetime_add() {
|
||||
fn check(
|
||||
(y, m, d, h, n, s): (i32, u32, u32, u32, u32, u32),
|
||||
rhs: TimeDelta,
|
||||
result: Option<(i32, u32, u32, u32, u32, u32)>,
|
||||
) {
|
||||
let lhs = NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap();
|
||||
let sum = result.map(|(y, m, d, h, n, s)| {
|
||||
NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap()
|
||||
});
|
||||
assert_eq!(lhs.checked_add_signed(rhs), sum);
|
||||
assert_eq!(lhs.checked_sub_signed(-rhs), sum);
|
||||
}
|
||||
let seconds = |s| TimeDelta::try_seconds(s).unwrap();
|
||||
|
||||
check((2014, 5, 6, 7, 8, 9), seconds(3600 + 60 + 1), Some((2014, 5, 6, 8, 9, 10)));
|
||||
check((2014, 5, 6, 7, 8, 9), seconds(-(3600 + 60 + 1)), Some((2014, 5, 6, 6, 7, 8)));
|
||||
check((2014, 5, 6, 7, 8, 9), seconds(86399), Some((2014, 5, 7, 7, 8, 8)));
|
||||
check((2014, 5, 6, 7, 8, 9), seconds(86_400 * 10), Some((2014, 5, 16, 7, 8, 9)));
|
||||
check((2014, 5, 6, 7, 8, 9), seconds(-86_400 * 10), Some((2014, 4, 26, 7, 8, 9)));
|
||||
check((2014, 5, 6, 7, 8, 9), seconds(86_400 * 10), Some((2014, 5, 16, 7, 8, 9)));
|
||||
|
||||
// overflow check
|
||||
// assumes that we have correct values for MAX/MIN_DAYS_FROM_YEAR_0 from `naive::date`.
|
||||
// (they are private constants, but the equivalence is tested in that module.)
|
||||
let max_days_from_year_0 =
|
||||
NaiveDate::MAX.signed_duration_since(NaiveDate::from_ymd_opt(0, 1, 1).unwrap());
|
||||
check((0, 1, 1, 0, 0, 0), max_days_from_year_0, Some((NaiveDate::MAX.year(), 12, 31, 0, 0, 0)));
|
||||
check(
|
||||
(0, 1, 1, 0, 0, 0),
|
||||
max_days_from_year_0 + seconds(86399),
|
||||
Some((NaiveDate::MAX.year(), 12, 31, 23, 59, 59)),
|
||||
);
|
||||
check((0, 1, 1, 0, 0, 0), max_days_from_year_0 + seconds(86_400), None);
|
||||
check((0, 1, 1, 0, 0, 0), TimeDelta::MAX, None);
|
||||
|
||||
let min_days_from_year_0 =
|
||||
NaiveDate::MIN.signed_duration_since(NaiveDate::from_ymd_opt(0, 1, 1).unwrap());
|
||||
check((0, 1, 1, 0, 0, 0), min_days_from_year_0, Some((NaiveDate::MIN.year(), 1, 1, 0, 0, 0)));
|
||||
check((0, 1, 1, 0, 0, 0), min_days_from_year_0 - seconds(1), None);
|
||||
check((0, 1, 1, 0, 0, 0), TimeDelta::MIN, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_datetime_sub() {
|
||||
let ymdhms =
|
||||
|y, m, d, h, n, s| NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap();
|
||||
let since = NaiveDateTime::signed_duration_since;
|
||||
assert_eq!(since(ymdhms(2014, 5, 6, 7, 8, 9), ymdhms(2014, 5, 6, 7, 8, 9)), TimeDelta::zero());
|
||||
assert_eq!(
|
||||
since(ymdhms(2014, 5, 6, 7, 8, 10), ymdhms(2014, 5, 6, 7, 8, 9)),
|
||||
TimeDelta::try_seconds(1).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
since(ymdhms(2014, 5, 6, 7, 8, 9), ymdhms(2014, 5, 6, 7, 8, 10)),
|
||||
TimeDelta::try_seconds(-1).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
since(ymdhms(2014, 5, 7, 7, 8, 9), ymdhms(2014, 5, 6, 7, 8, 10)),
|
||||
TimeDelta::try_seconds(86399).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
since(ymdhms(2001, 9, 9, 1, 46, 39), ymdhms(1970, 1, 1, 0, 0, 0)),
|
||||
TimeDelta::try_seconds(999_999_999).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_datetime_addassignment() {
|
||||
let ymdhms =
|
||||
|y, m, d, h, n, s| NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap();
|
||||
let mut date = ymdhms(2016, 10, 1, 10, 10, 10);
|
||||
date += TimeDelta::try_minutes(10_000_000).unwrap();
|
||||
assert_eq!(date, ymdhms(2035, 10, 6, 20, 50, 10));
|
||||
date += TimeDelta::try_days(10).unwrap();
|
||||
assert_eq!(date, ymdhms(2035, 10, 16, 20, 50, 10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_datetime_subassignment() {
|
||||
let ymdhms =
|
||||
|y, m, d, h, n, s| NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap();
|
||||
let mut date = ymdhms(2016, 10, 1, 10, 10, 10);
|
||||
date -= TimeDelta::try_minutes(10_000_000).unwrap();
|
||||
assert_eq!(date, ymdhms(1997, 9, 26, 23, 30, 10));
|
||||
date -= TimeDelta::try_days(10).unwrap();
|
||||
assert_eq!(date, ymdhms(1997, 9, 16, 23, 30, 10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_core_duration_ops() {
|
||||
use core::time::Duration;
|
||||
|
||||
let mut dt = NaiveDate::from_ymd_opt(2023, 8, 29).unwrap().and_hms_opt(11, 34, 12).unwrap();
|
||||
let same = dt + Duration::ZERO;
|
||||
assert_eq!(dt, same);
|
||||
|
||||
dt += Duration::new(3600, 0);
|
||||
assert_eq!(dt, NaiveDate::from_ymd_opt(2023, 8, 29).unwrap().and_hms_opt(12, 34, 12).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_core_duration_max() {
|
||||
use core::time::Duration;
|
||||
|
||||
let mut utc_dt = NaiveDate::from_ymd_opt(2023, 8, 29).unwrap().and_hms_opt(11, 34, 12).unwrap();
|
||||
utc_dt += Duration::MAX;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_datetime_from_str() {
|
||||
// valid cases
|
||||
let valid = [
|
||||
"2001-02-03T04:05:06",
|
||||
"2012-12-12T12:12:12",
|
||||
"2015-02-18T23:16:09.153",
|
||||
"2015-2-18T23:16:09.153",
|
||||
"-77-02-18T23:16:09",
|
||||
"+82701-05-6T15:9:60.898989898989",
|
||||
" +82701 - 05 - 6 T 15 : 9 : 60.898989898989 ",
|
||||
];
|
||||
for &s in &valid {
|
||||
eprintln!("test_parse_naivedatetime valid {:?}", s);
|
||||
let d = match s.parse::<NaiveDateTime>() {
|
||||
Ok(d) => d,
|
||||
Err(e) => panic!("parsing `{}` has failed: {}", s, e),
|
||||
};
|
||||
let s_ = format!("{:?}", d);
|
||||
// `s` and `s_` may differ, but `s.parse()` and `s_.parse()` must be same
|
||||
let d_ = match s_.parse::<NaiveDateTime>() {
|
||||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
panic!("`{}` is parsed into `{:?}`, but reparsing that has failed: {}", s, d, e)
|
||||
}
|
||||
};
|
||||
assert!(
|
||||
d == d_,
|
||||
"`{}` is parsed into `{:?}`, but reparsed result \
|
||||
`{:?}` does not match",
|
||||
s,
|
||||
d,
|
||||
d_
|
||||
);
|
||||
}
|
||||
|
||||
// some invalid cases
|
||||
// since `ParseErrorKind` is private, all we can do is to check if there was an error
|
||||
let invalid = [
|
||||
"", // empty
|
||||
"x", // invalid / missing data
|
||||
"15", // missing data
|
||||
"15:8:9", // looks like a time (invalid date)
|
||||
"15-8-9", // looks like a date (invalid)
|
||||
"Fri, 09 Aug 2013 23:54:35 GMT", // valid date, wrong format
|
||||
"Sat Jun 30 23:59:60 2012", // valid date, wrong format
|
||||
"1441497364.649", // valid date, wrong format
|
||||
"+1441497364.649", // valid date, wrong format
|
||||
"+1441497364", // valid date, wrong format
|
||||
"2014/02/03 04:05:06", // valid date, wrong format
|
||||
"2015-15-15T15:15:15", // invalid date
|
||||
"2012-12-12T12:12:12x", // bad timezone / trailing literal
|
||||
"2012-12-12T12:12:12+00:00", // unexpected timezone / trailing literal
|
||||
"2012-12-12T12:12:12 +00:00", // unexpected timezone / trailing literal
|
||||
"2012-12-12T12:12:12 GMT", // unexpected timezone / trailing literal
|
||||
"2012-123-12T12:12:12", // invalid month
|
||||
"2012-12-12t12:12:12", // bad divider 't'
|
||||
"2012-12-12 12:12:12", // missing divider 'T'
|
||||
"2012-12-12T12:12:12Z", // trailing char 'Z'
|
||||
"+ 82701-123-12T12:12:12", // strange year, invalid month
|
||||
"+802701-123-12T12:12:12", // out-of-bound year, invalid month
|
||||
];
|
||||
for &s in &invalid {
|
||||
eprintln!("test_datetime_from_str invalid {:?}", s);
|
||||
assert!(s.parse::<NaiveDateTime>().is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_datetime_parse_from_str() {
|
||||
let ymdhms =
|
||||
|y, m, d, h, n, s| NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap();
|
||||
let ymdhmsn = |y, m, d, h, n, s, nano| {
|
||||
NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_nano_opt(h, n, s, nano).unwrap()
|
||||
};
|
||||
assert_eq!(
|
||||
NaiveDateTime::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"),
|
||||
Ok(ymdhms(2014, 5, 7, 12, 34, 56))
|
||||
); // ignore offset
|
||||
assert_eq!(
|
||||
NaiveDateTime::parse_from_str("2015-W06-1 000000", "%G-W%V-%u%H%M%S"),
|
||||
Ok(ymdhms(2015, 2, 2, 0, 0, 0))
|
||||
);
|
||||
assert_eq!(
|
||||
NaiveDateTime::parse_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT"),
|
||||
Ok(ymdhms(2013, 8, 9, 23, 54, 35))
|
||||
);
|
||||
assert!(
|
||||
NaiveDateTime::parse_from_str("Sat, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT")
|
||||
.is_err()
|
||||
);
|
||||
assert!(NaiveDateTime::parse_from_str("2014-5-7 Q2 12:3456", "%Y-%m-%d Q%q %H:%M:%S").is_err());
|
||||
assert!(NaiveDateTime::parse_from_str("12:34:56", "%H:%M:%S").is_err()); // insufficient
|
||||
assert_eq!(
|
||||
NaiveDateTime::parse_from_str("1441497364", "%s"),
|
||||
Ok(ymdhms(2015, 9, 5, 23, 56, 4))
|
||||
);
|
||||
assert_eq!(
|
||||
NaiveDateTime::parse_from_str("1283929614.1234", "%s.%f"),
|
||||
Ok(ymdhmsn(2010, 9, 8, 7, 6, 54, 1234))
|
||||
);
|
||||
assert_eq!(
|
||||
NaiveDateTime::parse_from_str("1441497364.649", "%s%.3f"),
|
||||
Ok(ymdhmsn(2015, 9, 5, 23, 56, 4, 649000000))
|
||||
);
|
||||
assert_eq!(
|
||||
NaiveDateTime::parse_from_str("1497854303.087654", "%s%.6f"),
|
||||
Ok(ymdhmsn(2017, 6, 19, 6, 38, 23, 87654000))
|
||||
);
|
||||
assert_eq!(
|
||||
NaiveDateTime::parse_from_str("1437742189.918273645", "%s%.9f"),
|
||||
Ok(ymdhmsn(2015, 7, 24, 12, 49, 49, 918273645))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_datetime_parse_from_str_with_spaces() {
|
||||
let parse_from_str = NaiveDateTime::parse_from_str;
|
||||
let dt = NaiveDate::from_ymd_opt(2013, 8, 9).unwrap().and_hms_opt(23, 54, 35).unwrap();
|
||||
// with varying spaces - should succeed
|
||||
assert_eq!(parse_from_str(" Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt));
|
||||
assert_eq!(parse_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S "), Ok(dt));
|
||||
assert_eq!(parse_from_str(" Aug 09 2013 23:54:35 ", " %b %d %Y %H:%M:%S "), Ok(dt));
|
||||
assert_eq!(parse_from_str(" Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt));
|
||||
assert_eq!(parse_from_str(" Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt));
|
||||
assert_eq!(parse_from_str("\n\tAug 09 2013 23:54:35 ", "\n\t%b %d %Y %H:%M:%S "), Ok(dt));
|
||||
assert_eq!(parse_from_str("\tAug 09 2013 23:54:35\t", "\t%b %d %Y %H:%M:%S\t"), Ok(dt));
|
||||
assert_eq!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S"), Ok(dt));
|
||||
assert_eq!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S"), Ok(dt));
|
||||
assert_eq!(parse_from_str("Aug 09 2013\t23:54:35", "%b %d %Y\t%H:%M:%S"), Ok(dt));
|
||||
assert_eq!(parse_from_str("Aug 09 2013\t\t23:54:35", "%b %d %Y\t\t%H:%M:%S"), Ok(dt));
|
||||
assert_eq!(parse_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S\n"), Ok(dt));
|
||||
assert_eq!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y\t%H:%M:%S"), Ok(dt));
|
||||
assert_eq!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S "), Ok(dt));
|
||||
assert_eq!(parse_from_str("Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt));
|
||||
assert_eq!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S\n"), Ok(dt));
|
||||
// with varying spaces - should fail
|
||||
// leading space in data
|
||||
assert!(parse_from_str(" Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S").is_err());
|
||||
// trailing space in data
|
||||
assert!(parse_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S").is_err());
|
||||
// trailing tab in data
|
||||
assert!(parse_from_str("Aug 09 2013 23:54:35\t", "%b %d %Y %H:%M:%S").is_err());
|
||||
// mismatched newlines
|
||||
assert!(parse_from_str("\nAug 09 2013 23:54:35", "%b %d %Y %H:%M:%S\n").is_err());
|
||||
// trailing literal in data
|
||||
assert!(parse_from_str("Aug 09 2013 23:54:35 !!!", "%b %d %Y %H:%M:%S ").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_datetime_add_sub_invariant() {
|
||||
// issue #37
|
||||
let base = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap();
|
||||
let t = -946684799990000;
|
||||
let time = base + TimeDelta::microseconds(t);
|
||||
assert_eq!(t, time.signed_duration_since(base).num_microseconds().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_and_local_timezone() {
|
||||
let ndt = NaiveDate::from_ymd_opt(2022, 6, 15).unwrap().and_hms_opt(18, 59, 36).unwrap();
|
||||
let dt_utc = ndt.and_utc();
|
||||
assert_eq!(dt_utc.naive_local(), ndt);
|
||||
assert_eq!(dt_utc.timezone(), Utc);
|
||||
|
||||
let offset_tz = FixedOffset::west_opt(4 * 3600).unwrap();
|
||||
let dt_offset = ndt.and_local_timezone(offset_tz).unwrap();
|
||||
assert_eq!(dt_offset.naive_local(), ndt);
|
||||
assert_eq!(dt_offset.timezone(), offset_tz);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_and_utc() {
|
||||
let ndt = NaiveDate::from_ymd_opt(2023, 1, 30).unwrap().and_hms_opt(19, 32, 33).unwrap();
|
||||
let dt_utc = ndt.and_utc();
|
||||
assert_eq!(dt_utc.naive_local(), ndt);
|
||||
assert_eq!(dt_utc.timezone(), Utc);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_checked_add_offset() {
|
||||
let ymdhmsm = |y, m, d, h, mn, s, mi| {
|
||||
NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_milli_opt(h, mn, s, mi)
|
||||
};
|
||||
|
||||
let positive_offset = FixedOffset::east_opt(2 * 60 * 60).unwrap();
|
||||
// regular date
|
||||
let dt = ymdhmsm(2023, 5, 5, 20, 10, 0, 0).unwrap();
|
||||
assert_eq!(dt.checked_add_offset(positive_offset), ymdhmsm(2023, 5, 5, 22, 10, 0, 0));
|
||||
// leap second is preserved
|
||||
let dt = ymdhmsm(2023, 6, 30, 23, 59, 59, 1_000).unwrap();
|
||||
assert_eq!(dt.checked_add_offset(positive_offset), ymdhmsm(2023, 7, 1, 1, 59, 59, 1_000));
|
||||
// out of range
|
||||
assert!(NaiveDateTime::MAX.checked_add_offset(positive_offset).is_none());
|
||||
|
||||
let negative_offset = FixedOffset::west_opt(2 * 60 * 60).unwrap();
|
||||
// regular date
|
||||
let dt = ymdhmsm(2023, 5, 5, 20, 10, 0, 0).unwrap();
|
||||
assert_eq!(dt.checked_add_offset(negative_offset), ymdhmsm(2023, 5, 5, 18, 10, 0, 0));
|
||||
// leap second is preserved
|
||||
let dt = ymdhmsm(2023, 6, 30, 23, 59, 59, 1_000).unwrap();
|
||||
assert_eq!(dt.checked_add_offset(negative_offset), ymdhmsm(2023, 6, 30, 21, 59, 59, 1_000));
|
||||
// out of range
|
||||
assert!(NaiveDateTime::MIN.checked_add_offset(negative_offset).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_checked_sub_offset() {
|
||||
let ymdhmsm = |y, m, d, h, mn, s, mi| {
|
||||
NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_milli_opt(h, mn, s, mi)
|
||||
};
|
||||
|
||||
let positive_offset = FixedOffset::east_opt(2 * 60 * 60).unwrap();
|
||||
// regular date
|
||||
let dt = ymdhmsm(2023, 5, 5, 20, 10, 0, 0).unwrap();
|
||||
assert_eq!(dt.checked_sub_offset(positive_offset), ymdhmsm(2023, 5, 5, 18, 10, 0, 0));
|
||||
// leap second is preserved
|
||||
let dt = ymdhmsm(2023, 6, 30, 23, 59, 59, 1_000).unwrap();
|
||||
assert_eq!(dt.checked_sub_offset(positive_offset), ymdhmsm(2023, 6, 30, 21, 59, 59, 1_000));
|
||||
// out of range
|
||||
assert!(NaiveDateTime::MIN.checked_sub_offset(positive_offset).is_none());
|
||||
|
||||
let negative_offset = FixedOffset::west_opt(2 * 60 * 60).unwrap();
|
||||
// regular date
|
||||
let dt = ymdhmsm(2023, 5, 5, 20, 10, 0, 0).unwrap();
|
||||
assert_eq!(dt.checked_sub_offset(negative_offset), ymdhmsm(2023, 5, 5, 22, 10, 0, 0));
|
||||
// leap second is preserved
|
||||
let dt = ymdhmsm(2023, 6, 30, 23, 59, 59, 1_000).unwrap();
|
||||
assert_eq!(dt.checked_sub_offset(negative_offset), ymdhmsm(2023, 7, 1, 1, 59, 59, 1_000));
|
||||
// out of range
|
||||
assert!(NaiveDateTime::MAX.checked_sub_offset(negative_offset).is_none());
|
||||
|
||||
assert_eq!(dt.checked_add_offset(positive_offset), Some(dt + positive_offset));
|
||||
assert_eq!(dt.checked_sub_offset(positive_offset), Some(dt - positive_offset));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_overflowing_add_offset() {
|
||||
let ymdhmsm = |y, m, d, h, mn, s, mi| {
|
||||
NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_milli_opt(h, mn, s, mi).unwrap()
|
||||
};
|
||||
let positive_offset = FixedOffset::east_opt(2 * 60 * 60).unwrap();
|
||||
// regular date
|
||||
let dt = ymdhmsm(2023, 5, 5, 20, 10, 0, 0);
|
||||
assert_eq!(dt.overflowing_add_offset(positive_offset), ymdhmsm(2023, 5, 5, 22, 10, 0, 0));
|
||||
// leap second is preserved
|
||||
let dt = ymdhmsm(2023, 6, 30, 23, 59, 59, 1_000);
|
||||
assert_eq!(dt.overflowing_add_offset(positive_offset), ymdhmsm(2023, 7, 1, 1, 59, 59, 1_000));
|
||||
// out of range
|
||||
assert!(NaiveDateTime::MAX.overflowing_add_offset(positive_offset) > NaiveDateTime::MAX);
|
||||
|
||||
let negative_offset = FixedOffset::west_opt(2 * 60 * 60).unwrap();
|
||||
// regular date
|
||||
let dt = ymdhmsm(2023, 5, 5, 20, 10, 0, 0);
|
||||
assert_eq!(dt.overflowing_add_offset(negative_offset), ymdhmsm(2023, 5, 5, 18, 10, 0, 0));
|
||||
// leap second is preserved
|
||||
let dt = ymdhmsm(2023, 6, 30, 23, 59, 59, 1_000);
|
||||
assert_eq!(dt.overflowing_add_offset(negative_offset), ymdhmsm(2023, 6, 30, 21, 59, 59, 1_000));
|
||||
// out of range
|
||||
assert!(NaiveDateTime::MIN.overflowing_add_offset(negative_offset) < NaiveDateTime::MIN);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_and_timezone_min_max_dates() {
|
||||
for offset_hour in -23..=23 {
|
||||
dbg!(offset_hour);
|
||||
let offset = FixedOffset::east_opt(offset_hour * 60 * 60).unwrap();
|
||||
|
||||
let local_max = NaiveDateTime::MAX.and_local_timezone(offset);
|
||||
if offset_hour >= 0 {
|
||||
assert_eq!(local_max.unwrap().naive_local(), NaiveDateTime::MAX);
|
||||
} else {
|
||||
assert_eq!(local_max, MappedLocalTime::None);
|
||||
}
|
||||
let local_min = NaiveDateTime::MIN.and_local_timezone(offset);
|
||||
if offset_hour <= 0 {
|
||||
assert_eq!(local_min.unwrap().naive_local(), NaiveDateTime::MIN);
|
||||
} else {
|
||||
assert_eq!(local_min, MappedLocalTime::None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "rkyv-validation")]
|
||||
fn test_rkyv_validation() {
|
||||
let dt_min = NaiveDateTime::MIN;
|
||||
let bytes = rkyv::to_bytes::<_, 12>(&dt_min).unwrap();
|
||||
assert_eq!(rkyv::from_bytes::<NaiveDateTime>(&bytes).unwrap(), dt_min);
|
||||
|
||||
let dt_max = NaiveDateTime::MAX;
|
||||
let bytes = rkyv::to_bytes::<_, 12>(&dt_max).unwrap();
|
||||
assert_eq!(rkyv::from_bytes::<NaiveDateTime>(&bytes).unwrap(), dt_max);
|
||||
}
|
||||
622
third_party/rust/chrono/src/naive/internals.rs
vendored
622
third_party/rust/chrono/src/naive/internals.rs
vendored
@@ -1,59 +1,52 @@
|
||||
// This is a part of Chrono.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
//! Internal helper types for working with dates.
|
||||
|
||||
//! The internal implementation of the calendar and ordinal date.
|
||||
//!
|
||||
//! The current implementation is optimized for determining year, month, day and day of week.
|
||||
//! 4-bit `YearFlags` map to one of 14 possible classes of year in the Gregorian calendar,
|
||||
//! which are included in every packed `NaiveDate` instance.
|
||||
//! The conversion between the packed calendar date (`Mdf`) and the ordinal date (`Of`) is
|
||||
//! based on the moderately-sized lookup table (~1.5KB)
|
||||
//! and the packed representation is chosen for the efficient lookup.
|
||||
//! Every internal data structure does not validate its input,
|
||||
//! but the conversion keeps the valid value valid and the invalid value invalid
|
||||
//! so that the user-facing `NaiveDate` can validate the input as late as possible.
|
||||
|
||||
#![allow(dead_code)] // some internal methods have been left for consistency
|
||||
#![cfg_attr(feature = "__internal_bench", allow(missing_docs))]
|
||||
|
||||
use core::{fmt, i32};
|
||||
use div::{div_rem, mod_floor};
|
||||
use num_traits::FromPrimitive;
|
||||
use Weekday;
|
||||
use core::fmt;
|
||||
|
||||
/// The internal date representation. This also includes the packed `Mdf` value.
|
||||
pub type DateImpl = i32;
|
||||
|
||||
pub const MAX_YEAR: DateImpl = i32::MAX >> 13;
|
||||
pub const MIN_YEAR: DateImpl = i32::MIN >> 13;
|
||||
|
||||
/// The year flags (aka the dominical letter).
|
||||
/// Year flags (aka the dominical letter).
|
||||
///
|
||||
/// `YearFlags` are used as the last four bits of `NaiveDate`, `Mdf` and `IsoWeek`.
|
||||
///
|
||||
/// There are 14 possible classes of year in the Gregorian calendar:
|
||||
/// common and leap years starting with Monday through Sunday.
|
||||
/// The `YearFlags` stores this information into 4 bits `abbb`,
|
||||
/// where `a` is `1` for the common year (simplifies the `Of` validation)
|
||||
/// and `bbb` is a non-zero `Weekday` (mapping `Mon` to 7) of the last day in the past year
|
||||
/// (simplifies the day of week calculation from the 1-based ordinal).
|
||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||
pub struct YearFlags(pub u8);
|
||||
///
|
||||
/// The `YearFlags` stores this information into 4 bits `LWWW`. `L` is the leap year flag, with `1`
|
||||
/// for the common year (this simplifies validating an ordinal in `NaiveDate`). `WWW` is a non-zero
|
||||
/// `Weekday` of the last day in the preceding year.
|
||||
#[allow(unreachable_pub)] // public as an alias for benchmarks only
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Hash)]
|
||||
pub struct YearFlags(pub(super) u8);
|
||||
|
||||
pub const A: YearFlags = YearFlags(0o15);
|
||||
pub const AG: YearFlags = YearFlags(0o05);
|
||||
pub const B: YearFlags = YearFlags(0o14);
|
||||
pub const BA: YearFlags = YearFlags(0o04);
|
||||
pub const C: YearFlags = YearFlags(0o13);
|
||||
pub const CB: YearFlags = YearFlags(0o03);
|
||||
pub const D: YearFlags = YearFlags(0o12);
|
||||
pub const DC: YearFlags = YearFlags(0o02);
|
||||
pub const E: YearFlags = YearFlags(0o11);
|
||||
pub const ED: YearFlags = YearFlags(0o01);
|
||||
pub const F: YearFlags = YearFlags(0o17);
|
||||
pub const FE: YearFlags = YearFlags(0o07);
|
||||
pub const G: YearFlags = YearFlags(0o16);
|
||||
pub const GF: YearFlags = YearFlags(0o06);
|
||||
// Weekday of the last day in the preceding year.
|
||||
// Allows for quick day of week calculation from the 1-based ordinal.
|
||||
const YEAR_STARTS_AFTER_MONDAY: u8 = 7; // non-zero to allow use with `NonZero*`.
|
||||
const YEAR_STARTS_AFTER_THUESDAY: u8 = 1;
|
||||
const YEAR_STARTS_AFTER_WEDNESDAY: u8 = 2;
|
||||
const YEAR_STARTS_AFTER_THURSDAY: u8 = 3;
|
||||
const YEAR_STARTS_AFTER_FRIDAY: u8 = 4;
|
||||
const YEAR_STARTS_AFTER_SATURDAY: u8 = 5;
|
||||
const YEAR_STARTS_AFTER_SUNDAY: u8 = 6;
|
||||
|
||||
static YEAR_TO_FLAGS: [YearFlags; 400] = [
|
||||
const COMMON_YEAR: u8 = 1 << 3;
|
||||
const LEAP_YEAR: u8 = 0 << 3;
|
||||
|
||||
pub(super) const A: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_SATURDAY);
|
||||
pub(super) const AG: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_SATURDAY);
|
||||
pub(super) const B: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_FRIDAY);
|
||||
pub(super) const BA: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_FRIDAY);
|
||||
pub(super) const C: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_THURSDAY);
|
||||
pub(super) const CB: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_THURSDAY);
|
||||
pub(super) const D: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_WEDNESDAY);
|
||||
pub(super) const DC: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_WEDNESDAY);
|
||||
pub(super) const E: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_THUESDAY);
|
||||
pub(super) const ED: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_THUESDAY);
|
||||
pub(super) const F: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_MONDAY);
|
||||
pub(super) const FE: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_MONDAY);
|
||||
pub(super) const G: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_SUNDAY);
|
||||
pub(super) const GF: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_SUNDAY);
|
||||
|
||||
const YEAR_TO_FLAGS: &[YearFlags; 400] = &[
|
||||
BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA,
|
||||
G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G,
|
||||
F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F,
|
||||
@@ -72,66 +65,31 @@ static YEAR_TO_FLAGS: [YearFlags; 400] = [
|
||||
D, CB, A, G, F, ED, C, B, A, GF, E, D, C, // 400
|
||||
];
|
||||
|
||||
static YEAR_DELTAS: [u8; 401] = [
|
||||
0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8,
|
||||
8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14,
|
||||
15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20,
|
||||
21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, // 100
|
||||
25, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30,
|
||||
30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36,
|
||||
36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42,
|
||||
42, 43, 43, 43, 43, 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 48, 48, 48,
|
||||
48, 49, 49, 49, // 200
|
||||
49, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54,
|
||||
54, 55, 55, 55, 55, 56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, 60, 60, 60,
|
||||
60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63, 64, 64, 64, 64, 65, 65, 65, 65, 66, 66, 66,
|
||||
66, 67, 67, 67, 67, 68, 68, 68, 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, 72,
|
||||
72, 73, 73, 73, // 300
|
||||
73, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, 77, 78, 78, 78,
|
||||
78, 79, 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82, 82, 82, 82, 83, 83, 83, 83, 84, 84, 84,
|
||||
84, 85, 85, 85, 85, 86, 86, 86, 86, 87, 87, 87, 87, 88, 88, 88, 88, 89, 89, 89, 89, 90, 90, 90,
|
||||
90, 91, 91, 91, 91, 92, 92, 92, 92, 93, 93, 93, 93, 94, 94, 94, 94, 95, 95, 95, 95, 96, 96, 96,
|
||||
96, 97, 97, 97, 97, // 400+1
|
||||
];
|
||||
|
||||
pub fn cycle_to_yo(cycle: u32) -> (u32, u32) {
|
||||
let (mut year_mod_400, mut ordinal0) = div_rem(cycle, 365);
|
||||
let delta = u32::from(YEAR_DELTAS[year_mod_400 as usize]);
|
||||
if ordinal0 < delta {
|
||||
year_mod_400 -= 1;
|
||||
ordinal0 += 365 - u32::from(YEAR_DELTAS[year_mod_400 as usize]);
|
||||
} else {
|
||||
ordinal0 -= delta;
|
||||
}
|
||||
(year_mod_400, ordinal0 + 1)
|
||||
}
|
||||
|
||||
pub fn yo_to_cycle(year_mod_400: u32, ordinal: u32) -> u32 {
|
||||
year_mod_400 * 365 + u32::from(YEAR_DELTAS[year_mod_400 as usize]) + ordinal - 1
|
||||
}
|
||||
|
||||
impl YearFlags {
|
||||
#[allow(unreachable_pub)] // public as an alias for benchmarks only
|
||||
#[doc(hidden)] // for benchmarks only
|
||||
#[inline]
|
||||
pub fn from_year(year: i32) -> YearFlags {
|
||||
let year = mod_floor(year, 400);
|
||||
#[must_use]
|
||||
pub const fn from_year(year: i32) -> YearFlags {
|
||||
let year = year.rem_euclid(400);
|
||||
YearFlags::from_year_mod_400(year)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_year_mod_400(year: i32) -> YearFlags {
|
||||
pub(super) const fn from_year_mod_400(year: i32) -> YearFlags {
|
||||
YEAR_TO_FLAGS[year as usize]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ndays(&self) -> u32 {
|
||||
pub(super) const fn ndays(&self) -> u32 {
|
||||
let YearFlags(flags) = *self;
|
||||
366 - u32::from(flags >> 3)
|
||||
366 - (flags >> 3) as u32
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn isoweek_delta(&self) -> u32 {
|
||||
pub(super) const fn isoweek_delta(&self) -> u32 {
|
||||
let YearFlags(flags) = *self;
|
||||
let mut delta = u32::from(flags) & 0b0111;
|
||||
let mut delta = (flags & 0b0111) as u32;
|
||||
if delta < 3 {
|
||||
delta += 7;
|
||||
}
|
||||
@@ -139,7 +97,7 @@ impl YearFlags {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn nisoweeks(&self) -> u32 {
|
||||
pub(super) const fn nisoweeks(&self) -> u32 {
|
||||
let YearFlags(flags) = *self;
|
||||
52 + ((0b0000_0100_0000_0110 >> flags as usize) & 1)
|
||||
}
|
||||
@@ -170,13 +128,15 @@ impl fmt::Debug for YearFlags {
|
||||
}
|
||||
}
|
||||
|
||||
pub const MIN_OL: u32 = 1 << 1;
|
||||
pub const MAX_OL: u32 = 366 << 1; // larger than the non-leap last day `(365 << 1) | 1`
|
||||
pub const MIN_MDL: u32 = (1 << 6) | (1 << 1);
|
||||
pub const MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1;
|
||||
// OL: (ordinal << 1) | leap year flag
|
||||
const MAX_OL: u32 = 366 << 1; // `(366 << 1) | 1` would be day 366 in a non-leap year
|
||||
const MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1;
|
||||
|
||||
const XX: i8 = -128;
|
||||
static MDL_TO_OL: [i8; MAX_MDL as usize + 1] = [
|
||||
// The next table are adjustment values to convert a date encoded as month-day-leapyear to
|
||||
// ordinal-leapyear. OL = MDL - adjustment.
|
||||
// Dates that do not exist are encoded as `XX`.
|
||||
const XX: i8 = 0;
|
||||
const MDL_TO_OL: &[i8; MAX_MDL as usize + 1] = &[
|
||||
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
|
||||
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
|
||||
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0
|
||||
@@ -219,7 +179,7 @@ static MDL_TO_OL: [i8; MAX_MDL as usize + 1] = [
|
||||
100, // 12
|
||||
];
|
||||
|
||||
static OL_TO_MDL: [u8; MAX_OL as usize + 1] = [
|
||||
const OL_TO_MDL: &[u8; MAX_OL as usize + 1] = &[
|
||||
0, 0, // 0
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
@@ -260,209 +220,147 @@ static OL_TO_MDL: [u8; MAX_OL as usize + 1] = [
|
||||
98, // 12
|
||||
];
|
||||
|
||||
/// Ordinal (day of year) and year flags: `(ordinal << 4) | flags`.
|
||||
///
|
||||
/// The whole bits except for the least 3 bits are referred as `Ol` (ordinal and leap flag),
|
||||
/// which is an index to the `OL_TO_MDL` lookup table.
|
||||
#[derive(PartialEq, PartialOrd, Copy, Clone)]
|
||||
pub struct Of(pub u32);
|
||||
|
||||
impl Of {
|
||||
#[inline]
|
||||
fn clamp_ordinal(ordinal: u32) -> u32 {
|
||||
if ordinal > 366 {
|
||||
0
|
||||
} else {
|
||||
ordinal
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new(ordinal: u32, YearFlags(flags): YearFlags) -> Of {
|
||||
let ordinal = Of::clamp_ordinal(ordinal);
|
||||
Of((ordinal << 4) | u32::from(flags))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_mdf(Mdf(mdf): Mdf) -> Of {
|
||||
let mdl = mdf >> 3;
|
||||
match MDL_TO_OL.get(mdl as usize) {
|
||||
Some(&v) => Of(mdf.wrapping_sub((i32::from(v) as u32 & 0x3ff) << 3)),
|
||||
None => Of(0),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn valid(&self) -> bool {
|
||||
let Of(of) = *self;
|
||||
let ol = of >> 3;
|
||||
MIN_OL <= ol && ol <= MAX_OL
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ordinal(&self) -> u32 {
|
||||
let Of(of) = *self;
|
||||
of >> 4
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_ordinal(&self, ordinal: u32) -> Of {
|
||||
let ordinal = Of::clamp_ordinal(ordinal);
|
||||
let Of(of) = *self;
|
||||
Of((of & 0b1111) | (ordinal << 4))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn flags(&self) -> YearFlags {
|
||||
let Of(of) = *self;
|
||||
YearFlags((of & 0b1111) as u8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_flags(&self, YearFlags(flags): YearFlags) -> Of {
|
||||
let Of(of) = *self;
|
||||
Of((of & !0b1111) | u32::from(flags))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn weekday(&self) -> Weekday {
|
||||
let Of(of) = *self;
|
||||
Weekday::from_u32(((of >> 4) + (of & 0b111)) % 7).unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn isoweekdate_raw(&self) -> (u32, Weekday) {
|
||||
// week ordinal = ordinal + delta
|
||||
let Of(of) = *self;
|
||||
let weekord = (of >> 4).wrapping_add(self.flags().isoweek_delta());
|
||||
(weekord / 7, Weekday::from_u32(weekord % 7).unwrap())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_mdf(&self) -> Mdf {
|
||||
Mdf::from_of(*self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn succ(&self) -> Of {
|
||||
let Of(of) = *self;
|
||||
Of(of + (1 << 4))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pred(&self) -> Of {
|
||||
let Of(of) = *self;
|
||||
Of(of - (1 << 4))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Of {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Of(of) = *self;
|
||||
write!(
|
||||
f,
|
||||
"Of(({} << 4) | {:#04o} /*{:?}*/)",
|
||||
of >> 4,
|
||||
of & 0b1111,
|
||||
YearFlags((of & 0b1111) as u8)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Month, day of month and year flags: `(month << 9) | (day << 4) | flags`
|
||||
/// `M_MMMD_DDDD_LFFF`
|
||||
///
|
||||
/// The whole bits except for the least 3 bits are referred as `Mdl`
|
||||
/// (month, day of month and leap flag),
|
||||
/// which is an index to the `MDL_TO_OL` lookup table.
|
||||
/// The whole bits except for the least 3 bits are referred as `Mdl` (month, day of month, and leap
|
||||
/// year flag), which is an index to the `MDL_TO_OL` lookup table.
|
||||
///
|
||||
/// The conversion between the packed calendar date (`Mdf`) and the ordinal date (`NaiveDate`) is
|
||||
/// based on the moderately-sized lookup table (~1.5KB) and the packed representation is chosen for
|
||||
/// efficient lookup.
|
||||
///
|
||||
/// The methods of `Mdf` validate their inputs as late as possible. Dates that can't exist, like
|
||||
/// February 30, can still be represented. This allows the validation to be combined with the final
|
||||
/// table lookup, which is good for performance.
|
||||
#[derive(PartialEq, PartialOrd, Copy, Clone)]
|
||||
pub struct Mdf(pub u32);
|
||||
pub(super) struct Mdf(u32);
|
||||
|
||||
impl Mdf {
|
||||
/// Makes a new `Mdf` value from month, day and `YearFlags`.
|
||||
///
|
||||
/// This method doesn't fully validate the range of the `month` and `day` parameters, only as
|
||||
/// much as what can't be deferred until later. The year `flags` are trusted to be correct.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if `month > 12` or `day > 31`.
|
||||
#[inline]
|
||||
fn clamp_month(month: u32) -> u32 {
|
||||
if month > 12 {
|
||||
0
|
||||
} else {
|
||||
month
|
||||
pub(super) const fn new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Option<Mdf> {
|
||||
match month <= 12 && day <= 31 {
|
||||
true => Some(Mdf((month << 9) | (day << 4) | flags as u32)),
|
||||
false => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `Mdf` value from an `i32` with an ordinal and a leap year flag, and year
|
||||
/// `flags`.
|
||||
///
|
||||
/// The `ol` is trusted to be valid, and the `flags` are trusted to match it.
|
||||
#[inline]
|
||||
fn clamp_day(day: u32) -> u32 {
|
||||
if day > 31 {
|
||||
0
|
||||
} else {
|
||||
day
|
||||
}
|
||||
pub(super) const fn from_ol(ol: i32, YearFlags(flags): YearFlags) -> Mdf {
|
||||
debug_assert!(ol > 1 && ol <= MAX_OL as i32);
|
||||
// Array is indexed from `[2..=MAX_OL]`, with a `0` index having a meaningless value.
|
||||
Mdf(((ol as u32 + OL_TO_MDL[ol as usize] as u32) << 3) | flags as u32)
|
||||
}
|
||||
|
||||
/// Returns the month of this `Mdf`.
|
||||
#[inline]
|
||||
pub fn new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Mdf {
|
||||
let month = Mdf::clamp_month(month);
|
||||
let day = Mdf::clamp_day(day);
|
||||
Mdf((month << 9) | (day << 4) | u32::from(flags))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_of(Of(of): Of) -> Mdf {
|
||||
let ol = of >> 3;
|
||||
match OL_TO_MDL.get(ol as usize) {
|
||||
Some(&v) => Mdf(of + (u32::from(v) << 3)),
|
||||
None => Mdf(0),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn valid(&self) -> bool {
|
||||
let Mdf(mdf) = *self;
|
||||
let mdl = mdf >> 3;
|
||||
match MDL_TO_OL.get(mdl as usize) {
|
||||
Some(&v) => v >= 0,
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn month(&self) -> u32 {
|
||||
pub(super) const fn month(&self) -> u32 {
|
||||
let Mdf(mdf) = *self;
|
||||
mdf >> 9
|
||||
}
|
||||
|
||||
/// Replaces the month of this `Mdf`, keeping the day and flags.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if `month > 12`.
|
||||
#[inline]
|
||||
pub fn with_month(&self, month: u32) -> Mdf {
|
||||
let month = Mdf::clamp_month(month);
|
||||
pub(super) const fn with_month(&self, month: u32) -> Option<Mdf> {
|
||||
if month > 12 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let Mdf(mdf) = *self;
|
||||
Mdf((mdf & 0b1_1111_1111) | (month << 9))
|
||||
Some(Mdf((mdf & 0b1_1111_1111) | (month << 9)))
|
||||
}
|
||||
|
||||
/// Returns the day of this `Mdf`.
|
||||
#[inline]
|
||||
pub fn day(&self) -> u32 {
|
||||
pub(super) const fn day(&self) -> u32 {
|
||||
let Mdf(mdf) = *self;
|
||||
(mdf >> 4) & 0b1_1111
|
||||
}
|
||||
|
||||
/// Replaces the day of this `Mdf`, keeping the month and flags.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if `day > 31`.
|
||||
#[inline]
|
||||
pub fn with_day(&self, day: u32) -> Mdf {
|
||||
let day = Mdf::clamp_day(day);
|
||||
pub(super) const fn with_day(&self, day: u32) -> Option<Mdf> {
|
||||
if day > 31 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let Mdf(mdf) = *self;
|
||||
Mdf((mdf & !0b1_1111_0000) | (day << 4))
|
||||
Some(Mdf((mdf & !0b1_1111_0000) | (day << 4)))
|
||||
}
|
||||
|
||||
/// Replaces the flags of this `Mdf`, keeping the month and day.
|
||||
#[inline]
|
||||
pub fn flags(&self) -> YearFlags {
|
||||
pub(super) const fn with_flags(&self, YearFlags(flags): YearFlags) -> Mdf {
|
||||
let Mdf(mdf) = *self;
|
||||
YearFlags((mdf & 0b1111) as u8)
|
||||
Mdf((mdf & !0b1111) | flags as u32)
|
||||
}
|
||||
|
||||
/// Returns the ordinal that corresponds to this `Mdf`.
|
||||
///
|
||||
/// This does a table lookup to calculate the corresponding ordinal. It will return an error if
|
||||
/// the `Mdl` turns out not to be a valid date.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if `month == 0` or `day == 0`, or if a the given day does not exist in the
|
||||
/// given month.
|
||||
#[inline]
|
||||
pub fn with_flags(&self, YearFlags(flags): YearFlags) -> Mdf {
|
||||
let Mdf(mdf) = *self;
|
||||
Mdf((mdf & !0b1111) | u32::from(flags))
|
||||
pub(super) const fn ordinal(&self) -> Option<u32> {
|
||||
let mdl = self.0 >> 3;
|
||||
match MDL_TO_OL[mdl as usize] {
|
||||
XX => None,
|
||||
v => Some((mdl - v as u8 as u32) >> 1),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the year flags of this `Mdf`.
|
||||
#[inline]
|
||||
pub fn to_of(&self) -> Of {
|
||||
Of::from_mdf(*self)
|
||||
pub(super) const fn year_flags(&self) -> YearFlags {
|
||||
YearFlags((self.0 & 0b1111) as u8)
|
||||
}
|
||||
|
||||
/// Returns the ordinal that corresponds to this `Mdf`, encoded as a value including year flags.
|
||||
///
|
||||
/// This does a table lookup to calculate the corresponding ordinal. It will return an error if
|
||||
/// the `Mdl` turns out not to be a valid date.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if `month == 0` or `day == 0`, or if a the given day does not exist in the
|
||||
/// given month.
|
||||
#[inline]
|
||||
pub(super) const fn ordinal_and_flags(&self) -> Option<i32> {
|
||||
let mdl = self.0 >> 3;
|
||||
match MDL_TO_OL[mdl as usize] {
|
||||
XX => None,
|
||||
v => Some(self.0 as i32 - ((v as i32) << 3)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn valid(&self) -> bool {
|
||||
let mdl = self.0 >> 3;
|
||||
MDL_TO_OL[mdl as usize] > 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,14 +380,8 @@ impl fmt::Debug for Mdf {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[cfg(test)]
|
||||
extern crate num_iter;
|
||||
|
||||
use self::num_iter::range_inclusive;
|
||||
use super::{Mdf, Of};
|
||||
use super::{YearFlags, A, AG, B, BA, C, CB, D, DC, E, ED, F, FE, G, GF};
|
||||
use std::u32;
|
||||
use Weekday;
|
||||
use super::Mdf;
|
||||
use super::{A, AG, B, BA, C, CB, D, DC, E, ED, F, FE, G, GF, YearFlags};
|
||||
|
||||
const NONLEAP_FLAGS: [YearFlags; 7] = [A, B, C, D, E, F, G];
|
||||
const LEAP_FLAGS: [YearFlags; 7] = [AG, BA, CB, DC, ED, FE, GF];
|
||||
@@ -530,43 +422,17 @@ mod tests {
|
||||
assert_eq!(GF.nisoweeks(), 52);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of() {
|
||||
fn check(expected: bool, flags: YearFlags, ordinal1: u32, ordinal2: u32) {
|
||||
for ordinal in range_inclusive(ordinal1, ordinal2) {
|
||||
let of = Of::new(ordinal, flags);
|
||||
assert!(
|
||||
of.valid() == expected,
|
||||
"ordinal {} = {:?} should be {} for dominical year {:?}",
|
||||
ordinal,
|
||||
of,
|
||||
if expected { "valid" } else { "invalid" },
|
||||
flags
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for &flags in NONLEAP_FLAGS.iter() {
|
||||
check(false, flags, 0, 0);
|
||||
check(true, flags, 1, 365);
|
||||
check(false, flags, 366, 1024);
|
||||
check(false, flags, u32::MAX, u32::MAX);
|
||||
}
|
||||
|
||||
for &flags in LEAP_FLAGS.iter() {
|
||||
check(false, flags, 0, 0);
|
||||
check(true, flags, 1, 366);
|
||||
check(false, flags, 367, 1024);
|
||||
check(false, flags, u32::MAX, u32::MAX);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mdf_valid() {
|
||||
fn check(expected: bool, flags: YearFlags, month1: u32, day1: u32, month2: u32, day2: u32) {
|
||||
for month in range_inclusive(month1, month2) {
|
||||
for day in range_inclusive(day1, day2) {
|
||||
let mdf = Mdf::new(month, day, flags);
|
||||
for month in month1..=month2 {
|
||||
for day in day1..=day2 {
|
||||
let mdf = match Mdf::new(month, day, flags) {
|
||||
Some(mdf) => mdf,
|
||||
None if !expected => continue,
|
||||
None => panic!("Mdf::new({}, {}, {:?}) returned None", month, day, flags),
|
||||
};
|
||||
|
||||
assert!(
|
||||
mdf.valid() == expected,
|
||||
"month {} day {} = {:?} should be {} for dominical year {:?}",
|
||||
@@ -647,76 +513,16 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of_fields() {
|
||||
for &flags in FLAGS.iter() {
|
||||
for ordinal in range_inclusive(1u32, 366) {
|
||||
let of = Of::new(ordinal, flags);
|
||||
if of.valid() {
|
||||
assert_eq!(of.ordinal(), ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of_with_fields() {
|
||||
fn check(flags: YearFlags, ordinal: u32) {
|
||||
let of = Of::new(ordinal, flags);
|
||||
|
||||
for ordinal in range_inclusive(0u32, 1024) {
|
||||
let of = of.with_ordinal(ordinal);
|
||||
assert_eq!(of.valid(), Of::new(ordinal, flags).valid());
|
||||
if of.valid() {
|
||||
assert_eq!(of.ordinal(), ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for &flags in NONLEAP_FLAGS.iter() {
|
||||
check(flags, 1);
|
||||
check(flags, 365);
|
||||
}
|
||||
for &flags in LEAP_FLAGS.iter() {
|
||||
check(flags, 1);
|
||||
check(flags, 366);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of_weekday() {
|
||||
assert_eq!(Of::new(1, A).weekday(), Weekday::Sun);
|
||||
assert_eq!(Of::new(1, B).weekday(), Weekday::Sat);
|
||||
assert_eq!(Of::new(1, C).weekday(), Weekday::Fri);
|
||||
assert_eq!(Of::new(1, D).weekday(), Weekday::Thu);
|
||||
assert_eq!(Of::new(1, E).weekday(), Weekday::Wed);
|
||||
assert_eq!(Of::new(1, F).weekday(), Weekday::Tue);
|
||||
assert_eq!(Of::new(1, G).weekday(), Weekday::Mon);
|
||||
assert_eq!(Of::new(1, AG).weekday(), Weekday::Sun);
|
||||
assert_eq!(Of::new(1, BA).weekday(), Weekday::Sat);
|
||||
assert_eq!(Of::new(1, CB).weekday(), Weekday::Fri);
|
||||
assert_eq!(Of::new(1, DC).weekday(), Weekday::Thu);
|
||||
assert_eq!(Of::new(1, ED).weekday(), Weekday::Wed);
|
||||
assert_eq!(Of::new(1, FE).weekday(), Weekday::Tue);
|
||||
assert_eq!(Of::new(1, GF).weekday(), Weekday::Mon);
|
||||
|
||||
for &flags in FLAGS.iter() {
|
||||
let mut prev = Of::new(1, flags).weekday();
|
||||
for ordinal in range_inclusive(2u32, flags.ndays()) {
|
||||
let of = Of::new(ordinal, flags);
|
||||
let expected = prev.succ();
|
||||
assert_eq!(of.weekday(), expected);
|
||||
prev = expected;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mdf_fields() {
|
||||
for &flags in FLAGS.iter() {
|
||||
for month in range_inclusive(1u32, 12) {
|
||||
for day in range_inclusive(1u32, 31) {
|
||||
let mdf = Mdf::new(month, day, flags);
|
||||
for month in 1u32..=12 {
|
||||
for day in 1u32..31 {
|
||||
let mdf = match Mdf::new(month, day, flags) {
|
||||
Some(mdf) => mdf,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
if mdf.valid() {
|
||||
assert_eq!(mdf.month(), month);
|
||||
assert_eq!(mdf.day(), day);
|
||||
@@ -729,20 +535,28 @@ mod tests {
|
||||
#[test]
|
||||
fn test_mdf_with_fields() {
|
||||
fn check(flags: YearFlags, month: u32, day: u32) {
|
||||
let mdf = Mdf::new(month, day, flags);
|
||||
let mdf = Mdf::new(month, day, flags).unwrap();
|
||||
|
||||
for month in 0u32..=16 {
|
||||
let mdf = match mdf.with_month(month) {
|
||||
Some(mdf) => mdf,
|
||||
None if month > 12 => continue,
|
||||
None => panic!("failed to create Mdf with month {}", month),
|
||||
};
|
||||
|
||||
for month in range_inclusive(0u32, 16) {
|
||||
let mdf = mdf.with_month(month);
|
||||
assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid());
|
||||
if mdf.valid() {
|
||||
assert_eq!(mdf.month(), month);
|
||||
assert_eq!(mdf.day(), day);
|
||||
}
|
||||
}
|
||||
|
||||
for day in range_inclusive(0u32, 1024) {
|
||||
let mdf = mdf.with_day(day);
|
||||
assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid());
|
||||
for day in 0u32..=1024 {
|
||||
let mdf = match mdf.with_day(day) {
|
||||
Some(mdf) => mdf,
|
||||
None if day > 31 => continue,
|
||||
None => panic!("failed to create Mdf with month {}", month),
|
||||
};
|
||||
|
||||
if mdf.valid() {
|
||||
assert_eq!(mdf.month(), month);
|
||||
assert_eq!(mdf.day(), day);
|
||||
@@ -769,47 +583,9 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of_isoweekdate_raw() {
|
||||
for &flags in FLAGS.iter() {
|
||||
// January 4 should be in the first week
|
||||
let (week, _) = Of::new(4 /* January 4 */, flags).isoweekdate_raw();
|
||||
assert_eq!(week, 1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of_to_mdf() {
|
||||
for i in range_inclusive(0u32, 8192) {
|
||||
let of = Of(i);
|
||||
assert_eq!(of.valid(), of.to_mdf().valid());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mdf_to_of() {
|
||||
for i in range_inclusive(0u32, 8192) {
|
||||
let mdf = Mdf(i);
|
||||
assert_eq!(mdf.valid(), mdf.to_of().valid());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of_to_mdf_to_of() {
|
||||
for i in range_inclusive(0u32, 8192) {
|
||||
let of = Of(i);
|
||||
if of.valid() {
|
||||
assert_eq!(of, of.to_mdf().to_of());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mdf_to_of_to_mdf() {
|
||||
for i in range_inclusive(0u32, 8192) {
|
||||
let mdf = Mdf(i);
|
||||
if mdf.valid() {
|
||||
assert_eq!(mdf, mdf.to_of().to_mdf());
|
||||
}
|
||||
}
|
||||
fn test_mdf_new_range() {
|
||||
let flags = YearFlags::from_year(2023);
|
||||
assert!(Mdf::new(13, 1, flags).is_none());
|
||||
assert!(Mdf::new(1, 32, flags).is_none());
|
||||
}
|
||||
}
|
||||
|
||||
206
third_party/rust/chrono/src/naive/isoweek.rs
vendored
206
third_party/rust/chrono/src/naive/isoweek.rs
vendored
@@ -5,69 +5,80 @@
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use super::internals::{DateImpl, Of, YearFlags};
|
||||
use super::internals::YearFlags;
|
||||
|
||||
#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))]
|
||||
use rkyv::{Archive, Deserialize, Serialize};
|
||||
|
||||
/// ISO 8601 week.
|
||||
///
|
||||
/// This type, combined with [`Weekday`](../enum.Weekday.html),
|
||||
/// constitues the ISO 8601 [week date](./struct.NaiveDate.html#week-date).
|
||||
/// constitutes the ISO 8601 [week date](./struct.NaiveDate.html#week-date).
|
||||
/// One can retrieve this type from the existing [`Datelike`](../trait.Datelike.html) types
|
||||
/// via the [`Datelike::iso_week`](../trait.Datelike.html#tymethod.iso_week) method.
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Hash)]
|
||||
#[cfg_attr(
|
||||
any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"),
|
||||
derive(Archive, Deserialize, Serialize),
|
||||
archive(compare(PartialEq, PartialOrd)),
|
||||
archive_attr(derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash))
|
||||
)]
|
||||
#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
|
||||
pub struct IsoWeek {
|
||||
// note that this allows for larger year range than `NaiveDate`.
|
||||
// this is crucial because we have an edge case for the first and last week supported,
|
||||
// Note that this allows for larger year range than `NaiveDate`.
|
||||
// This is crucial because we have an edge case for the first and last week supported,
|
||||
// which year number might not match the calendar year number.
|
||||
ywf: DateImpl, // (year << 10) | (week << 4) | flag
|
||||
}
|
||||
|
||||
/// Returns the corresponding `IsoWeek` from the year and the `Of` internal value.
|
||||
//
|
||||
// internal use only. we don't expose the public constructor for `IsoWeek` for now,
|
||||
// because the year range for the week date and the calendar date do not match and
|
||||
// it is confusing to have a date that is out of range in one and not in another.
|
||||
// currently we sidestep this issue by making `IsoWeek` fully dependent of `Datelike`.
|
||||
pub fn iso_week_from_yof(year: i32, of: Of) -> IsoWeek {
|
||||
let (rawweek, _) = of.isoweekdate_raw();
|
||||
let (year, week) = if rawweek < 1 {
|
||||
// previous year
|
||||
let prevlastweek = YearFlags::from_year(year - 1).nisoweeks();
|
||||
(year - 1, prevlastweek)
|
||||
} else {
|
||||
let lastweek = of.flags().nisoweeks();
|
||||
if rawweek > lastweek {
|
||||
// next year
|
||||
(year + 1, 1)
|
||||
} else {
|
||||
(year, rawweek)
|
||||
}
|
||||
};
|
||||
IsoWeek { ywf: (year << 10) | (week << 4) as DateImpl | DateImpl::from(of.flags().0) }
|
||||
ywf: i32, // (year << 10) | (week << 4) | flag
|
||||
}
|
||||
|
||||
impl IsoWeek {
|
||||
/// Returns the corresponding `IsoWeek` from the year and the `Of` internal value.
|
||||
//
|
||||
// Internal use only. We don't expose the public constructor for `IsoWeek` for now
|
||||
// because the year range for the week date and the calendar date do not match, and
|
||||
// it is confusing to have a date that is out of range in one and not in another.
|
||||
// Currently we sidestep this issue by making `IsoWeek` fully dependent of `Datelike`.
|
||||
pub(super) fn from_yof(year: i32, ordinal: u32, year_flags: YearFlags) -> Self {
|
||||
let rawweek = (ordinal + year_flags.isoweek_delta()) / 7;
|
||||
let (year, week) = if rawweek < 1 {
|
||||
// previous year
|
||||
let prevlastweek = YearFlags::from_year(year - 1).nisoweeks();
|
||||
(year - 1, prevlastweek)
|
||||
} else {
|
||||
let lastweek = year_flags.nisoweeks();
|
||||
if rawweek > lastweek {
|
||||
// next year
|
||||
(year + 1, 1)
|
||||
} else {
|
||||
(year, rawweek)
|
||||
}
|
||||
};
|
||||
let flags = YearFlags::from_year(year);
|
||||
IsoWeek { ywf: (year << 10) | (week << 4) as i32 | i32::from(flags.0) }
|
||||
}
|
||||
|
||||
/// Returns the year number for this ISO week.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{NaiveDate, Datelike, Weekday};
|
||||
/// ```
|
||||
/// use chrono::{Datelike, NaiveDate, Weekday};
|
||||
///
|
||||
/// let d = NaiveDate::from_isoywd(2015, 1, Weekday::Mon);
|
||||
/// let d = NaiveDate::from_isoywd_opt(2015, 1, Weekday::Mon).unwrap();
|
||||
/// assert_eq!(d.iso_week().year(), 2015);
|
||||
/// ~~~~
|
||||
/// ```
|
||||
///
|
||||
/// This year number might not match the calendar year number.
|
||||
/// Continuing the example...
|
||||
///
|
||||
/// ~~~~
|
||||
/// ```
|
||||
/// # use chrono::{NaiveDate, Datelike, Weekday};
|
||||
/// # let d = NaiveDate::from_isoywd(2015, 1, Weekday::Mon);
|
||||
/// # let d = NaiveDate::from_isoywd_opt(2015, 1, Weekday::Mon).unwrap();
|
||||
/// assert_eq!(d.year(), 2014);
|
||||
/// assert_eq!(d, NaiveDate::from_ymd(2014, 12, 29));
|
||||
/// ~~~~
|
||||
/// assert_eq!(d, NaiveDate::from_ymd_opt(2014, 12, 29).unwrap());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn year(&self) -> i32 {
|
||||
pub const fn year(&self) -> i32 {
|
||||
self.ywf >> 10
|
||||
}
|
||||
|
||||
@@ -77,14 +88,14 @@ impl IsoWeek {
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{NaiveDate, Datelike, Weekday};
|
||||
/// ```
|
||||
/// use chrono::{Datelike, NaiveDate, Weekday};
|
||||
///
|
||||
/// let d = NaiveDate::from_isoywd(2015, 15, Weekday::Mon);
|
||||
/// let d = NaiveDate::from_isoywd_opt(2015, 15, Weekday::Mon).unwrap();
|
||||
/// assert_eq!(d.iso_week().week(), 15);
|
||||
/// ~~~~
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn week(&self) -> u32 {
|
||||
pub const fn week(&self) -> u32 {
|
||||
((self.ywf >> 4) & 0x3f) as u32
|
||||
}
|
||||
|
||||
@@ -94,14 +105,14 @@ impl IsoWeek {
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{NaiveDate, Datelike, Weekday};
|
||||
/// ```
|
||||
/// use chrono::{Datelike, NaiveDate, Weekday};
|
||||
///
|
||||
/// let d = NaiveDate::from_isoywd(2015, 15, Weekday::Mon);
|
||||
/// let d = NaiveDate::from_isoywd_opt(2015, 15, Weekday::Mon).unwrap();
|
||||
/// assert_eq!(d.iso_week().week0(), 14);
|
||||
/// ~~~~
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn week0(&self) -> u32 {
|
||||
pub const fn week0(&self) -> u32 {
|
||||
((self.ywf >> 4) & 0x3f) as u32 - 1
|
||||
}
|
||||
}
|
||||
@@ -112,26 +123,35 @@ impl IsoWeek {
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{NaiveDate, Datelike};
|
||||
/// ```
|
||||
/// use chrono::{Datelike, NaiveDate};
|
||||
///
|
||||
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(2015, 9, 5).iso_week()), "2015-W36");
|
||||
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd( 0, 1, 3).iso_week()), "0000-W01");
|
||||
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(9999, 12, 31).iso_week()), "9999-W52");
|
||||
/// ~~~~
|
||||
/// assert_eq!(
|
||||
/// format!("{:?}", NaiveDate::from_ymd_opt(2015, 9, 5).unwrap().iso_week()),
|
||||
/// "2015-W36"
|
||||
/// );
|
||||
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(0, 1, 3).unwrap().iso_week()), "0000-W01");
|
||||
/// assert_eq!(
|
||||
/// format!("{:?}", NaiveDate::from_ymd_opt(9999, 12, 31).unwrap().iso_week()),
|
||||
/// "9999-W52"
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// ISO 8601 requires an explicit sign for years before 1 BCE or after 9999 CE.
|
||||
///
|
||||
/// ~~~~
|
||||
/// ```
|
||||
/// # use chrono::{NaiveDate, Datelike};
|
||||
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd( 0, 1, 2).iso_week()), "-0001-W52");
|
||||
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(10000, 12, 31).iso_week()), "+10000-W52");
|
||||
/// ~~~~
|
||||
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(0, 1, 2).unwrap().iso_week()), "-0001-W52");
|
||||
/// assert_eq!(
|
||||
/// format!("{:?}", NaiveDate::from_ymd_opt(10000, 12, 31).unwrap().iso_week()),
|
||||
/// "+10000-W52"
|
||||
/// );
|
||||
/// ```
|
||||
impl fmt::Debug for IsoWeek {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let year = self.year();
|
||||
let week = self.week();
|
||||
if 0 <= year && year <= 9999 {
|
||||
if (0..=9999).contains(&year) {
|
||||
write!(f, "{:04}-W{:02}", year, week)
|
||||
} else {
|
||||
// ISO 8601 requires the explicit sign for out-of-range years
|
||||
@@ -142,22 +162,72 @@ impl fmt::Debug for IsoWeek {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use naive::{internals, MAX_DATE, MIN_DATE};
|
||||
use Datelike;
|
||||
#[cfg(feature = "rkyv-validation")]
|
||||
use super::IsoWeek;
|
||||
use crate::Datelike;
|
||||
use crate::naive::date::{self, NaiveDate};
|
||||
|
||||
#[test]
|
||||
fn test_iso_week_extremes() {
|
||||
let minweek = MIN_DATE.iso_week();
|
||||
let maxweek = MAX_DATE.iso_week();
|
||||
let minweek = NaiveDate::MIN.iso_week();
|
||||
let maxweek = NaiveDate::MAX.iso_week();
|
||||
|
||||
assert_eq!(minweek.year(), internals::MIN_YEAR);
|
||||
assert_eq!(minweek.year(), date::MIN_YEAR);
|
||||
assert_eq!(minweek.week(), 1);
|
||||
assert_eq!(minweek.week0(), 0);
|
||||
assert_eq!(format!("{:?}", minweek), MIN_DATE.format("%G-W%V").to_string());
|
||||
#[cfg(feature = "alloc")]
|
||||
assert_eq!(format!("{:?}", minweek), NaiveDate::MIN.format("%G-W%V").to_string());
|
||||
|
||||
assert_eq!(maxweek.year(), internals::MAX_YEAR + 1);
|
||||
assert_eq!(maxweek.year(), date::MAX_YEAR + 1);
|
||||
assert_eq!(maxweek.week(), 1);
|
||||
assert_eq!(maxweek.week0(), 0);
|
||||
assert_eq!(format!("{:?}", maxweek), MAX_DATE.format("%G-W%V").to_string());
|
||||
#[cfg(feature = "alloc")]
|
||||
assert_eq!(format!("{:?}", maxweek), NaiveDate::MAX.format("%G-W%V").to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iso_week_equivalence_for_first_week() {
|
||||
let monday = NaiveDate::from_ymd_opt(2024, 12, 30).unwrap();
|
||||
let friday = NaiveDate::from_ymd_opt(2025, 1, 3).unwrap();
|
||||
|
||||
assert_eq!(monday.iso_week(), friday.iso_week());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iso_week_equivalence_for_last_week() {
|
||||
let monday = NaiveDate::from_ymd_opt(2026, 12, 28).unwrap();
|
||||
let friday = NaiveDate::from_ymd_opt(2027, 1, 1).unwrap();
|
||||
|
||||
assert_eq!(monday.iso_week(), friday.iso_week());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iso_week_ordering_for_first_week() {
|
||||
let monday = NaiveDate::from_ymd_opt(2024, 12, 30).unwrap();
|
||||
let friday = NaiveDate::from_ymd_opt(2025, 1, 3).unwrap();
|
||||
|
||||
assert!(monday.iso_week() >= friday.iso_week());
|
||||
assert!(monday.iso_week() <= friday.iso_week());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iso_week_ordering_for_last_week() {
|
||||
let monday = NaiveDate::from_ymd_opt(2026, 12, 28).unwrap();
|
||||
let friday = NaiveDate::from_ymd_opt(2027, 1, 1).unwrap();
|
||||
|
||||
assert!(monday.iso_week() >= friday.iso_week());
|
||||
assert!(monday.iso_week() <= friday.iso_week());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "rkyv-validation")]
|
||||
fn test_rkyv_validation() {
|
||||
let minweek = NaiveDate::MIN.iso_week();
|
||||
let bytes = rkyv::to_bytes::<_, 4>(&minweek).unwrap();
|
||||
assert_eq!(rkyv::from_bytes::<IsoWeek>(&bytes).unwrap(), minweek);
|
||||
|
||||
let maxweek = NaiveDate::MAX.iso_week();
|
||||
let bytes = rkyv::to_bytes::<_, 4>(&maxweek).unwrap();
|
||||
assert_eq!(rkyv::from_bytes::<IsoWeek>(&bytes).unwrap(), maxweek);
|
||||
}
|
||||
}
|
||||
|
||||
281
third_party/rust/chrono/src/naive/mod.rs
vendored
Normal file
281
third_party/rust/chrono/src/naive/mod.rs
vendored
Normal file
@@ -0,0 +1,281 @@
|
||||
//! Date and time types unconcerned with timezones.
|
||||
//!
|
||||
//! They are primarily building blocks for other types
|
||||
//! (e.g. [`TimeZone`](../offset/trait.TimeZone.html)),
|
||||
//! but can be also used for the simpler date and time handling.
|
||||
|
||||
use core::ops::RangeInclusive;
|
||||
|
||||
use crate::Weekday;
|
||||
use crate::expect;
|
||||
|
||||
pub(crate) mod date;
|
||||
pub(crate) mod datetime;
|
||||
mod internals;
|
||||
pub(crate) mod isoweek;
|
||||
pub(crate) mod time;
|
||||
|
||||
#[allow(deprecated)]
|
||||
pub use self::date::{MAX_DATE, MIN_DATE};
|
||||
pub use self::date::{NaiveDate, NaiveDateDaysIterator, NaiveDateWeeksIterator};
|
||||
#[allow(deprecated)]
|
||||
pub use self::datetime::{MAX_DATETIME, MIN_DATETIME, NaiveDateTime};
|
||||
pub use self::isoweek::IsoWeek;
|
||||
pub use self::time::NaiveTime;
|
||||
|
||||
#[cfg(feature = "__internal_bench")]
|
||||
#[doc(hidden)]
|
||||
pub use self::internals::YearFlags as __BenchYearFlags;
|
||||
|
||||
/// A week represented by a [`NaiveDate`] and a [`Weekday`] which is the first
|
||||
/// day of the week.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct NaiveWeek {
|
||||
date: NaiveDate,
|
||||
start: Weekday,
|
||||
}
|
||||
|
||||
impl NaiveWeek {
|
||||
/// Create a new `NaiveWeek`
|
||||
pub(crate) const fn new(date: NaiveDate, start: Weekday) -> Self {
|
||||
Self { date, start }
|
||||
}
|
||||
|
||||
/// Returns a date representing the first day of the week.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the first day of the week happens to fall just out of range of `NaiveDate`
|
||||
/// (more than ca. 262,000 years away from common era).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use chrono::{NaiveDate, Weekday};
|
||||
///
|
||||
/// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap();
|
||||
/// let week = date.week(Weekday::Mon);
|
||||
/// assert!(week.first_day() <= date);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn first_day(&self) -> NaiveDate {
|
||||
expect(self.checked_first_day(), "first weekday out of range for `NaiveDate`")
|
||||
}
|
||||
|
||||
/// Returns a date representing the first day of the week or
|
||||
/// `None` if the date is out of `NaiveDate`'s range
|
||||
/// (more than ca. 262,000 years away from common era).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use chrono::{NaiveDate, Weekday};
|
||||
///
|
||||
/// let date = NaiveDate::MIN;
|
||||
/// let week = date.week(Weekday::Mon);
|
||||
/// if let Some(first_day) = week.checked_first_day() {
|
||||
/// assert!(first_day == date);
|
||||
/// } else {
|
||||
/// // error handling code
|
||||
/// return;
|
||||
/// };
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn checked_first_day(&self) -> Option<NaiveDate> {
|
||||
let start = self.start.num_days_from_monday() as i32;
|
||||
let ref_day = self.date.weekday().num_days_from_monday() as i32;
|
||||
// Calculate the number of days to subtract from `self.date`.
|
||||
// Do not construct an intermediate date beyond `self.date`, because that may be out of
|
||||
// range if `date` is close to `NaiveDate::MAX`.
|
||||
let days = start - ref_day - if start > ref_day { 7 } else { 0 };
|
||||
self.date.add_days(days)
|
||||
}
|
||||
|
||||
/// Returns a date representing the last day of the week.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the last day of the week happens to fall just out of range of `NaiveDate`
|
||||
/// (more than ca. 262,000 years away from common era).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use chrono::{NaiveDate, Weekday};
|
||||
///
|
||||
/// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap();
|
||||
/// let week = date.week(Weekday::Mon);
|
||||
/// assert!(week.last_day() >= date);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn last_day(&self) -> NaiveDate {
|
||||
expect(self.checked_last_day(), "last weekday out of range for `NaiveDate`")
|
||||
}
|
||||
|
||||
/// Returns a date representing the last day of the week or
|
||||
/// `None` if the date is out of `NaiveDate`'s range
|
||||
/// (more than ca. 262,000 years away from common era).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use chrono::{NaiveDate, Weekday};
|
||||
///
|
||||
/// let date = NaiveDate::MAX;
|
||||
/// let week = date.week(Weekday::Mon);
|
||||
/// if let Some(last_day) = week.checked_last_day() {
|
||||
/// assert!(last_day == date);
|
||||
/// } else {
|
||||
/// // error handling code
|
||||
/// return;
|
||||
/// };
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn checked_last_day(&self) -> Option<NaiveDate> {
|
||||
let end = self.start.pred().num_days_from_monday() as i32;
|
||||
let ref_day = self.date.weekday().num_days_from_monday() as i32;
|
||||
// Calculate the number of days to add to `self.date`.
|
||||
// Do not construct an intermediate date before `self.date` (like with `first_day()`),
|
||||
// because that may be out of range if `date` is close to `NaiveDate::MIN`.
|
||||
let days = end - ref_day + if end < ref_day { 7 } else { 0 };
|
||||
self.date.add_days(days)
|
||||
}
|
||||
|
||||
/// Returns a [`RangeInclusive<T>`] representing the whole week bounded by
|
||||
/// [first_day](NaiveWeek::first_day) and [last_day](NaiveWeek::last_day) functions.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the either the first or last day of the week happens to fall just out of range of
|
||||
/// `NaiveDate` (more than ca. 262,000 years away from common era).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use chrono::{NaiveDate, Weekday};
|
||||
///
|
||||
/// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap();
|
||||
/// let week = date.week(Weekday::Mon);
|
||||
/// let days = week.days();
|
||||
/// assert!(days.contains(&date));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn days(&self) -> RangeInclusive<NaiveDate> {
|
||||
// `expect` doesn't work because `RangeInclusive` is not `Copy`
|
||||
match self.checked_days() {
|
||||
Some(val) => val,
|
||||
None => panic!("{}", "first or last weekday is out of range for `NaiveDate`"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an [`Option<RangeInclusive<T>>`] representing the whole week bounded by
|
||||
/// [checked_first_day](NaiveWeek::checked_first_day) and
|
||||
/// [checked_last_day](NaiveWeek::checked_last_day) functions.
|
||||
///
|
||||
/// Returns `None` if either of the boundaries are out of `NaiveDate`'s range
|
||||
/// (more than ca. 262,000 years away from common era).
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use chrono::{NaiveDate, Weekday};
|
||||
///
|
||||
/// let date = NaiveDate::MAX;
|
||||
/// let week = date.week(Weekday::Mon);
|
||||
/// let _days = match week.checked_days() {
|
||||
/// Some(d) => d,
|
||||
/// None => {
|
||||
/// // error handling code
|
||||
/// return;
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn checked_days(&self) -> Option<RangeInclusive<NaiveDate>> {
|
||||
match (self.checked_first_day(), self.checked_last_day()) {
|
||||
(Some(first), Some(last)) => Some(first..=last),
|
||||
(_, _) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A duration in calendar days.
|
||||
///
|
||||
/// This is useful because when using `TimeDelta` it is possible that adding `TimeDelta::days(1)`
|
||||
/// doesn't increment the day value as expected due to it being a fixed number of seconds. This
|
||||
/// difference applies only when dealing with `DateTime<TimeZone>` data types and in other cases
|
||||
/// `TimeDelta::days(n)` and `Days::new(n)` are equivalent.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||
pub struct Days(pub(crate) u64);
|
||||
|
||||
impl Days {
|
||||
/// Construct a new `Days` from a number of days
|
||||
pub const fn new(num: u64) -> Self {
|
||||
Self(num)
|
||||
}
|
||||
}
|
||||
|
||||
/// Serialization/Deserialization of `NaiveDateTime` in alternate formats
|
||||
///
|
||||
/// The various modules in here are intended to be used with serde's [`with` annotation] to
|
||||
/// serialize as something other than the default ISO 8601 format.
|
||||
///
|
||||
/// [`with` annotation]: https://serde.rs/field-attrs.html#with
|
||||
#[cfg(feature = "serde")]
|
||||
pub mod serde {
|
||||
pub use super::datetime::serde::*;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{NaiveDate, Weekday};
|
||||
#[test]
|
||||
fn test_naiveweek() {
|
||||
let date = NaiveDate::from_ymd_opt(2022, 5, 18).unwrap();
|
||||
let asserts = [
|
||||
(Weekday::Mon, "Mon 2022-05-16", "Sun 2022-05-22"),
|
||||
(Weekday::Tue, "Tue 2022-05-17", "Mon 2022-05-23"),
|
||||
(Weekday::Wed, "Wed 2022-05-18", "Tue 2022-05-24"),
|
||||
(Weekday::Thu, "Thu 2022-05-12", "Wed 2022-05-18"),
|
||||
(Weekday::Fri, "Fri 2022-05-13", "Thu 2022-05-19"),
|
||||
(Weekday::Sat, "Sat 2022-05-14", "Fri 2022-05-20"),
|
||||
(Weekday::Sun, "Sun 2022-05-15", "Sat 2022-05-21"),
|
||||
];
|
||||
for (start, first_day, last_day) in asserts {
|
||||
let week = date.week(start);
|
||||
let days = week.days();
|
||||
assert_eq!(Ok(week.first_day()), NaiveDate::parse_from_str(first_day, "%a %Y-%m-%d"));
|
||||
assert_eq!(Ok(week.last_day()), NaiveDate::parse_from_str(last_day, "%a %Y-%m-%d"));
|
||||
assert!(days.contains(&date));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_naiveweek_min_max() {
|
||||
let date_max = NaiveDate::MAX;
|
||||
assert!(date_max.week(Weekday::Mon).first_day() <= date_max);
|
||||
let date_min = NaiveDate::MIN;
|
||||
assert!(date_min.week(Weekday::Mon).last_day() >= date_min);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_naiveweek_checked_no_panic() {
|
||||
let date_max = NaiveDate::MAX;
|
||||
if let Some(last) = date_max.week(Weekday::Mon).checked_last_day() {
|
||||
assert!(last == date_max);
|
||||
}
|
||||
let date_min = NaiveDate::MIN;
|
||||
if let Some(first) = date_min.week(Weekday::Mon).checked_first_day() {
|
||||
assert!(first == date_min);
|
||||
}
|
||||
let _ = date_min.week(Weekday::Mon).checked_days();
|
||||
let _ = date_max.week(Weekday::Mon).checked_days();
|
||||
}
|
||||
}
|
||||
1814
third_party/rust/chrono/src/naive/time.rs
vendored
1814
third_party/rust/chrono/src/naive/time.rs
vendored
File diff suppressed because it is too large
Load Diff
1643
third_party/rust/chrono/src/naive/time/mod.rs
vendored
Normal file
1643
third_party/rust/chrono/src/naive/time/mod.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
143
third_party/rust/chrono/src/naive/time/serde.rs
vendored
Normal file
143
third_party/rust/chrono/src/naive/time/serde.rs
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
use super::NaiveTime;
|
||||
use core::fmt;
|
||||
use serde::{de, ser};
|
||||
|
||||
// TODO not very optimized for space (binary formats would want something better)
|
||||
// TODO round-trip for general leap seconds (not just those with second = 60)
|
||||
|
||||
impl ser::Serialize for NaiveTime {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ser::Serializer,
|
||||
{
|
||||
serializer.collect_str(&self)
|
||||
}
|
||||
}
|
||||
|
||||
struct NaiveTimeVisitor;
|
||||
|
||||
impl de::Visitor<'_> for NaiveTimeVisitor {
|
||||
type Value = NaiveTime;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a formatted time string")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
value.parse().map_err(E::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> de::Deserialize<'de> for NaiveTime {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_str(NaiveTimeVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::NaiveTime;
|
||||
|
||||
#[test]
|
||||
fn test_serde_serialize() {
|
||||
assert_eq!(
|
||||
serde_json::to_string(&NaiveTime::from_hms_opt(0, 0, 0).unwrap()).ok(),
|
||||
Some(r#""00:00:00""#.into())
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_string(&NaiveTime::from_hms_milli_opt(0, 0, 0, 950).unwrap()).ok(),
|
||||
Some(r#""00:00:00.950""#.into())
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_string(&NaiveTime::from_hms_milli_opt(0, 0, 59, 1_000).unwrap()).ok(),
|
||||
Some(r#""00:00:60""#.into())
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_string(&NaiveTime::from_hms_opt(0, 1, 2).unwrap()).ok(),
|
||||
Some(r#""00:01:02""#.into())
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_string(&NaiveTime::from_hms_nano_opt(3, 5, 7, 98765432).unwrap()).ok(),
|
||||
Some(r#""03:05:07.098765432""#.into())
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_string(&NaiveTime::from_hms_opt(7, 8, 9).unwrap()).ok(),
|
||||
Some(r#""07:08:09""#.into())
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_string(&NaiveTime::from_hms_micro_opt(12, 34, 56, 789).unwrap()).ok(),
|
||||
Some(r#""12:34:56.000789""#.into())
|
||||
);
|
||||
let leap = NaiveTime::from_hms_nano_opt(23, 59, 59, 1_999_999_999).unwrap();
|
||||
assert_eq!(serde_json::to_string(&leap).ok(), Some(r#""23:59:60.999999999""#.into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serde_deserialize() {
|
||||
let from_str = serde_json::from_str::<NaiveTime>;
|
||||
|
||||
assert_eq!(from_str(r#""00:00:00""#).ok(), Some(NaiveTime::from_hms_opt(0, 0, 0).unwrap()));
|
||||
assert_eq!(from_str(r#""0:0:0""#).ok(), Some(NaiveTime::from_hms_opt(0, 0, 0).unwrap()));
|
||||
assert_eq!(
|
||||
from_str(r#""00:00:00.950""#).ok(),
|
||||
Some(NaiveTime::from_hms_milli_opt(0, 0, 0, 950).unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
from_str(r#""0:0:0.95""#).ok(),
|
||||
Some(NaiveTime::from_hms_milli_opt(0, 0, 0, 950).unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
from_str(r#""00:00:60""#).ok(),
|
||||
Some(NaiveTime::from_hms_milli_opt(0, 0, 59, 1_000).unwrap())
|
||||
);
|
||||
assert_eq!(from_str(r#""00:01:02""#).ok(), Some(NaiveTime::from_hms_opt(0, 1, 2).unwrap()));
|
||||
assert_eq!(
|
||||
from_str(r#""03:05:07.098765432""#).ok(),
|
||||
Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 98765432).unwrap())
|
||||
);
|
||||
assert_eq!(from_str(r#""07:08:09""#).ok(), Some(NaiveTime::from_hms_opt(7, 8, 9).unwrap()));
|
||||
assert_eq!(
|
||||
from_str(r#""12:34:56.000789""#).ok(),
|
||||
Some(NaiveTime::from_hms_micro_opt(12, 34, 56, 789).unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
from_str(r#""23:59:60.999999999""#).ok(),
|
||||
Some(NaiveTime::from_hms_nano_opt(23, 59, 59, 1_999_999_999).unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
from_str(r#""23:59:60.9999999999997""#).ok(), // excess digits are ignored
|
||||
Some(NaiveTime::from_hms_nano_opt(23, 59, 59, 1_999_999_999).unwrap())
|
||||
);
|
||||
|
||||
// bad formats
|
||||
assert!(from_str(r#""""#).is_err());
|
||||
assert!(from_str(r#""000000""#).is_err());
|
||||
assert!(from_str(r#""00:00:61""#).is_err());
|
||||
assert!(from_str(r#""00:60:00""#).is_err());
|
||||
assert!(from_str(r#""24:00:00""#).is_err());
|
||||
assert!(from_str(r#""23:59:59,1""#).is_err());
|
||||
assert!(from_str(r#""012:34:56""#).is_err());
|
||||
assert!(from_str(r#""hh:mm:ss""#).is_err());
|
||||
assert!(from_str(r#"0"#).is_err());
|
||||
assert!(from_str(r#"86399"#).is_err());
|
||||
assert!(from_str(r#"{}"#).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serde_bincode() {
|
||||
// Bincode is relevant to test separately from JSON because
|
||||
// it is not self-describing.
|
||||
use bincode::{deserialize, serialize};
|
||||
|
||||
let t = NaiveTime::from_hms_nano_opt(3, 5, 7, 98765432).unwrap();
|
||||
let encoded = serialize(&t).unwrap();
|
||||
let decoded: NaiveTime = deserialize(&encoded).unwrap();
|
||||
assert_eq!(t, decoded);
|
||||
}
|
||||
}
|
||||
393
third_party/rust/chrono/src/naive/time/tests.rs
vendored
Normal file
393
third_party/rust/chrono/src/naive/time/tests.rs
vendored
Normal file
@@ -0,0 +1,393 @@
|
||||
use super::NaiveTime;
|
||||
use crate::{FixedOffset, TimeDelta, Timelike};
|
||||
|
||||
#[test]
|
||||
fn test_time_from_hms_milli() {
|
||||
assert_eq!(
|
||||
NaiveTime::from_hms_milli_opt(3, 5, 7, 0),
|
||||
Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 0).unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
NaiveTime::from_hms_milli_opt(3, 5, 7, 777),
|
||||
Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 777_000_000).unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
NaiveTime::from_hms_milli_opt(3, 5, 59, 1_999),
|
||||
Some(NaiveTime::from_hms_nano_opt(3, 5, 59, 1_999_000_000).unwrap())
|
||||
);
|
||||
assert_eq!(NaiveTime::from_hms_milli_opt(3, 5, 59, 2_000), None);
|
||||
assert_eq!(NaiveTime::from_hms_milli_opt(3, 5, 59, 5_000), None); // overflow check
|
||||
assert_eq!(NaiveTime::from_hms_milli_opt(3, 5, 59, u32::MAX), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_time_from_hms_micro() {
|
||||
assert_eq!(
|
||||
NaiveTime::from_hms_micro_opt(3, 5, 7, 0),
|
||||
Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 0).unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
NaiveTime::from_hms_micro_opt(3, 5, 7, 333),
|
||||
Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 333_000).unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
NaiveTime::from_hms_micro_opt(3, 5, 7, 777_777),
|
||||
Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 777_777_000).unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
NaiveTime::from_hms_micro_opt(3, 5, 59, 1_999_999),
|
||||
Some(NaiveTime::from_hms_nano_opt(3, 5, 59, 1_999_999_000).unwrap())
|
||||
);
|
||||
assert_eq!(NaiveTime::from_hms_micro_opt(3, 5, 59, 2_000_000), None);
|
||||
assert_eq!(NaiveTime::from_hms_micro_opt(3, 5, 59, 5_000_000), None); // overflow check
|
||||
assert_eq!(NaiveTime::from_hms_micro_opt(3, 5, 59, u32::MAX), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_time_hms() {
|
||||
assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().hour(), 3);
|
||||
assert_eq!(
|
||||
NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_hour(0),
|
||||
Some(NaiveTime::from_hms_opt(0, 5, 7).unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_hour(23),
|
||||
Some(NaiveTime::from_hms_opt(23, 5, 7).unwrap())
|
||||
);
|
||||
assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_hour(24), None);
|
||||
assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_hour(u32::MAX), None);
|
||||
|
||||
assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().minute(), 5);
|
||||
assert_eq!(
|
||||
NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_minute(0),
|
||||
Some(NaiveTime::from_hms_opt(3, 0, 7).unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_minute(59),
|
||||
Some(NaiveTime::from_hms_opt(3, 59, 7).unwrap())
|
||||
);
|
||||
assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_minute(60), None);
|
||||
assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_minute(u32::MAX), None);
|
||||
|
||||
assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().second(), 7);
|
||||
assert_eq!(
|
||||
NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_second(0),
|
||||
Some(NaiveTime::from_hms_opt(3, 5, 0).unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_second(59),
|
||||
Some(NaiveTime::from_hms_opt(3, 5, 59).unwrap())
|
||||
);
|
||||
assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_second(60), None);
|
||||
assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_second(u32::MAX), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_time_add() {
|
||||
macro_rules! check {
|
||||
($lhs:expr, $rhs:expr, $sum:expr) => {{
|
||||
assert_eq!($lhs + $rhs, $sum);
|
||||
//assert_eq!($rhs + $lhs, $sum);
|
||||
}};
|
||||
}
|
||||
|
||||
let hmsm = |h, m, s, ms| NaiveTime::from_hms_milli_opt(h, m, s, ms).unwrap();
|
||||
|
||||
check!(hmsm(3, 5, 59, 900), TimeDelta::zero(), hmsm(3, 5, 59, 900));
|
||||
check!(hmsm(3, 5, 59, 900), TimeDelta::try_milliseconds(100).unwrap(), hmsm(3, 6, 0, 0));
|
||||
check!(hmsm(3, 5, 59, 1_300), TimeDelta::try_milliseconds(-1800).unwrap(), hmsm(3, 5, 58, 500));
|
||||
check!(hmsm(3, 5, 59, 1_300), TimeDelta::try_milliseconds(-800).unwrap(), hmsm(3, 5, 59, 500));
|
||||
check!(
|
||||
hmsm(3, 5, 59, 1_300),
|
||||
TimeDelta::try_milliseconds(-100).unwrap(),
|
||||
hmsm(3, 5, 59, 1_200)
|
||||
);
|
||||
check!(hmsm(3, 5, 59, 1_300), TimeDelta::try_milliseconds(100).unwrap(), hmsm(3, 5, 59, 1_400));
|
||||
check!(hmsm(3, 5, 59, 1_300), TimeDelta::try_milliseconds(800).unwrap(), hmsm(3, 6, 0, 100));
|
||||
check!(hmsm(3, 5, 59, 1_300), TimeDelta::try_milliseconds(1800).unwrap(), hmsm(3, 6, 1, 100));
|
||||
check!(hmsm(3, 5, 59, 900), TimeDelta::try_seconds(86399).unwrap(), hmsm(3, 5, 58, 900)); // overwrap
|
||||
check!(hmsm(3, 5, 59, 900), TimeDelta::try_seconds(-86399).unwrap(), hmsm(3, 6, 0, 900));
|
||||
check!(hmsm(3, 5, 59, 900), TimeDelta::try_days(12345).unwrap(), hmsm(3, 5, 59, 900));
|
||||
check!(hmsm(3, 5, 59, 1_300), TimeDelta::try_days(1).unwrap(), hmsm(3, 5, 59, 300));
|
||||
check!(hmsm(3, 5, 59, 1_300), TimeDelta::try_days(-1).unwrap(), hmsm(3, 6, 0, 300));
|
||||
|
||||
// regression tests for #37
|
||||
check!(hmsm(0, 0, 0, 0), TimeDelta::try_milliseconds(-990).unwrap(), hmsm(23, 59, 59, 10));
|
||||
check!(hmsm(0, 0, 0, 0), TimeDelta::try_milliseconds(-9990).unwrap(), hmsm(23, 59, 50, 10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_time_overflowing_add() {
|
||||
let hmsm = |h, m, s, ms| NaiveTime::from_hms_milli_opt(h, m, s, ms).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
hmsm(3, 4, 5, 678).overflowing_add_signed(TimeDelta::try_hours(11).unwrap()),
|
||||
(hmsm(14, 4, 5, 678), 0)
|
||||
);
|
||||
assert_eq!(
|
||||
hmsm(3, 4, 5, 678).overflowing_add_signed(TimeDelta::try_hours(23).unwrap()),
|
||||
(hmsm(2, 4, 5, 678), 86_400)
|
||||
);
|
||||
assert_eq!(
|
||||
hmsm(3, 4, 5, 678).overflowing_add_signed(TimeDelta::try_hours(-7).unwrap()),
|
||||
(hmsm(20, 4, 5, 678), -86_400)
|
||||
);
|
||||
|
||||
// overflowing_add_signed with leap seconds may be counter-intuitive
|
||||
assert_eq!(
|
||||
hmsm(3, 4, 59, 1_678).overflowing_add_signed(TimeDelta::try_days(1).unwrap()),
|
||||
(hmsm(3, 4, 59, 678), 86_400)
|
||||
);
|
||||
assert_eq!(
|
||||
hmsm(3, 4, 59, 1_678).overflowing_add_signed(TimeDelta::try_days(-1).unwrap()),
|
||||
(hmsm(3, 5, 0, 678), -86_400)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_time_addassignment() {
|
||||
let hms = |h, m, s| NaiveTime::from_hms_opt(h, m, s).unwrap();
|
||||
let mut time = hms(12, 12, 12);
|
||||
time += TimeDelta::try_hours(10).unwrap();
|
||||
assert_eq!(time, hms(22, 12, 12));
|
||||
time += TimeDelta::try_hours(10).unwrap();
|
||||
assert_eq!(time, hms(8, 12, 12));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_time_subassignment() {
|
||||
let hms = |h, m, s| NaiveTime::from_hms_opt(h, m, s).unwrap();
|
||||
let mut time = hms(12, 12, 12);
|
||||
time -= TimeDelta::try_hours(10).unwrap();
|
||||
assert_eq!(time, hms(2, 12, 12));
|
||||
time -= TimeDelta::try_hours(10).unwrap();
|
||||
assert_eq!(time, hms(16, 12, 12));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_time_sub() {
|
||||
macro_rules! check {
|
||||
($lhs:expr, $rhs:expr, $diff:expr) => {{
|
||||
// `time1 - time2 = duration` is equivalent to `time2 - time1 = -duration`
|
||||
assert_eq!($lhs.signed_duration_since($rhs), $diff);
|
||||
assert_eq!($rhs.signed_duration_since($lhs), -$diff);
|
||||
}};
|
||||
}
|
||||
|
||||
let hmsm = |h, m, s, ms| NaiveTime::from_hms_milli_opt(h, m, s, ms).unwrap();
|
||||
|
||||
check!(hmsm(3, 5, 7, 900), hmsm(3, 5, 7, 900), TimeDelta::zero());
|
||||
check!(hmsm(3, 5, 7, 900), hmsm(3, 5, 7, 600), TimeDelta::try_milliseconds(300).unwrap());
|
||||
check!(hmsm(3, 5, 7, 200), hmsm(2, 4, 6, 200), TimeDelta::try_seconds(3600 + 60 + 1).unwrap());
|
||||
check!(
|
||||
hmsm(3, 5, 7, 200),
|
||||
hmsm(2, 4, 6, 300),
|
||||
TimeDelta::try_seconds(3600 + 60).unwrap() + TimeDelta::try_milliseconds(900).unwrap()
|
||||
);
|
||||
|
||||
// treats the leap second as if it coincides with the prior non-leap second,
|
||||
// as required by `time1 - time2 = duration` and `time2 - time1 = -duration` equivalence.
|
||||
check!(hmsm(3, 6, 0, 200), hmsm(3, 5, 59, 1_800), TimeDelta::try_milliseconds(400).unwrap());
|
||||
//check!(hmsm(3, 5, 7, 1_200), hmsm(3, 5, 6, 1_800), TimeDelta::try_milliseconds(1400).unwrap());
|
||||
//check!(hmsm(3, 5, 7, 1_200), hmsm(3, 5, 6, 800), TimeDelta::try_milliseconds(1400).unwrap());
|
||||
|
||||
// additional equality: `time1 + duration = time2` is equivalent to
|
||||
// `time2 - time1 = duration` IF AND ONLY IF `time2` represents a non-leap second.
|
||||
assert_eq!(hmsm(3, 5, 6, 800) + TimeDelta::try_milliseconds(400).unwrap(), hmsm(3, 5, 7, 200));
|
||||
//assert_eq!(hmsm(3, 5, 6, 1_800) + TimeDelta::try_milliseconds(400).unwrap(), hmsm(3, 5, 7, 200));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_core_duration_ops() {
|
||||
use core::time::Duration;
|
||||
|
||||
let mut t = NaiveTime::from_hms_opt(11, 34, 23).unwrap();
|
||||
let same = t + Duration::ZERO;
|
||||
assert_eq!(t, same);
|
||||
|
||||
t += Duration::new(3600, 0);
|
||||
assert_eq!(t, NaiveTime::from_hms_opt(12, 34, 23).unwrap());
|
||||
|
||||
t -= Duration::new(7200, 0);
|
||||
assert_eq!(t, NaiveTime::from_hms_opt(10, 34, 23).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_time_fmt() {
|
||||
assert_eq!(
|
||||
format!("{}", NaiveTime::from_hms_milli_opt(23, 59, 59, 999).unwrap()),
|
||||
"23:59:59.999"
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{}", NaiveTime::from_hms_milli_opt(23, 59, 59, 1_000).unwrap()),
|
||||
"23:59:60"
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{}", NaiveTime::from_hms_milli_opt(23, 59, 59, 1_001).unwrap()),
|
||||
"23:59:60.001"
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{}", NaiveTime::from_hms_micro_opt(0, 0, 0, 43210).unwrap()),
|
||||
"00:00:00.043210"
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{}", NaiveTime::from_hms_nano_opt(0, 0, 0, 6543210).unwrap()),
|
||||
"00:00:00.006543210"
|
||||
);
|
||||
|
||||
// the format specifier should have no effect on `NaiveTime`
|
||||
assert_eq!(
|
||||
format!("{:30}", NaiveTime::from_hms_milli_opt(3, 5, 7, 9).unwrap()),
|
||||
"03:05:07.009"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_time_from_str() {
|
||||
// valid cases
|
||||
let valid = [
|
||||
"0:0:0",
|
||||
"0:0:0.0000000",
|
||||
"0:0:0.0000003",
|
||||
" 4 : 3 : 2.1 ",
|
||||
" 09:08:07 ",
|
||||
" 09:08 ",
|
||||
" 9:8:07 ",
|
||||
"01:02:03",
|
||||
"4:3:2.1",
|
||||
"9:8:7",
|
||||
"09:8:7",
|
||||
"9:08:7",
|
||||
"9:8:07",
|
||||
"09:08:7",
|
||||
"09:8:07",
|
||||
"09:08:7",
|
||||
"9:08:07",
|
||||
"09:08:07",
|
||||
"9:8:07.123",
|
||||
"9:08:7.123",
|
||||
"09:8:7.123",
|
||||
"09:08:7.123",
|
||||
"9:08:07.123",
|
||||
"09:8:07.123",
|
||||
"09:08:07.123",
|
||||
"09:08:07.123",
|
||||
"09:08:07.1234",
|
||||
"09:08:07.12345",
|
||||
"09:08:07.123456",
|
||||
"09:08:07.1234567",
|
||||
"09:08:07.12345678",
|
||||
"09:08:07.123456789",
|
||||
"09:08:07.1234567891",
|
||||
"09:08:07.12345678912",
|
||||
"23:59:60.373929310237",
|
||||
];
|
||||
for &s in &valid {
|
||||
eprintln!("test_time_parse_from_str valid {:?}", s);
|
||||
let d = match s.parse::<NaiveTime>() {
|
||||
Ok(d) => d,
|
||||
Err(e) => panic!("parsing `{}` has failed: {}", s, e),
|
||||
};
|
||||
let s_ = format!("{:?}", d);
|
||||
// `s` and `s_` may differ, but `s.parse()` and `s_.parse()` must be same
|
||||
let d_ = match s_.parse::<NaiveTime>() {
|
||||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
panic!("`{}` is parsed into `{:?}`, but reparsing that has failed: {}", s, d, e)
|
||||
}
|
||||
};
|
||||
assert!(
|
||||
d == d_,
|
||||
"`{}` is parsed into `{:?}`, but reparsed result \
|
||||
`{:?}` does not match",
|
||||
s,
|
||||
d,
|
||||
d_
|
||||
);
|
||||
}
|
||||
|
||||
// some invalid cases
|
||||
// since `ParseErrorKind` is private, all we can do is to check if there was an error
|
||||
let invalid = [
|
||||
"", // empty
|
||||
"x", // invalid
|
||||
"15", // missing data
|
||||
"15:8:", // trailing colon
|
||||
"15:8:x", // invalid data
|
||||
"15:8:9x", // invalid data
|
||||
"23:59:61", // invalid second (out of bounds)
|
||||
"23:54:35 GMT", // invalid (timezone non-sensical for NaiveTime)
|
||||
"23:54:35 +0000", // invalid (timezone non-sensical for NaiveTime)
|
||||
"1441497364.649", // valid datetime, not a NaiveTime
|
||||
"+1441497364.649", // valid datetime, not a NaiveTime
|
||||
"+1441497364", // valid datetime, not a NaiveTime
|
||||
"001:02:03", // invalid hour
|
||||
"01:002:03", // invalid minute
|
||||
"01:02:003", // invalid second
|
||||
"12:34:56.x", // invalid fraction
|
||||
"12:34:56. 0", // invalid fraction format
|
||||
"09:08:00000000007", // invalid second / invalid fraction format
|
||||
];
|
||||
for &s in &invalid {
|
||||
eprintln!("test_time_parse_from_str invalid {:?}", s);
|
||||
assert!(s.parse::<NaiveTime>().is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_time_parse_from_str() {
|
||||
let hms = |h, m, s| NaiveTime::from_hms_opt(h, m, s).unwrap();
|
||||
assert_eq!(
|
||||
NaiveTime::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"),
|
||||
Ok(hms(12, 34, 56))
|
||||
); // ignore date and offset
|
||||
assert_eq!(NaiveTime::parse_from_str("PM 12:59", "%P %H:%M"), Ok(hms(12, 59, 0)));
|
||||
assert_eq!(NaiveTime::parse_from_str("12:59 \n\t PM", "%H:%M \n\t %P"), Ok(hms(12, 59, 0)));
|
||||
assert_eq!(NaiveTime::parse_from_str("\t\t12:59\tPM\t", "\t\t%H:%M\t%P\t"), Ok(hms(12, 59, 0)));
|
||||
assert_eq!(
|
||||
NaiveTime::parse_from_str("\t\t1259\t\tPM\t", "\t\t%H%M\t\t%P\t"),
|
||||
Ok(hms(12, 59, 0))
|
||||
);
|
||||
assert!(NaiveTime::parse_from_str("12:59 PM", "%H:%M\t%P").is_ok());
|
||||
assert!(NaiveTime::parse_from_str("\t\t12:59 PM\t", "\t\t%H:%M\t%P\t").is_ok());
|
||||
assert!(NaiveTime::parse_from_str("12:59 PM", "%H:%M %P").is_ok());
|
||||
assert!(NaiveTime::parse_from_str("12:3456", "%H:%M:%S").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_overflowing_offset() {
|
||||
let hmsm = |h, m, s, n| NaiveTime::from_hms_milli_opt(h, m, s, n).unwrap();
|
||||
|
||||
let positive_offset = FixedOffset::east_opt(4 * 60 * 60).unwrap();
|
||||
// regular time
|
||||
let t = hmsm(5, 6, 7, 890);
|
||||
assert_eq!(t.overflowing_add_offset(positive_offset), (hmsm(9, 6, 7, 890), 0));
|
||||
assert_eq!(t.overflowing_sub_offset(positive_offset), (hmsm(1, 6, 7, 890), 0));
|
||||
// leap second is preserved, and wrap to next day
|
||||
let t = hmsm(23, 59, 59, 1_000);
|
||||
assert_eq!(t.overflowing_add_offset(positive_offset), (hmsm(3, 59, 59, 1_000), 1));
|
||||
assert_eq!(t.overflowing_sub_offset(positive_offset), (hmsm(19, 59, 59, 1_000), 0));
|
||||
// wrap to previous day
|
||||
let t = hmsm(1, 2, 3, 456);
|
||||
assert_eq!(t.overflowing_sub_offset(positive_offset), (hmsm(21, 2, 3, 456), -1));
|
||||
// an odd offset
|
||||
let negative_offset = FixedOffset::west_opt(((2 * 60) + 3) * 60 + 4).unwrap();
|
||||
let t = hmsm(5, 6, 7, 890);
|
||||
assert_eq!(t.overflowing_add_offset(negative_offset), (hmsm(3, 3, 3, 890), 0));
|
||||
assert_eq!(t.overflowing_sub_offset(negative_offset), (hmsm(7, 9, 11, 890), 0));
|
||||
|
||||
assert_eq!(t.overflowing_add_offset(positive_offset).0, t + positive_offset);
|
||||
assert_eq!(t.overflowing_sub_offset(positive_offset).0, t - positive_offset);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "rkyv-validation")]
|
||||
fn test_rkyv_validation() {
|
||||
let t_min = NaiveTime::MIN;
|
||||
let bytes = rkyv::to_bytes::<_, 8>(&t_min).unwrap();
|
||||
assert_eq!(rkyv::from_bytes::<NaiveTime>(&bytes).unwrap(), t_min);
|
||||
|
||||
let t_max = NaiveTime::MAX;
|
||||
let bytes = rkyv::to_bytes::<_, 8>(&t_max).unwrap();
|
||||
assert_eq!(rkyv::from_bytes::<NaiveTime>(&bytes).unwrap(), t_max);
|
||||
}
|
||||
220
third_party/rust/chrono/src/offset/fixed.rs
vendored
220
third_party/rust/chrono/src/offset/fixed.rs
vendored
@@ -4,22 +4,29 @@
|
||||
//! The time zone which has a fixed offset from UTC.
|
||||
|
||||
use core::fmt;
|
||||
use core::ops::{Add, Sub};
|
||||
use oldtime::Duration as OldDuration;
|
||||
use core::str::FromStr;
|
||||
|
||||
use super::{LocalResult, Offset, TimeZone};
|
||||
use div::div_mod_floor;
|
||||
use naive::{NaiveDate, NaiveDateTime, NaiveTime};
|
||||
use DateTime;
|
||||
use Timelike;
|
||||
#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))]
|
||||
use rkyv::{Archive, Deserialize, Serialize};
|
||||
|
||||
use super::{MappedLocalTime, Offset, TimeZone};
|
||||
use crate::format::{OUT_OF_RANGE, ParseError, scan};
|
||||
use crate::naive::{NaiveDate, NaiveDateTime};
|
||||
|
||||
/// The time zone with fixed offset, from UTC-23:59:59 to UTC+23:59:59.
|
||||
///
|
||||
/// Using the [`TimeZone`](./trait.TimeZone.html) methods
|
||||
/// on a `FixedOffset` struct is the preferred way to construct
|
||||
/// `DateTime<FixedOffset>` instances. See the [`east`](#method.east) and
|
||||
/// [`west`](#method.west) methods for examples.
|
||||
/// `DateTime<FixedOffset>` instances. See the [`east_opt`](#method.east_opt) and
|
||||
/// [`west_opt`](#method.west_opt) methods for examples.
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
|
||||
#[cfg_attr(
|
||||
any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"),
|
||||
derive(Archive, Deserialize, Serialize),
|
||||
archive(compare(PartialEq)),
|
||||
archive_attr(derive(Clone, Copy, PartialEq, Eq, Hash, Debug))
|
||||
)]
|
||||
#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
|
||||
pub struct FixedOffset {
|
||||
local_minus_utc: i32,
|
||||
}
|
||||
@@ -29,16 +36,8 @@ impl FixedOffset {
|
||||
/// The negative `secs` means the Western Hemisphere.
|
||||
///
|
||||
/// Panics on the out-of-bound `secs`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{FixedOffset, TimeZone};
|
||||
/// let hour = 3600;
|
||||
/// let datetime = FixedOffset::east(5 * hour).ymd(2016, 11, 08)
|
||||
/// .and_hms(0, 0, 0);
|
||||
/// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00+05:00")
|
||||
/// ~~~~
|
||||
#[deprecated(since = "0.4.23", note = "use `east_opt()` instead")]
|
||||
#[must_use]
|
||||
pub fn east(secs: i32) -> FixedOffset {
|
||||
FixedOffset::east_opt(secs).expect("FixedOffset::east out of bounds")
|
||||
}
|
||||
@@ -47,7 +46,20 @@ impl FixedOffset {
|
||||
/// The negative `secs` means the Western Hemisphere.
|
||||
///
|
||||
/// Returns `None` on the out-of-bound `secs`.
|
||||
pub fn east_opt(secs: i32) -> Option<FixedOffset> {
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "alloc")] {
|
||||
/// use chrono::{FixedOffset, TimeZone};
|
||||
/// let hour = 3600;
|
||||
/// let datetime =
|
||||
/// FixedOffset::east_opt(5 * hour).unwrap().with_ymd_and_hms(2016, 11, 08, 0, 0, 0).unwrap();
|
||||
/// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00+05:00")
|
||||
/// # }
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub const fn east_opt(secs: i32) -> Option<FixedOffset> {
|
||||
if -86_400 < secs && secs < 86_400 {
|
||||
Some(FixedOffset { local_minus_utc: secs })
|
||||
} else {
|
||||
@@ -59,16 +71,8 @@ impl FixedOffset {
|
||||
/// The negative `secs` means the Eastern Hemisphere.
|
||||
///
|
||||
/// Panics on the out-of-bound `secs`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{FixedOffset, TimeZone};
|
||||
/// let hour = 3600;
|
||||
/// let datetime = FixedOffset::west(5 * hour).ymd(2016, 11, 08)
|
||||
/// .and_hms(0, 0, 0);
|
||||
/// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00-05:00")
|
||||
/// ~~~~
|
||||
#[deprecated(since = "0.4.23", note = "use `west_opt()` instead")]
|
||||
#[must_use]
|
||||
pub fn west(secs: i32) -> FixedOffset {
|
||||
FixedOffset::west_opt(secs).expect("FixedOffset::west out of bounds")
|
||||
}
|
||||
@@ -77,7 +81,20 @@ impl FixedOffset {
|
||||
/// The negative `secs` means the Eastern Hemisphere.
|
||||
///
|
||||
/// Returns `None` on the out-of-bound `secs`.
|
||||
pub fn west_opt(secs: i32) -> Option<FixedOffset> {
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "alloc")] {
|
||||
/// use chrono::{FixedOffset, TimeZone};
|
||||
/// let hour = 3600;
|
||||
/// let datetime =
|
||||
/// FixedOffset::west_opt(5 * hour).unwrap().with_ymd_and_hms(2016, 11, 08, 0, 0, 0).unwrap();
|
||||
/// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00-05:00")
|
||||
/// # }
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub const fn west_opt(secs: i32) -> Option<FixedOffset> {
|
||||
if -86_400 < secs && secs < 86_400 {
|
||||
Some(FixedOffset { local_minus_utc: -secs })
|
||||
} else {
|
||||
@@ -87,17 +104,26 @@ impl FixedOffset {
|
||||
|
||||
/// Returns the number of seconds to add to convert from UTC to the local time.
|
||||
#[inline]
|
||||
pub fn local_minus_utc(&self) -> i32 {
|
||||
pub const fn local_minus_utc(&self) -> i32 {
|
||||
self.local_minus_utc
|
||||
}
|
||||
|
||||
/// Returns the number of seconds to add to convert from the local time to UTC.
|
||||
#[inline]
|
||||
pub fn utc_minus_local(&self) -> i32 {
|
||||
pub const fn utc_minus_local(&self) -> i32 {
|
||||
-self.local_minus_utc
|
||||
}
|
||||
}
|
||||
|
||||
/// Parsing a `str` into a `FixedOffset` uses the format [`%z`](crate::format::strftime).
|
||||
impl FromStr for FixedOffset {
|
||||
type Err = ParseError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (_, offset) = scan::timezone_offset(s, scan::colon_or_space, false, false, true)?;
|
||||
Self::east_opt(offset).ok_or(OUT_OF_RANGE)
|
||||
}
|
||||
}
|
||||
|
||||
impl TimeZone for FixedOffset {
|
||||
type Offset = FixedOffset;
|
||||
|
||||
@@ -105,11 +131,11 @@ impl TimeZone for FixedOffset {
|
||||
*offset
|
||||
}
|
||||
|
||||
fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult<FixedOffset> {
|
||||
LocalResult::Single(*self)
|
||||
fn offset_from_local_date(&self, _local: &NaiveDate) -> MappedLocalTime<FixedOffset> {
|
||||
MappedLocalTime::Single(*self)
|
||||
}
|
||||
fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<FixedOffset> {
|
||||
LocalResult::Single(*self)
|
||||
fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> MappedLocalTime<FixedOffset> {
|
||||
MappedLocalTime::Single(*self)
|
||||
}
|
||||
|
||||
fn offset_from_utc_date(&self, _utc: &NaiveDate) -> FixedOffset {
|
||||
@@ -130,8 +156,10 @@ impl fmt::Debug for FixedOffset {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let offset = self.local_minus_utc;
|
||||
let (sign, offset) = if offset < 0 { ('-', -offset) } else { ('+', offset) };
|
||||
let (mins, sec) = div_mod_floor(offset, 60);
|
||||
let (hour, min) = div_mod_floor(mins, 60);
|
||||
let sec = offset.rem_euclid(60);
|
||||
let mins = offset.div_euclid(60);
|
||||
let min = mins.rem_euclid(60);
|
||||
let hour = mins.div_euclid(60);
|
||||
if sec == 0 {
|
||||
write!(f, "{}{:02}:{:02}", sign, hour, min)
|
||||
} else {
|
||||
@@ -146,99 +174,63 @@ impl fmt::Display for FixedOffset {
|
||||
}
|
||||
}
|
||||
|
||||
// addition or subtraction of FixedOffset to/from Timelike values is the same as
|
||||
// adding or subtracting the offset's local_minus_utc value
|
||||
// but keep keeps the leap second information.
|
||||
// this should be implemented more efficiently, but for the time being, this is generic right now.
|
||||
|
||||
fn add_with_leapsecond<T>(lhs: &T, rhs: i32) -> T
|
||||
where
|
||||
T: Timelike + Add<OldDuration, Output = T>,
|
||||
{
|
||||
// extract and temporarily remove the fractional part and later recover it
|
||||
let nanos = lhs.nanosecond();
|
||||
let lhs = lhs.with_nanosecond(0).unwrap();
|
||||
(lhs + OldDuration::seconds(i64::from(rhs))).with_nanosecond(nanos).unwrap()
|
||||
}
|
||||
|
||||
impl Add<FixedOffset> for NaiveTime {
|
||||
type Output = NaiveTime;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: FixedOffset) -> NaiveTime {
|
||||
add_with_leapsecond(&self, rhs.local_minus_utc)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<FixedOffset> for NaiveTime {
|
||||
type Output = NaiveTime;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: FixedOffset) -> NaiveTime {
|
||||
add_with_leapsecond(&self, -rhs.local_minus_utc)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<FixedOffset> for NaiveDateTime {
|
||||
type Output = NaiveDateTime;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: FixedOffset) -> NaiveDateTime {
|
||||
add_with_leapsecond(&self, rhs.local_minus_utc)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<FixedOffset> for NaiveDateTime {
|
||||
type Output = NaiveDateTime;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: FixedOffset) -> NaiveDateTime {
|
||||
add_with_leapsecond(&self, -rhs.local_minus_utc)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> Add<FixedOffset> for DateTime<Tz> {
|
||||
type Output = DateTime<Tz>;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: FixedOffset) -> DateTime<Tz> {
|
||||
add_with_leapsecond(&self, rhs.local_minus_utc)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> Sub<FixedOffset> for DateTime<Tz> {
|
||||
type Output = DateTime<Tz>;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: FixedOffset) -> DateTime<Tz> {
|
||||
add_with_leapsecond(&self, -rhs.local_minus_utc)
|
||||
#[cfg(all(feature = "arbitrary", feature = "std"))]
|
||||
impl arbitrary::Arbitrary<'_> for FixedOffset {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<FixedOffset> {
|
||||
let secs = u.int_in_range(-86_399..=86_399)?;
|
||||
let fixed_offset = FixedOffset::east_opt(secs)
|
||||
.expect("Could not generate a valid chrono::FixedOffset. It looks like implementation of Arbitrary for FixedOffset is erroneous.");
|
||||
Ok(fixed_offset)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FixedOffset;
|
||||
use offset::TimeZone;
|
||||
use crate::offset::TimeZone;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn test_date_extreme_offset() {
|
||||
// starting from 0.3 we don't have an offset exceeding one day.
|
||||
// this makes everything easier!
|
||||
let offset = FixedOffset::east_opt(86399).unwrap();
|
||||
assert_eq!(
|
||||
format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29)),
|
||||
"2012-02-29+23:59:59".to_string()
|
||||
format!("{:?}", offset.with_ymd_and_hms(2012, 2, 29, 5, 6, 7).unwrap()),
|
||||
"2012-02-29T05:06:07+23:59:59"
|
||||
);
|
||||
let offset = FixedOffset::east_opt(-86399).unwrap();
|
||||
assert_eq!(
|
||||
format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29).and_hms(5, 6, 7)),
|
||||
"2012-02-29T05:06:07+23:59:59".to_string()
|
||||
format!("{:?}", offset.with_ymd_and_hms(2012, 2, 29, 5, 6, 7).unwrap()),
|
||||
"2012-02-29T05:06:07-23:59:59"
|
||||
);
|
||||
let offset = FixedOffset::west_opt(86399).unwrap();
|
||||
assert_eq!(
|
||||
format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4)),
|
||||
"2012-03-04-23:59:59".to_string()
|
||||
format!("{:?}", offset.with_ymd_and_hms(2012, 3, 4, 5, 6, 7).unwrap()),
|
||||
"2012-03-04T05:06:07-23:59:59"
|
||||
);
|
||||
let offset = FixedOffset::west_opt(-86399).unwrap();
|
||||
assert_eq!(
|
||||
format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4).and_hms(5, 6, 7)),
|
||||
"2012-03-04T05:06:07-23:59:59".to_string()
|
||||
format!("{:?}", offset.with_ymd_and_hms(2012, 3, 4, 5, 6, 7).unwrap()),
|
||||
"2012-03-04T05:06:07+23:59:59"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_offset() {
|
||||
let offset = FixedOffset::from_str("-0500").unwrap();
|
||||
assert_eq!(offset.local_minus_utc, -5 * 3600);
|
||||
let offset = FixedOffset::from_str("-08:00").unwrap();
|
||||
assert_eq!(offset.local_minus_utc, -8 * 3600);
|
||||
let offset = FixedOffset::from_str("+06:30").unwrap();
|
||||
assert_eq!(offset.local_minus_utc, (6 * 3600) + 1800);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "rkyv-validation")]
|
||||
fn test_rkyv_validation() {
|
||||
let offset = FixedOffset::from_str("-0500").unwrap();
|
||||
let bytes = rkyv::to_bytes::<_, 4>(&offset).unwrap();
|
||||
assert_eq!(rkyv::from_bytes::<FixedOffset>(&bytes).unwrap(), offset);
|
||||
}
|
||||
}
|
||||
|
||||
227
third_party/rust/chrono/src/offset/local.rs
vendored
227
third_party/rust/chrono/src/offset/local.rs
vendored
@@ -1,227 +0,0 @@
|
||||
// This is a part of Chrono.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
//! The local (system) time zone.
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
|
||||
use sys::{self, Timespec};
|
||||
|
||||
use super::fixed::FixedOffset;
|
||||
use super::{LocalResult, TimeZone};
|
||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
|
||||
use naive::NaiveTime;
|
||||
use naive::{NaiveDate, NaiveDateTime};
|
||||
use {Date, DateTime};
|
||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
|
||||
use {Datelike, Timelike};
|
||||
|
||||
/// Converts a `time::Tm` struct into the timezone-aware `DateTime`.
|
||||
/// This assumes that `time` is working correctly, i.e. any error is fatal.
|
||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
|
||||
fn tm_to_datetime(mut tm: sys::Tm) -> DateTime<Local> {
|
||||
if tm.tm_sec >= 60 {
|
||||
tm.tm_nsec += (tm.tm_sec - 59) * 1_000_000_000;
|
||||
tm.tm_sec = 59;
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn tm_to_naive_date(tm: &sys::Tm) -> NaiveDate {
|
||||
// from_yo is more efficient than from_ymd (since it's the internal representation).
|
||||
NaiveDate::from_yo(tm.tm_year + 1900, tm.tm_yday as u32 + 1)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn tm_to_naive_date(tm: &sys::Tm) -> NaiveDate {
|
||||
// ...but tm_yday is broken in Windows (issue #85)
|
||||
NaiveDate::from_ymd(tm.tm_year + 1900, tm.tm_mon as u32 + 1, tm.tm_mday as u32)
|
||||
}
|
||||
|
||||
let date = tm_to_naive_date(&tm);
|
||||
let time = NaiveTime::from_hms_nano(
|
||||
tm.tm_hour as u32,
|
||||
tm.tm_min as u32,
|
||||
tm.tm_sec as u32,
|
||||
tm.tm_nsec as u32,
|
||||
);
|
||||
let offset = FixedOffset::east(tm.tm_utcoff);
|
||||
DateTime::from_utc(date.and_time(time) - offset, offset)
|
||||
}
|
||||
|
||||
/// Converts a local `NaiveDateTime` to the `time::Timespec`.
|
||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
|
||||
fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> sys::Timespec {
|
||||
// well, this exploits an undocumented `Tm::to_timespec` behavior
|
||||
// to get the exact function we want (either `timegm` or `mktime`).
|
||||
// the number 1 is arbitrary but should be non-zero to trigger `mktime`.
|
||||
let tm_utcoff = if local { 1 } else { 0 };
|
||||
|
||||
let tm = sys::Tm {
|
||||
tm_sec: d.second() as i32,
|
||||
tm_min: d.minute() as i32,
|
||||
tm_hour: d.hour() as i32,
|
||||
tm_mday: d.day() as i32,
|
||||
tm_mon: d.month0() as i32, // yes, C is that strange...
|
||||
tm_year: d.year() - 1900, // this doesn't underflow, we know that d is `NaiveDateTime`.
|
||||
tm_wday: 0, // to_local ignores this
|
||||
tm_yday: 0, // and this
|
||||
tm_isdst: -1,
|
||||
tm_utcoff: tm_utcoff,
|
||||
// do not set this, OS APIs are heavily inconsistent in terms of leap second handling
|
||||
tm_nsec: 0,
|
||||
};
|
||||
|
||||
tm.to_timespec()
|
||||
}
|
||||
|
||||
/// The local timescale. This is implemented via the standard `time` crate.
|
||||
///
|
||||
/// Using the [`TimeZone`](./trait.TimeZone.html) methods
|
||||
/// on the Local struct is the preferred way to construct `DateTime<Local>`
|
||||
/// instances.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{Local, DateTime, TimeZone};
|
||||
///
|
||||
/// let dt: DateTime<Local> = Local::now();
|
||||
/// let dt: DateTime<Local> = Local.timestamp(0, 0);
|
||||
/// ~~~~
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Local;
|
||||
|
||||
impl Local {
|
||||
/// Returns a `Date` which corresponds to the current date.
|
||||
pub fn today() -> Date<Local> {
|
||||
Local::now().date()
|
||||
}
|
||||
|
||||
/// Returns a `DateTime` which corresponds to the current date.
|
||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
|
||||
pub fn now() -> DateTime<Local> {
|
||||
tm_to_datetime(Timespec::now().local())
|
||||
}
|
||||
|
||||
/// Returns a `DateTime` which corresponds to the current date.
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
|
||||
pub fn now() -> DateTime<Local> {
|
||||
use super::Utc;
|
||||
let now: DateTime<Utc> = super::Utc::now();
|
||||
|
||||
// Workaround missing timezone logic in `time` crate
|
||||
let offset = FixedOffset::west((js_sys::Date::new_0().get_timezone_offset() as i32) * 60);
|
||||
DateTime::from_utc(now.naive_utc(), offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl TimeZone for Local {
|
||||
type Offset = FixedOffset;
|
||||
|
||||
fn from_offset(_offset: &FixedOffset) -> Local {
|
||||
Local
|
||||
}
|
||||
|
||||
// they are easier to define in terms of the finished date and time unlike other offsets
|
||||
fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<FixedOffset> {
|
||||
self.from_local_date(local).map(|date| *date.offset())
|
||||
}
|
||||
|
||||
fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<FixedOffset> {
|
||||
self.from_local_datetime(local).map(|datetime| *datetime.offset())
|
||||
}
|
||||
|
||||
fn offset_from_utc_date(&self, utc: &NaiveDate) -> FixedOffset {
|
||||
*self.from_utc_date(utc).offset()
|
||||
}
|
||||
|
||||
fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> FixedOffset {
|
||||
*self.from_utc_datetime(utc).offset()
|
||||
}
|
||||
|
||||
// override them for avoiding redundant works
|
||||
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Local>> {
|
||||
// this sounds very strange, but required for keeping `TimeZone::ymd` sane.
|
||||
// in the other words, we use the offset at the local midnight
|
||||
// but keep the actual date unaltered (much like `FixedOffset`).
|
||||
let midnight = self.from_local_datetime(&local.and_hms(0, 0, 0));
|
||||
midnight.map(|datetime| Date::from_utc(*local, *datetime.offset()))
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
|
||||
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Local>> {
|
||||
let mut local = local.clone();
|
||||
// Get the offset from the js runtime
|
||||
let offset = FixedOffset::west((js_sys::Date::new_0().get_timezone_offset() as i32) * 60);
|
||||
local -= ::Duration::seconds(offset.local_minus_utc() as i64);
|
||||
LocalResult::Single(DateTime::from_utc(local, offset))
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
|
||||
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Local>> {
|
||||
let timespec = datetime_to_timespec(local, true);
|
||||
|
||||
// datetime_to_timespec completely ignores leap seconds, so we need to adjust for them
|
||||
let mut tm = timespec.local();
|
||||
assert_eq!(tm.tm_nsec, 0);
|
||||
tm.tm_nsec = local.nanosecond() as i32;
|
||||
|
||||
LocalResult::Single(tm_to_datetime(tm))
|
||||
}
|
||||
|
||||
fn from_utc_date(&self, utc: &NaiveDate) -> Date<Local> {
|
||||
let midnight = self.from_utc_datetime(&utc.and_hms(0, 0, 0));
|
||||
Date::from_utc(*utc, *midnight.offset())
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
|
||||
fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Local> {
|
||||
// Get the offset from the js runtime
|
||||
let offset = FixedOffset::west((js_sys::Date::new_0().get_timezone_offset() as i32) * 60);
|
||||
DateTime::from_utc(*utc, offset)
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
|
||||
fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Local> {
|
||||
let timespec = datetime_to_timespec(utc, false);
|
||||
|
||||
// datetime_to_timespec completely ignores leap seconds, so we need to adjust for them
|
||||
let mut tm = timespec.local();
|
||||
assert_eq!(tm.tm_nsec, 0);
|
||||
tm.tm_nsec = utc.nanosecond() as i32;
|
||||
|
||||
tm_to_datetime(tm)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Local;
|
||||
use offset::TimeZone;
|
||||
use Datelike;
|
||||
|
||||
#[test]
|
||||
fn test_local_date_sanity_check() {
|
||||
// issue #27
|
||||
assert_eq!(Local.ymd(2999, 12, 28).day(), 28);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_leap_second() {
|
||||
// issue #123
|
||||
let today = Local::today();
|
||||
|
||||
let dt = today.and_hms_milli(1, 2, 59, 1000);
|
||||
let timestr = dt.time().to_string();
|
||||
// the OS API may or may not support the leap second,
|
||||
// but there are only two sensible options.
|
||||
assert!(timestr == "01:02:60" || timestr == "01:03:00", "unexpected timestr {:?}", timestr);
|
||||
|
||||
let dt = today.and_hms_milli(1, 2, 3, 1234);
|
||||
let timestr = dt.time().to_string();
|
||||
assert!(
|
||||
timestr == "01:02:03.234" || timestr == "01:02:04.234",
|
||||
"unexpected timestr {:?}",
|
||||
timestr
|
||||
);
|
||||
}
|
||||
}
|
||||
541
third_party/rust/chrono/src/offset/local/mod.rs
vendored
Normal file
541
third_party/rust/chrono/src/offset/local/mod.rs
vendored
Normal file
@@ -0,0 +1,541 @@
|
||||
// This is a part of Chrono.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
//! The local (system) time zone.
|
||||
|
||||
#[cfg(windows)]
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))]
|
||||
use rkyv::{Archive, Deserialize, Serialize};
|
||||
|
||||
use super::fixed::FixedOffset;
|
||||
use super::{MappedLocalTime, TimeZone};
|
||||
#[allow(deprecated)]
|
||||
use crate::Date;
|
||||
use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime};
|
||||
use crate::{DateTime, Utc};
|
||||
|
||||
#[cfg(unix)]
|
||||
#[path = "unix.rs"]
|
||||
mod inner;
|
||||
|
||||
#[cfg(windows)]
|
||||
#[path = "windows.rs"]
|
||||
mod inner;
|
||||
|
||||
#[cfg(all(windows, feature = "clock"))]
|
||||
#[allow(unreachable_pub)]
|
||||
mod win_bindings;
|
||||
|
||||
#[cfg(all(
|
||||
not(unix),
|
||||
not(windows),
|
||||
not(all(
|
||||
target_arch = "wasm32",
|
||||
feature = "wasmbind",
|
||||
not(any(target_os = "emscripten", target_os = "wasi"))
|
||||
))
|
||||
))]
|
||||
mod inner {
|
||||
use crate::{FixedOffset, MappedLocalTime, NaiveDateTime};
|
||||
|
||||
pub(super) fn offset_from_utc_datetime(
|
||||
_utc_time: &NaiveDateTime,
|
||||
) -> MappedLocalTime<FixedOffset> {
|
||||
MappedLocalTime::Single(FixedOffset::east_opt(0).unwrap())
|
||||
}
|
||||
|
||||
pub(super) fn offset_from_local_datetime(
|
||||
_local_time: &NaiveDateTime,
|
||||
) -> MappedLocalTime<FixedOffset> {
|
||||
MappedLocalTime::Single(FixedOffset::east_opt(0).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
target_arch = "wasm32",
|
||||
feature = "wasmbind",
|
||||
not(any(target_os = "emscripten", target_os = "wasi"))
|
||||
))]
|
||||
mod inner {
|
||||
use crate::{Datelike, FixedOffset, MappedLocalTime, NaiveDateTime, Timelike};
|
||||
|
||||
pub(super) fn offset_from_utc_datetime(utc: &NaiveDateTime) -> MappedLocalTime<FixedOffset> {
|
||||
let offset = js_sys::Date::from(utc.and_utc()).get_timezone_offset();
|
||||
MappedLocalTime::Single(FixedOffset::west_opt((offset as i32) * 60).unwrap())
|
||||
}
|
||||
|
||||
pub(super) fn offset_from_local_datetime(
|
||||
local: &NaiveDateTime,
|
||||
) -> MappedLocalTime<FixedOffset> {
|
||||
let mut year = local.year();
|
||||
if year < 100 {
|
||||
// The API in `js_sys` does not let us create a `Date` with negative years.
|
||||
// And values for years from `0` to `99` map to the years `1900` to `1999`.
|
||||
// Shift the value by a multiple of 400 years until it is `>= 100`.
|
||||
let shift_cycles = (year - 100).div_euclid(400);
|
||||
year -= shift_cycles * 400;
|
||||
}
|
||||
let js_date = js_sys::Date::new_with_year_month_day_hr_min_sec(
|
||||
year as u32,
|
||||
local.month0() as i32,
|
||||
local.day() as i32,
|
||||
local.hour() as i32,
|
||||
local.minute() as i32,
|
||||
local.second() as i32,
|
||||
// ignore milliseconds, our representation of leap seconds may be problematic
|
||||
);
|
||||
let offset = js_date.get_timezone_offset();
|
||||
// We always get a result, even if this time does not exist or is ambiguous.
|
||||
MappedLocalTime::Single(FixedOffset::west_opt((offset as i32) * 60).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
mod tz_info;
|
||||
|
||||
/// The local timescale.
|
||||
///
|
||||
/// Using the [`TimeZone`](./trait.TimeZone.html) methods
|
||||
/// on the Local struct is the preferred way to construct `DateTime<Local>`
|
||||
/// instances.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use chrono::{DateTime, Local, TimeZone};
|
||||
///
|
||||
/// let dt1: DateTime<Local> = Local::now();
|
||||
/// let dt2: DateTime<Local> = Local.timestamp_opt(0, 0).unwrap();
|
||||
/// assert!(dt1 >= dt2);
|
||||
/// ```
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[cfg_attr(
|
||||
any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"),
|
||||
derive(Archive, Deserialize, Serialize),
|
||||
archive(compare(PartialEq)),
|
||||
archive_attr(derive(Clone, Copy, Debug))
|
||||
)]
|
||||
#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
pub struct Local;
|
||||
|
||||
impl Local {
|
||||
/// Returns a `Date` which corresponds to the current date.
|
||||
#[deprecated(since = "0.4.23", note = "use `Local::now()` instead")]
|
||||
#[allow(deprecated)]
|
||||
#[must_use]
|
||||
pub fn today() -> Date<Local> {
|
||||
Local::now().date()
|
||||
}
|
||||
|
||||
/// Returns a `DateTime<Local>` which corresponds to the current date, time and offset from
|
||||
/// UTC.
|
||||
///
|
||||
/// See also the similar [`Utc::now()`] which returns `DateTime<Utc>`, i.e. without the local
|
||||
/// offset.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # #![allow(unused_variables)]
|
||||
/// # use chrono::{DateTime, FixedOffset, Local};
|
||||
/// // Current local time
|
||||
/// let now = Local::now();
|
||||
///
|
||||
/// // Current local date
|
||||
/// let today = now.date_naive();
|
||||
///
|
||||
/// // Current local time, converted to `DateTime<FixedOffset>`
|
||||
/// let now_fixed_offset = Local::now().fixed_offset();
|
||||
/// // or
|
||||
/// let now_fixed_offset: DateTime<FixedOffset> = Local::now().into();
|
||||
///
|
||||
/// // Current time in some timezone (let's use +05:00)
|
||||
/// // Note that it is usually more efficient to use `Utc::now` for this use case.
|
||||
/// let offset = FixedOffset::east_opt(5 * 60 * 60).unwrap();
|
||||
/// let now_with_offset = Local::now().with_timezone(&offset);
|
||||
/// ```
|
||||
pub fn now() -> DateTime<Local> {
|
||||
Utc::now().with_timezone(&Local)
|
||||
}
|
||||
}
|
||||
|
||||
impl TimeZone for Local {
|
||||
type Offset = FixedOffset;
|
||||
|
||||
fn from_offset(_offset: &FixedOffset) -> Local {
|
||||
Local
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
fn offset_from_local_date(&self, local: &NaiveDate) -> MappedLocalTime<FixedOffset> {
|
||||
// Get the offset at local midnight.
|
||||
self.offset_from_local_datetime(&local.and_time(NaiveTime::MIN))
|
||||
}
|
||||
|
||||
fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> MappedLocalTime<FixedOffset> {
|
||||
inner::offset_from_local_datetime(local)
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
fn offset_from_utc_date(&self, utc: &NaiveDate) -> FixedOffset {
|
||||
// Get the offset at midnight.
|
||||
self.offset_from_utc_datetime(&utc.and_time(NaiveTime::MIN))
|
||||
}
|
||||
|
||||
fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> FixedOffset {
|
||||
inner::offset_from_utc_datetime(utc).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
struct Transition {
|
||||
transition_utc: NaiveDateTime,
|
||||
offset_before: FixedOffset,
|
||||
offset_after: FixedOffset,
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl Transition {
|
||||
fn new(
|
||||
transition_local: NaiveDateTime,
|
||||
offset_before: FixedOffset,
|
||||
offset_after: FixedOffset,
|
||||
) -> Transition {
|
||||
// It is no problem if the transition time in UTC falls a couple of hours inside the buffer
|
||||
// space around the `NaiveDateTime` range (although it is very theoretical to have a
|
||||
// transition at midnight around `NaiveDate::(MIN|MAX)`.
|
||||
let transition_utc = transition_local.overflowing_sub_offset(offset_before);
|
||||
Transition { transition_utc, offset_before, offset_after }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl PartialOrd for Transition {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.transition_utc.cmp(&other.transition_utc))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl Ord for Transition {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.transition_utc.cmp(&other.transition_utc)
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the time in UTC given a local time and transitions.
|
||||
// `transitions` must be sorted.
|
||||
#[cfg(windows)]
|
||||
fn lookup_with_dst_transitions(
|
||||
transitions: &[Transition],
|
||||
dt: NaiveDateTime,
|
||||
) -> MappedLocalTime<FixedOffset> {
|
||||
for t in transitions.iter() {
|
||||
// A transition can result in the wall clock time going forward (creating a gap) or going
|
||||
// backward (creating a fold). We are interested in the earliest and latest wall time of the
|
||||
// transition, as this are the times between which `dt` does may not exist or is ambiguous.
|
||||
//
|
||||
// It is no problem if the transition times falls a couple of hours inside the buffer
|
||||
// space around the `NaiveDateTime` range (although it is very theoretical to have a
|
||||
// transition at midnight around `NaiveDate::(MIN|MAX)`.
|
||||
let (offset_min, offset_max) =
|
||||
match t.offset_after.local_minus_utc() > t.offset_before.local_minus_utc() {
|
||||
true => (t.offset_before, t.offset_after),
|
||||
false => (t.offset_after, t.offset_before),
|
||||
};
|
||||
let wall_earliest = t.transition_utc.overflowing_add_offset(offset_min);
|
||||
let wall_latest = t.transition_utc.overflowing_add_offset(offset_max);
|
||||
|
||||
if dt < wall_earliest {
|
||||
return MappedLocalTime::Single(t.offset_before);
|
||||
} else if dt <= wall_latest {
|
||||
return match t.offset_after.local_minus_utc().cmp(&t.offset_before.local_minus_utc()) {
|
||||
Ordering::Equal => MappedLocalTime::Single(t.offset_before),
|
||||
Ordering::Less => MappedLocalTime::Ambiguous(t.offset_before, t.offset_after),
|
||||
Ordering::Greater => {
|
||||
if dt == wall_earliest {
|
||||
MappedLocalTime::Single(t.offset_before)
|
||||
} else if dt == wall_latest {
|
||||
MappedLocalTime::Single(t.offset_after)
|
||||
} else {
|
||||
MappedLocalTime::None
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
MappedLocalTime::Single(transitions.last().unwrap().offset_after)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Local;
|
||||
use crate::offset::TimeZone;
|
||||
#[cfg(windows)]
|
||||
use crate::offset::local::{Transition, lookup_with_dst_transitions};
|
||||
use crate::{Datelike, Days, Utc};
|
||||
#[cfg(windows)]
|
||||
use crate::{FixedOffset, MappedLocalTime, NaiveDate, NaiveDateTime};
|
||||
|
||||
#[test]
|
||||
fn verify_correct_offsets() {
|
||||
let now = Local::now();
|
||||
let from_local = Local.from_local_datetime(&now.naive_local()).unwrap();
|
||||
let from_utc = Local.from_utc_datetime(&now.naive_utc());
|
||||
|
||||
assert_eq!(now.offset().local_minus_utc(), from_local.offset().local_minus_utc());
|
||||
assert_eq!(now.offset().local_minus_utc(), from_utc.offset().local_minus_utc());
|
||||
|
||||
assert_eq!(now, from_local);
|
||||
assert_eq!(now, from_utc);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_correct_offsets_distant_past() {
|
||||
let distant_past = Local::now() - Days::new(365 * 500);
|
||||
let from_local = Local.from_local_datetime(&distant_past.naive_local()).unwrap();
|
||||
let from_utc = Local.from_utc_datetime(&distant_past.naive_utc());
|
||||
|
||||
assert_eq!(distant_past.offset().local_minus_utc(), from_local.offset().local_minus_utc());
|
||||
assert_eq!(distant_past.offset().local_minus_utc(), from_utc.offset().local_minus_utc());
|
||||
|
||||
assert_eq!(distant_past, from_local);
|
||||
assert_eq!(distant_past, from_utc);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_correct_offsets_distant_future() {
|
||||
let distant_future = Local::now() + Days::new(365 * 35000);
|
||||
let from_local = Local.from_local_datetime(&distant_future.naive_local()).unwrap();
|
||||
let from_utc = Local.from_utc_datetime(&distant_future.naive_utc());
|
||||
|
||||
assert_eq!(
|
||||
distant_future.offset().local_minus_utc(),
|
||||
from_local.offset().local_minus_utc()
|
||||
);
|
||||
assert_eq!(distant_future.offset().local_minus_utc(), from_utc.offset().local_minus_utc());
|
||||
|
||||
assert_eq!(distant_future, from_local);
|
||||
assert_eq!(distant_future, from_utc);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_local_date_sanity_check() {
|
||||
// issue #27
|
||||
assert_eq!(Local.with_ymd_and_hms(2999, 12, 28, 0, 0, 0).unwrap().day(), 28);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_leap_second() {
|
||||
// issue #123
|
||||
let today = Utc::now().date_naive();
|
||||
|
||||
if let Some(dt) = today.and_hms_milli_opt(15, 2, 59, 1000) {
|
||||
let timestr = dt.time().to_string();
|
||||
// the OS API may or may not support the leap second,
|
||||
// but there are only two sensible options.
|
||||
assert!(
|
||||
timestr == "15:02:60" || timestr == "15:03:00",
|
||||
"unexpected timestr {:?}",
|
||||
timestr
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(dt) = today.and_hms_milli_opt(15, 2, 3, 1234) {
|
||||
let timestr = dt.time().to_string();
|
||||
assert!(
|
||||
timestr == "15:02:03.234" || timestr == "15:02:04.234",
|
||||
"unexpected timestr {:?}",
|
||||
timestr
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn test_lookup_with_dst_transitions() {
|
||||
let ymdhms = |y, m, d, h, n, s| {
|
||||
NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap()
|
||||
};
|
||||
|
||||
#[track_caller]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compare_lookup(
|
||||
transitions: &[Transition],
|
||||
y: i32,
|
||||
m: u32,
|
||||
d: u32,
|
||||
h: u32,
|
||||
n: u32,
|
||||
s: u32,
|
||||
result: MappedLocalTime<FixedOffset>,
|
||||
) {
|
||||
let dt = NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap();
|
||||
assert_eq!(lookup_with_dst_transitions(transitions, dt), result);
|
||||
}
|
||||
|
||||
// dst transition before std transition
|
||||
// dst offset > std offset
|
||||
let std = FixedOffset::east_opt(3 * 60 * 60).unwrap();
|
||||
let dst = FixedOffset::east_opt(4 * 60 * 60).unwrap();
|
||||
let transitions = [
|
||||
Transition::new(ymdhms(2023, 3, 26, 2, 0, 0), std, dst),
|
||||
Transition::new(ymdhms(2023, 10, 29, 3, 0, 0), dst, std),
|
||||
];
|
||||
compare_lookup(&transitions, 2023, 3, 26, 1, 0, 0, MappedLocalTime::Single(std));
|
||||
compare_lookup(&transitions, 2023, 3, 26, 2, 0, 0, MappedLocalTime::Single(std));
|
||||
compare_lookup(&transitions, 2023, 3, 26, 2, 30, 0, MappedLocalTime::None);
|
||||
compare_lookup(&transitions, 2023, 3, 26, 3, 0, 0, MappedLocalTime::Single(dst));
|
||||
compare_lookup(&transitions, 2023, 3, 26, 4, 0, 0, MappedLocalTime::Single(dst));
|
||||
|
||||
compare_lookup(&transitions, 2023, 10, 29, 1, 0, 0, MappedLocalTime::Single(dst));
|
||||
compare_lookup(&transitions, 2023, 10, 29, 2, 0, 0, MappedLocalTime::Ambiguous(dst, std));
|
||||
compare_lookup(&transitions, 2023, 10, 29, 2, 30, 0, MappedLocalTime::Ambiguous(dst, std));
|
||||
compare_lookup(&transitions, 2023, 10, 29, 3, 0, 0, MappedLocalTime::Ambiguous(dst, std));
|
||||
compare_lookup(&transitions, 2023, 10, 29, 4, 0, 0, MappedLocalTime::Single(std));
|
||||
|
||||
// std transition before dst transition
|
||||
// dst offset > std offset
|
||||
let std = FixedOffset::east_opt(-5 * 60 * 60).unwrap();
|
||||
let dst = FixedOffset::east_opt(-4 * 60 * 60).unwrap();
|
||||
let transitions = [
|
||||
Transition::new(ymdhms(2023, 3, 24, 3, 0, 0), dst, std),
|
||||
Transition::new(ymdhms(2023, 10, 27, 2, 0, 0), std, dst),
|
||||
];
|
||||
compare_lookup(&transitions, 2023, 3, 24, 1, 0, 0, MappedLocalTime::Single(dst));
|
||||
compare_lookup(&transitions, 2023, 3, 24, 2, 0, 0, MappedLocalTime::Ambiguous(dst, std));
|
||||
compare_lookup(&transitions, 2023, 3, 24, 2, 30, 0, MappedLocalTime::Ambiguous(dst, std));
|
||||
compare_lookup(&transitions, 2023, 3, 24, 3, 0, 0, MappedLocalTime::Ambiguous(dst, std));
|
||||
compare_lookup(&transitions, 2023, 3, 24, 4, 0, 0, MappedLocalTime::Single(std));
|
||||
|
||||
compare_lookup(&transitions, 2023, 10, 27, 1, 0, 0, MappedLocalTime::Single(std));
|
||||
compare_lookup(&transitions, 2023, 10, 27, 2, 0, 0, MappedLocalTime::Single(std));
|
||||
compare_lookup(&transitions, 2023, 10, 27, 2, 30, 0, MappedLocalTime::None);
|
||||
compare_lookup(&transitions, 2023, 10, 27, 3, 0, 0, MappedLocalTime::Single(dst));
|
||||
compare_lookup(&transitions, 2023, 10, 27, 4, 0, 0, MappedLocalTime::Single(dst));
|
||||
|
||||
// dst transition before std transition
|
||||
// dst offset < std offset
|
||||
let std = FixedOffset::east_opt(3 * 60 * 60).unwrap();
|
||||
let dst = FixedOffset::east_opt((2 * 60 + 30) * 60).unwrap();
|
||||
let transitions = [
|
||||
Transition::new(ymdhms(2023, 3, 26, 2, 30, 0), std, dst),
|
||||
Transition::new(ymdhms(2023, 10, 29, 2, 0, 0), dst, std),
|
||||
];
|
||||
compare_lookup(&transitions, 2023, 3, 26, 1, 0, 0, MappedLocalTime::Single(std));
|
||||
compare_lookup(&transitions, 2023, 3, 26, 2, 0, 0, MappedLocalTime::Ambiguous(std, dst));
|
||||
compare_lookup(&transitions, 2023, 3, 26, 2, 15, 0, MappedLocalTime::Ambiguous(std, dst));
|
||||
compare_lookup(&transitions, 2023, 3, 26, 2, 30, 0, MappedLocalTime::Ambiguous(std, dst));
|
||||
compare_lookup(&transitions, 2023, 3, 26, 3, 0, 0, MappedLocalTime::Single(dst));
|
||||
|
||||
compare_lookup(&transitions, 2023, 10, 29, 1, 0, 0, MappedLocalTime::Single(dst));
|
||||
compare_lookup(&transitions, 2023, 10, 29, 2, 0, 0, MappedLocalTime::Single(dst));
|
||||
compare_lookup(&transitions, 2023, 10, 29, 2, 15, 0, MappedLocalTime::None);
|
||||
compare_lookup(&transitions, 2023, 10, 29, 2, 30, 0, MappedLocalTime::Single(std));
|
||||
compare_lookup(&transitions, 2023, 10, 29, 3, 0, 0, MappedLocalTime::Single(std));
|
||||
|
||||
// std transition before dst transition
|
||||
// dst offset < std offset
|
||||
let std = FixedOffset::east_opt(-(4 * 60 + 30) * 60).unwrap();
|
||||
let dst = FixedOffset::east_opt(-5 * 60 * 60).unwrap();
|
||||
let transitions = [
|
||||
Transition::new(ymdhms(2023, 3, 24, 2, 0, 0), dst, std),
|
||||
Transition::new(ymdhms(2023, 10, 27, 2, 30, 0), std, dst),
|
||||
];
|
||||
compare_lookup(&transitions, 2023, 3, 24, 1, 0, 0, MappedLocalTime::Single(dst));
|
||||
compare_lookup(&transitions, 2023, 3, 24, 2, 0, 0, MappedLocalTime::Single(dst));
|
||||
compare_lookup(&transitions, 2023, 3, 24, 2, 15, 0, MappedLocalTime::None);
|
||||
compare_lookup(&transitions, 2023, 3, 24, 2, 30, 0, MappedLocalTime::Single(std));
|
||||
compare_lookup(&transitions, 2023, 3, 24, 3, 0, 0, MappedLocalTime::Single(std));
|
||||
|
||||
compare_lookup(&transitions, 2023, 10, 27, 1, 0, 0, MappedLocalTime::Single(std));
|
||||
compare_lookup(&transitions, 2023, 10, 27, 2, 0, 0, MappedLocalTime::Ambiguous(std, dst));
|
||||
compare_lookup(&transitions, 2023, 10, 27, 2, 15, 0, MappedLocalTime::Ambiguous(std, dst));
|
||||
compare_lookup(&transitions, 2023, 10, 27, 2, 30, 0, MappedLocalTime::Ambiguous(std, dst));
|
||||
compare_lookup(&transitions, 2023, 10, 27, 3, 0, 0, MappedLocalTime::Single(dst));
|
||||
|
||||
// offset stays the same
|
||||
let std = FixedOffset::east_opt(3 * 60 * 60).unwrap();
|
||||
let transitions = [
|
||||
Transition::new(ymdhms(2023, 3, 26, 2, 0, 0), std, std),
|
||||
Transition::new(ymdhms(2023, 10, 29, 3, 0, 0), std, std),
|
||||
];
|
||||
compare_lookup(&transitions, 2023, 3, 26, 2, 0, 0, MappedLocalTime::Single(std));
|
||||
compare_lookup(&transitions, 2023, 10, 29, 3, 0, 0, MappedLocalTime::Single(std));
|
||||
|
||||
// single transition
|
||||
let std = FixedOffset::east_opt(3 * 60 * 60).unwrap();
|
||||
let dst = FixedOffset::east_opt(4 * 60 * 60).unwrap();
|
||||
let transitions = [Transition::new(ymdhms(2023, 3, 26, 2, 0, 0), std, dst)];
|
||||
compare_lookup(&transitions, 2023, 3, 26, 1, 0, 0, MappedLocalTime::Single(std));
|
||||
compare_lookup(&transitions, 2023, 3, 26, 2, 0, 0, MappedLocalTime::Single(std));
|
||||
compare_lookup(&transitions, 2023, 3, 26, 2, 30, 0, MappedLocalTime::None);
|
||||
compare_lookup(&transitions, 2023, 3, 26, 3, 0, 0, MappedLocalTime::Single(dst));
|
||||
compare_lookup(&transitions, 2023, 3, 26, 4, 0, 0, MappedLocalTime::Single(dst));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn test_lookup_with_dst_transitions_limits() {
|
||||
// Transition beyond UTC year end doesn't panic in year of `NaiveDate::MAX`
|
||||
let std = FixedOffset::east_opt(3 * 60 * 60).unwrap();
|
||||
let dst = FixedOffset::east_opt(4 * 60 * 60).unwrap();
|
||||
let transitions = [
|
||||
Transition::new(NaiveDateTime::MAX.with_month(7).unwrap(), std, dst),
|
||||
Transition::new(NaiveDateTime::MAX, dst, std),
|
||||
];
|
||||
assert_eq!(
|
||||
lookup_with_dst_transitions(&transitions, NaiveDateTime::MAX.with_month(3).unwrap()),
|
||||
MappedLocalTime::Single(std)
|
||||
);
|
||||
assert_eq!(
|
||||
lookup_with_dst_transitions(&transitions, NaiveDateTime::MAX.with_month(8).unwrap()),
|
||||
MappedLocalTime::Single(dst)
|
||||
);
|
||||
// Doesn't panic with `NaiveDateTime::MAX` as argument (which would be out of range when
|
||||
// converted to UTC).
|
||||
assert_eq!(
|
||||
lookup_with_dst_transitions(&transitions, NaiveDateTime::MAX),
|
||||
MappedLocalTime::Ambiguous(dst, std)
|
||||
);
|
||||
|
||||
// Transition before UTC year end doesn't panic in year of `NaiveDate::MIN`
|
||||
let std = FixedOffset::west_opt(3 * 60 * 60).unwrap();
|
||||
let dst = FixedOffset::west_opt(4 * 60 * 60).unwrap();
|
||||
let transitions = [
|
||||
Transition::new(NaiveDateTime::MIN, std, dst),
|
||||
Transition::new(NaiveDateTime::MIN.with_month(6).unwrap(), dst, std),
|
||||
];
|
||||
assert_eq!(
|
||||
lookup_with_dst_transitions(&transitions, NaiveDateTime::MIN.with_month(3).unwrap()),
|
||||
MappedLocalTime::Single(dst)
|
||||
);
|
||||
assert_eq!(
|
||||
lookup_with_dst_transitions(&transitions, NaiveDateTime::MIN.with_month(8).unwrap()),
|
||||
MappedLocalTime::Single(std)
|
||||
);
|
||||
// Doesn't panic with `NaiveDateTime::MIN` as argument (which would be out of range when
|
||||
// converted to UTC).
|
||||
assert_eq!(
|
||||
lookup_with_dst_transitions(&transitions, NaiveDateTime::MIN),
|
||||
MappedLocalTime::Ambiguous(std, dst)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "rkyv-validation")]
|
||||
fn test_rkyv_validation() {
|
||||
let local = Local;
|
||||
// Local is a ZST and serializes to 0 bytes
|
||||
let bytes = rkyv::to_bytes::<_, 0>(&local).unwrap();
|
||||
assert_eq!(bytes.len(), 0);
|
||||
|
||||
// but is deserialized to an archived variant without a
|
||||
// wrapping object
|
||||
assert_eq!(rkyv::from_bytes::<Local>(&bytes).unwrap(), super::ArchivedLocal);
|
||||
}
|
||||
}
|
||||
116
third_party/rust/chrono/src/offset/local/tz_info/mod.rs
vendored
Normal file
116
third_party/rust/chrono/src/offset/local/tz_info/mod.rs
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
#![deny(missing_docs)]
|
||||
#![allow(dead_code)]
|
||||
#![warn(unreachable_pub)]
|
||||
|
||||
use std::num::ParseIntError;
|
||||
use std::str::Utf8Error;
|
||||
use std::time::SystemTimeError;
|
||||
use std::{error, fmt, io};
|
||||
|
||||
mod timezone;
|
||||
pub(crate) use timezone::TimeZone;
|
||||
|
||||
mod parser;
|
||||
mod rule;
|
||||
|
||||
/// Unified error type for everything in the crate
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum Error {
|
||||
/// Date time error
|
||||
DateTime(&'static str),
|
||||
/// Local time type search error
|
||||
FindLocalTimeType(&'static str),
|
||||
/// Local time type error
|
||||
LocalTimeType(&'static str),
|
||||
/// Invalid slice for integer conversion
|
||||
InvalidSlice(&'static str),
|
||||
/// Invalid Tzif file
|
||||
InvalidTzFile(&'static str),
|
||||
/// Invalid TZ string
|
||||
InvalidTzString(&'static str),
|
||||
/// I/O error
|
||||
Io(io::Error),
|
||||
/// Out of range error
|
||||
OutOfRange(&'static str),
|
||||
/// Integer parsing error
|
||||
ParseInt(ParseIntError),
|
||||
/// Date time projection error
|
||||
ProjectDateTime(&'static str),
|
||||
/// System time error
|
||||
SystemTime(SystemTimeError),
|
||||
/// Time zone error
|
||||
TimeZone(&'static str),
|
||||
/// Transition rule error
|
||||
TransitionRule(&'static str),
|
||||
/// Unsupported Tzif file
|
||||
UnsupportedTzFile(&'static str),
|
||||
/// Unsupported TZ string
|
||||
UnsupportedTzString(&'static str),
|
||||
/// UTF-8 error
|
||||
Utf8(Utf8Error),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use Error::*;
|
||||
match self {
|
||||
DateTime(error) => write!(f, "invalid date time: {}", error),
|
||||
FindLocalTimeType(error) => error.fmt(f),
|
||||
LocalTimeType(error) => write!(f, "invalid local time type: {}", error),
|
||||
InvalidSlice(error) => error.fmt(f),
|
||||
InvalidTzString(error) => write!(f, "invalid TZ string: {}", error),
|
||||
InvalidTzFile(error) => error.fmt(f),
|
||||
Io(error) => error.fmt(f),
|
||||
OutOfRange(error) => error.fmt(f),
|
||||
ParseInt(error) => error.fmt(f),
|
||||
ProjectDateTime(error) => error.fmt(f),
|
||||
SystemTime(error) => error.fmt(f),
|
||||
TransitionRule(error) => write!(f, "invalid transition rule: {}", error),
|
||||
TimeZone(error) => write!(f, "invalid time zone: {}", error),
|
||||
UnsupportedTzFile(error) => error.fmt(f),
|
||||
UnsupportedTzString(error) => write!(f, "unsupported TZ string: {}", error),
|
||||
Utf8(error) => error.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Error {}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(error: io::Error) -> Self {
|
||||
Error::Io(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseIntError> for Error {
|
||||
fn from(error: ParseIntError) -> Self {
|
||||
Error::ParseInt(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SystemTimeError> for Error {
|
||||
fn from(error: SystemTimeError) -> Self {
|
||||
Error::SystemTime(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Utf8Error> for Error {
|
||||
fn from(error: Utf8Error) -> Self {
|
||||
Error::Utf8(error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Number of hours in one day
|
||||
const HOURS_PER_DAY: i64 = 24;
|
||||
/// Number of seconds in one hour
|
||||
const SECONDS_PER_HOUR: i64 = 3600;
|
||||
/// Number of seconds in one day
|
||||
const SECONDS_PER_DAY: i64 = SECONDS_PER_HOUR * HOURS_PER_DAY;
|
||||
/// Number of days in one week
|
||||
const DAYS_PER_WEEK: i64 = 7;
|
||||
|
||||
/// Month days in a normal year
|
||||
const DAY_IN_MONTHS_NORMAL_YEAR: [i64; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
||||
/// Cumulated month days in a normal year
|
||||
const CUMUL_DAY_IN_MONTHS_NORMAL_YEAR: [i64; 12] =
|
||||
[0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
|
||||
348
third_party/rust/chrono/src/offset/local/tz_info/parser.rs
vendored
Normal file
348
third_party/rust/chrono/src/offset/local/tz_info/parser.rs
vendored
Normal file
@@ -0,0 +1,348 @@
|
||||
use std::io::{self, ErrorKind};
|
||||
use std::iter;
|
||||
use std::num::ParseIntError;
|
||||
use std::str::{self, FromStr};
|
||||
|
||||
use super::Error;
|
||||
use super::rule::TransitionRule;
|
||||
use super::timezone::{LeapSecond, LocalTimeType, TimeZone, Transition};
|
||||
|
||||
pub(super) fn parse(bytes: &[u8]) -> Result<TimeZone, Error> {
|
||||
let mut cursor = Cursor::new(bytes);
|
||||
let state = State::new(&mut cursor, true)?;
|
||||
let (state, footer) = match state.header.version {
|
||||
Version::V1 => match cursor.is_empty() {
|
||||
true => (state, None),
|
||||
false => {
|
||||
return Err(Error::InvalidTzFile("remaining data after end of TZif v1 data block"));
|
||||
}
|
||||
},
|
||||
Version::V2 | Version::V3 => {
|
||||
let state = State::new(&mut cursor, false)?;
|
||||
(state, Some(cursor.remaining()))
|
||||
}
|
||||
};
|
||||
|
||||
let mut transitions = Vec::with_capacity(state.header.transition_count);
|
||||
for (arr_time, &local_time_type_index) in
|
||||
state.transition_times.chunks_exact(state.time_size).zip(state.transition_types)
|
||||
{
|
||||
let unix_leap_time =
|
||||
state.parse_time(&arr_time[0..state.time_size], state.header.version)?;
|
||||
let local_time_type_index = local_time_type_index as usize;
|
||||
transitions.push(Transition::new(unix_leap_time, local_time_type_index));
|
||||
}
|
||||
|
||||
let mut local_time_types = Vec::with_capacity(state.header.type_count);
|
||||
for arr in state.local_time_types.chunks_exact(6) {
|
||||
let ut_offset = read_be_i32(&arr[..4])?;
|
||||
|
||||
let is_dst = match arr[4] {
|
||||
0 => false,
|
||||
1 => true,
|
||||
_ => return Err(Error::InvalidTzFile("invalid DST indicator")),
|
||||
};
|
||||
|
||||
let char_index = arr[5] as usize;
|
||||
if char_index >= state.header.char_count {
|
||||
return Err(Error::InvalidTzFile("invalid time zone name char index"));
|
||||
}
|
||||
|
||||
let position = match state.names[char_index..].iter().position(|&c| c == b'\0') {
|
||||
Some(position) => position,
|
||||
None => return Err(Error::InvalidTzFile("invalid time zone name char index")),
|
||||
};
|
||||
|
||||
let name = &state.names[char_index..char_index + position];
|
||||
let name = if !name.is_empty() { Some(name) } else { None };
|
||||
local_time_types.push(LocalTimeType::new(ut_offset, is_dst, name)?);
|
||||
}
|
||||
|
||||
let mut leap_seconds = Vec::with_capacity(state.header.leap_count);
|
||||
for arr in state.leap_seconds.chunks_exact(state.time_size + 4) {
|
||||
let unix_leap_time = state.parse_time(&arr[0..state.time_size], state.header.version)?;
|
||||
let correction = read_be_i32(&arr[state.time_size..state.time_size + 4])?;
|
||||
leap_seconds.push(LeapSecond::new(unix_leap_time, correction));
|
||||
}
|
||||
|
||||
let std_walls_iter = state.std_walls.iter().copied().chain(iter::repeat(0));
|
||||
let ut_locals_iter = state.ut_locals.iter().copied().chain(iter::repeat(0));
|
||||
if std_walls_iter.zip(ut_locals_iter).take(state.header.type_count).any(|pair| pair == (0, 1)) {
|
||||
return Err(Error::InvalidTzFile(
|
||||
"invalid couple of standard/wall and UT/local indicators",
|
||||
));
|
||||
}
|
||||
|
||||
let extra_rule = match footer {
|
||||
Some(footer) => {
|
||||
let footer = str::from_utf8(footer)?;
|
||||
if !(footer.starts_with('\n') && footer.ends_with('\n')) {
|
||||
return Err(Error::InvalidTzFile("invalid footer"));
|
||||
}
|
||||
|
||||
let tz_string = footer.trim_matches(|c: char| c.is_ascii_whitespace());
|
||||
if tz_string.starts_with(':') || tz_string.contains('\0') {
|
||||
return Err(Error::InvalidTzFile("invalid footer"));
|
||||
}
|
||||
|
||||
match tz_string.is_empty() {
|
||||
true => None,
|
||||
false => Some(TransitionRule::from_tz_string(
|
||||
tz_string.as_bytes(),
|
||||
state.header.version == Version::V3,
|
||||
)?),
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
TimeZone::new(transitions, local_time_types, leap_seconds, extra_rule)
|
||||
}
|
||||
|
||||
/// TZif data blocks
|
||||
struct State<'a> {
|
||||
header: Header,
|
||||
/// Time size in bytes
|
||||
time_size: usize,
|
||||
/// Transition times data block
|
||||
transition_times: &'a [u8],
|
||||
/// Transition types data block
|
||||
transition_types: &'a [u8],
|
||||
/// Local time types data block
|
||||
local_time_types: &'a [u8],
|
||||
/// Time zone names data block
|
||||
names: &'a [u8],
|
||||
/// Leap seconds data block
|
||||
leap_seconds: &'a [u8],
|
||||
/// UT/local indicators data block
|
||||
std_walls: &'a [u8],
|
||||
/// Standard/wall indicators data block
|
||||
ut_locals: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> State<'a> {
|
||||
/// Read TZif data blocks
|
||||
fn new(cursor: &mut Cursor<'a>, first: bool) -> Result<Self, Error> {
|
||||
let header = Header::new(cursor)?;
|
||||
let time_size = match first {
|
||||
true => 4, // We always parse V1 first
|
||||
false => 8,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
time_size,
|
||||
transition_times: cursor.read_exact(header.transition_count * time_size)?,
|
||||
transition_types: cursor.read_exact(header.transition_count)?,
|
||||
local_time_types: cursor.read_exact(header.type_count * 6)?,
|
||||
names: cursor.read_exact(header.char_count)?,
|
||||
leap_seconds: cursor.read_exact(header.leap_count * (time_size + 4))?,
|
||||
std_walls: cursor.read_exact(header.std_wall_count)?,
|
||||
ut_locals: cursor.read_exact(header.ut_local_count)?,
|
||||
header,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse time values
|
||||
fn parse_time(&self, arr: &[u8], version: Version) -> Result<i64, Error> {
|
||||
match version {
|
||||
Version::V1 => Ok(read_be_i32(&arr[..4])?.into()),
|
||||
Version::V2 | Version::V3 => read_be_i64(arr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// TZif header
|
||||
#[derive(Debug)]
|
||||
struct Header {
|
||||
/// TZif version
|
||||
version: Version,
|
||||
/// Number of UT/local indicators
|
||||
ut_local_count: usize,
|
||||
/// Number of standard/wall indicators
|
||||
std_wall_count: usize,
|
||||
/// Number of leap-second records
|
||||
leap_count: usize,
|
||||
/// Number of transition times
|
||||
transition_count: usize,
|
||||
/// Number of local time type records
|
||||
type_count: usize,
|
||||
/// Number of time zone names bytes
|
||||
char_count: usize,
|
||||
}
|
||||
|
||||
impl Header {
|
||||
fn new(cursor: &mut Cursor) -> Result<Self, Error> {
|
||||
let magic = cursor.read_exact(4)?;
|
||||
if magic != *b"TZif" {
|
||||
return Err(Error::InvalidTzFile("invalid magic number"));
|
||||
}
|
||||
|
||||
let version = match cursor.read_exact(1)? {
|
||||
[0x00] => Version::V1,
|
||||
[0x32] => Version::V2,
|
||||
[0x33] => Version::V3,
|
||||
_ => return Err(Error::UnsupportedTzFile("unsupported TZif version")),
|
||||
};
|
||||
|
||||
cursor.read_exact(15)?;
|
||||
let ut_local_count = cursor.read_be_u32()?;
|
||||
let std_wall_count = cursor.read_be_u32()?;
|
||||
let leap_count = cursor.read_be_u32()?;
|
||||
let transition_count = cursor.read_be_u32()?;
|
||||
let type_count = cursor.read_be_u32()?;
|
||||
let char_count = cursor.read_be_u32()?;
|
||||
|
||||
if !(type_count != 0
|
||||
&& char_count != 0
|
||||
&& (ut_local_count == 0 || ut_local_count == type_count)
|
||||
&& (std_wall_count == 0 || std_wall_count == type_count))
|
||||
{
|
||||
return Err(Error::InvalidTzFile("invalid header"));
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
version,
|
||||
ut_local_count: ut_local_count as usize,
|
||||
std_wall_count: std_wall_count as usize,
|
||||
leap_count: leap_count as usize,
|
||||
transition_count: transition_count as usize,
|
||||
type_count: type_count as usize,
|
||||
char_count: char_count as usize,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A `Cursor` contains a slice of a buffer and a read count.
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub(crate) struct Cursor<'a> {
|
||||
/// Slice representing the remaining data to be read
|
||||
remaining: &'a [u8],
|
||||
/// Number of already read bytes
|
||||
read_count: usize,
|
||||
}
|
||||
|
||||
impl<'a> Cursor<'a> {
|
||||
/// Construct a new `Cursor` from remaining data
|
||||
pub(crate) const fn new(remaining: &'a [u8]) -> Self {
|
||||
Self { remaining, read_count: 0 }
|
||||
}
|
||||
|
||||
pub(crate) fn peek(&self) -> Option<&u8> {
|
||||
self.remaining().first()
|
||||
}
|
||||
|
||||
/// Returns remaining data
|
||||
pub(crate) const fn remaining(&self) -> &'a [u8] {
|
||||
self.remaining
|
||||
}
|
||||
|
||||
/// Returns `true` if data is remaining
|
||||
pub(crate) const fn is_empty(&self) -> bool {
|
||||
self.remaining.is_empty()
|
||||
}
|
||||
|
||||
pub(crate) fn read_be_u32(&mut self) -> Result<u32, Error> {
|
||||
let mut buf = [0; 4];
|
||||
buf.copy_from_slice(self.read_exact(4)?);
|
||||
Ok(u32::from_be_bytes(buf))
|
||||
}
|
||||
|
||||
#[cfg(target_env = "ohos")]
|
||||
pub(crate) fn seek_after(&mut self, offset: usize) -> Result<usize, io::Error> {
|
||||
if offset < self.read_count {
|
||||
return Err(io::Error::from(ErrorKind::UnexpectedEof));
|
||||
}
|
||||
match self.remaining.get((offset - self.read_count)..) {
|
||||
Some(remaining) => {
|
||||
self.remaining = remaining;
|
||||
self.read_count = offset;
|
||||
Ok(offset)
|
||||
}
|
||||
_ => Err(io::Error::from(ErrorKind::UnexpectedEof)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Read exactly `count` bytes, reducing remaining data and incrementing read count
|
||||
pub(crate) fn read_exact(&mut self, count: usize) -> Result<&'a [u8], io::Error> {
|
||||
match (self.remaining.get(..count), self.remaining.get(count..)) {
|
||||
(Some(result), Some(remaining)) => {
|
||||
self.remaining = remaining;
|
||||
self.read_count += count;
|
||||
Ok(result)
|
||||
}
|
||||
_ => Err(io::Error::from(ErrorKind::UnexpectedEof)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Read bytes and compare them to the provided tag
|
||||
pub(crate) fn read_tag(&mut self, tag: &[u8]) -> Result<(), io::Error> {
|
||||
if self.read_exact(tag.len())? == tag {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::from(ErrorKind::InvalidData))
|
||||
}
|
||||
}
|
||||
|
||||
/// Read bytes if the remaining data is prefixed by the provided tag
|
||||
pub(crate) fn read_optional_tag(&mut self, tag: &[u8]) -> Result<bool, io::Error> {
|
||||
if self.remaining.starts_with(tag) {
|
||||
self.read_exact(tag.len())?;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Read bytes as long as the provided predicate is true
|
||||
pub(crate) fn read_while<F: Fn(&u8) -> bool>(&mut self, f: F) -> Result<&'a [u8], io::Error> {
|
||||
match self.remaining.iter().position(|x| !f(x)) {
|
||||
None => self.read_exact(self.remaining.len()),
|
||||
Some(position) => self.read_exact(position),
|
||||
}
|
||||
}
|
||||
|
||||
// Parse an integer out of the ASCII digits
|
||||
pub(crate) fn read_int<T: FromStr<Err = ParseIntError>>(&mut self) -> Result<T, Error> {
|
||||
let bytes = self.read_while(u8::is_ascii_digit)?;
|
||||
Ok(str::from_utf8(bytes)?.parse()?)
|
||||
}
|
||||
|
||||
/// Read bytes until the provided predicate is true
|
||||
pub(crate) fn read_until<F: Fn(&u8) -> bool>(&mut self, f: F) -> Result<&'a [u8], io::Error> {
|
||||
match self.remaining.iter().position(f) {
|
||||
None => self.read_exact(self.remaining.len()),
|
||||
Some(position) => self.read_exact(position),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn read_be_i32(bytes: &[u8]) -> Result<i32, Error> {
|
||||
if bytes.len() != 4 {
|
||||
return Err(Error::InvalidSlice("too short for i32"));
|
||||
}
|
||||
|
||||
let mut buf = [0; 4];
|
||||
buf.copy_from_slice(bytes);
|
||||
Ok(i32::from_be_bytes(buf))
|
||||
}
|
||||
|
||||
pub(crate) fn read_be_i64(bytes: &[u8]) -> Result<i64, Error> {
|
||||
if bytes.len() != 8 {
|
||||
return Err(Error::InvalidSlice("too short for i64"));
|
||||
}
|
||||
|
||||
let mut buf = [0; 8];
|
||||
buf.copy_from_slice(bytes);
|
||||
Ok(i64::from_be_bytes(buf))
|
||||
}
|
||||
|
||||
/// TZif version
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
enum Version {
|
||||
/// Version 1
|
||||
V1,
|
||||
/// Version 2
|
||||
V2,
|
||||
/// Version 3
|
||||
V3,
|
||||
}
|
||||
1037
third_party/rust/chrono/src/offset/local/tz_info/rule.rs
vendored
Normal file
1037
third_party/rust/chrono/src/offset/local/tz_info/rule.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1007
third_party/rust/chrono/src/offset/local/tz_info/timezone.rs
vendored
Normal file
1007
third_party/rust/chrono/src/offset/local/tz_info/timezone.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
171
third_party/rust/chrono/src/offset/local/unix.rs
vendored
Normal file
171
third_party/rust/chrono/src/offset/local/unix.rs
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
// 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.
|
||||
|
||||
use std::{cell::RefCell, collections::hash_map, env, fs, hash::Hasher, time::SystemTime};
|
||||
|
||||
use super::tz_info::TimeZone;
|
||||
use super::{FixedOffset, NaiveDateTime};
|
||||
use crate::MappedLocalTime;
|
||||
|
||||
pub(super) fn offset_from_utc_datetime(utc: &NaiveDateTime) -> MappedLocalTime<FixedOffset> {
|
||||
offset(utc, false)
|
||||
}
|
||||
|
||||
pub(super) fn offset_from_local_datetime(local: &NaiveDateTime) -> MappedLocalTime<FixedOffset> {
|
||||
offset(local, true)
|
||||
}
|
||||
|
||||
fn offset(d: &NaiveDateTime, local: bool) -> MappedLocalTime<FixedOffset> {
|
||||
TZ_INFO.with(|maybe_cache| {
|
||||
maybe_cache.borrow_mut().get_or_insert_with(Cache::default).offset(*d, local)
|
||||
})
|
||||
}
|
||||
|
||||
// we have to store the `Cache` in an option as it can't
|
||||
// be initialized in a static context.
|
||||
thread_local! {
|
||||
static TZ_INFO: RefCell<Option<Cache>> = Default::default();
|
||||
}
|
||||
|
||||
enum Source {
|
||||
LocalTime { mtime: SystemTime },
|
||||
Environment { hash: u64 },
|
||||
}
|
||||
|
||||
impl Source {
|
||||
fn new(env_tz: Option<&str>) -> Source {
|
||||
match env_tz {
|
||||
Some(tz) => {
|
||||
let mut hasher = hash_map::DefaultHasher::new();
|
||||
hasher.write(tz.as_bytes());
|
||||
let hash = hasher.finish();
|
||||
Source::Environment { hash }
|
||||
}
|
||||
None => match fs::symlink_metadata("/etc/localtime") {
|
||||
Ok(data) => Source::LocalTime {
|
||||
// we have to pick a sensible default when the mtime fails
|
||||
// by picking SystemTime::now() we raise the probability of
|
||||
// the cache being invalidated if/when the mtime starts working
|
||||
mtime: data.modified().unwrap_or_else(|_| SystemTime::now()),
|
||||
},
|
||||
Err(_) => {
|
||||
// as above, now() should be a better default than some constant
|
||||
// TODO: see if we can improve caching in the case where the fallback is a valid timezone
|
||||
Source::LocalTime { mtime: SystemTime::now() }
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Cache {
|
||||
zone: TimeZone,
|
||||
source: Source,
|
||||
last_checked: SystemTime,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "aix")]
|
||||
const TZDB_LOCATION: &str = "/usr/share/lib/zoneinfo";
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "aix")))]
|
||||
const TZDB_LOCATION: &str = "/usr/share/zoneinfo";
|
||||
|
||||
fn fallback_timezone() -> Option<TimeZone> {
|
||||
let tz_name = iana_time_zone::get_timezone().ok()?;
|
||||
#[cfg(not(target_os = "android"))]
|
||||
let bytes = fs::read(format!("{}/{}", TZDB_LOCATION, tz_name)).ok()?;
|
||||
#[cfg(target_os = "android")]
|
||||
let bytes = android_tzdata::find_tz_data(&tz_name).ok()?;
|
||||
TimeZone::from_tz_data(&bytes).ok()
|
||||
}
|
||||
|
||||
impl Default for Cache {
|
||||
fn default() -> Cache {
|
||||
// default to UTC if no local timezone can be found
|
||||
let env_tz = env::var("TZ").ok();
|
||||
let env_ref = env_tz.as_deref();
|
||||
Cache {
|
||||
last_checked: SystemTime::now(),
|
||||
source: Source::new(env_ref),
|
||||
zone: current_zone(env_ref),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn current_zone(var: Option<&str>) -> TimeZone {
|
||||
TimeZone::local(var).ok().or_else(fallback_timezone).unwrap_or_else(TimeZone::utc)
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
fn offset(&mut self, d: NaiveDateTime, local: bool) -> MappedLocalTime<FixedOffset> {
|
||||
let now = SystemTime::now();
|
||||
|
||||
match now.duration_since(self.last_checked) {
|
||||
// If the cache has been around for less than a second then we reuse it
|
||||
// unconditionally. This is a reasonable tradeoff because the timezone
|
||||
// generally won't be changing _that_ often, but if the time zone does
|
||||
// change, it will reflect sufficiently quickly from an application
|
||||
// user's perspective.
|
||||
Ok(d) if d.as_secs() < 1 => (),
|
||||
Ok(_) | Err(_) => {
|
||||
let env_tz = env::var("TZ").ok();
|
||||
let env_ref = env_tz.as_deref();
|
||||
let new_source = Source::new(env_ref);
|
||||
|
||||
let out_of_date = match (&self.source, &new_source) {
|
||||
// change from env to file or file to env, must recreate the zone
|
||||
(Source::Environment { .. }, Source::LocalTime { .. })
|
||||
| (Source::LocalTime { .. }, Source::Environment { .. }) => true,
|
||||
// stay as file, but mtime has changed
|
||||
(Source::LocalTime { mtime: old_mtime }, Source::LocalTime { mtime })
|
||||
if old_mtime != mtime =>
|
||||
{
|
||||
true
|
||||
}
|
||||
// stay as env, but hash of variable has changed
|
||||
(Source::Environment { hash: old_hash }, Source::Environment { hash })
|
||||
if old_hash != hash =>
|
||||
{
|
||||
true
|
||||
}
|
||||
// cache can be reused
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if out_of_date {
|
||||
self.zone = current_zone(env_ref);
|
||||
}
|
||||
|
||||
self.last_checked = now;
|
||||
self.source = new_source;
|
||||
}
|
||||
}
|
||||
|
||||
if !local {
|
||||
let offset = self
|
||||
.zone
|
||||
.find_local_time_type(d.and_utc().timestamp())
|
||||
.expect("unable to select local time type")
|
||||
.offset();
|
||||
|
||||
return match FixedOffset::east_opt(offset) {
|
||||
Some(offset) => MappedLocalTime::Single(offset),
|
||||
None => MappedLocalTime::None,
|
||||
};
|
||||
}
|
||||
|
||||
// we pass through the year as the year of a local point in time must either be valid in that locale, or
|
||||
// the entire time was skipped in which case we will return MappedLocalTime::None anyway.
|
||||
self.zone
|
||||
.find_local_time_type_from_local(d)
|
||||
.expect("unable to select local time type")
|
||||
.and_then(|o| FixedOffset::east_opt(o.offset()))
|
||||
}
|
||||
}
|
||||
49
third_party/rust/chrono/src/offset/local/win_bindings.rs
vendored
Normal file
49
third_party/rust/chrono/src/offset/local/win_bindings.rs
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
#![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)]
|
||||
|
||||
windows_link::link!("kernel32.dll" "system" fn GetTimeZoneInformationForYear(wyear : u16, pdtzi : *const DYNAMIC_TIME_ZONE_INFORMATION, ptzi : *mut TIME_ZONE_INFORMATION) -> BOOL);
|
||||
windows_link::link!("kernel32.dll" "system" fn SystemTimeToFileTime(lpsystemtime : *const SYSTEMTIME, lpfiletime : *mut FILETIME) -> BOOL);
|
||||
windows_link::link!("kernel32.dll" "system" fn SystemTimeToTzSpecificLocalTime(lptimezoneinformation : *const TIME_ZONE_INFORMATION, lpuniversaltime : *const SYSTEMTIME, lplocaltime : *mut SYSTEMTIME) -> BOOL);
|
||||
windows_link::link!("kernel32.dll" "system" fn TzSpecificLocalTimeToSystemTime(lptimezoneinformation : *const TIME_ZONE_INFORMATION, lplocaltime : *const SYSTEMTIME, lpuniversaltime : *mut SYSTEMTIME) -> BOOL);
|
||||
pub type BOOL = i32;
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct DYNAMIC_TIME_ZONE_INFORMATION {
|
||||
pub Bias: i32,
|
||||
pub StandardName: [u16; 32],
|
||||
pub StandardDate: SYSTEMTIME,
|
||||
pub StandardBias: i32,
|
||||
pub DaylightName: [u16; 32],
|
||||
pub DaylightDate: SYSTEMTIME,
|
||||
pub DaylightBias: i32,
|
||||
pub TimeZoneKeyName: [u16; 128],
|
||||
pub DynamicDaylightTimeDisabled: bool,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct FILETIME {
|
||||
pub dwLowDateTime: u32,
|
||||
pub dwHighDateTime: u32,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct SYSTEMTIME {
|
||||
pub wYear: u16,
|
||||
pub wMonth: u16,
|
||||
pub wDayOfWeek: u16,
|
||||
pub wDay: u16,
|
||||
pub wHour: u16,
|
||||
pub wMinute: u16,
|
||||
pub wSecond: u16,
|
||||
pub wMilliseconds: u16,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct TIME_ZONE_INFORMATION {
|
||||
pub Bias: i32,
|
||||
pub StandardName: [u16; 32],
|
||||
pub StandardDate: SYSTEMTIME,
|
||||
pub StandardBias: i32,
|
||||
pub DaylightName: [u16; 32],
|
||||
pub DaylightDate: SYSTEMTIME,
|
||||
pub DaylightBias: i32,
|
||||
}
|
||||
7
third_party/rust/chrono/src/offset/local/win_bindings.txt
vendored
Normal file
7
third_party/rust/chrono/src/offset/local/win_bindings.txt
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
--out src/offset/local/win_bindings.rs
|
||||
--flat --sys --no-comment
|
||||
--filter
|
||||
GetTimeZoneInformationForYear
|
||||
SystemTimeToFileTime
|
||||
SystemTimeToTzSpecificLocalTime
|
||||
TzSpecificLocalTimeToSystemTime
|
||||
293
third_party/rust/chrono/src/offset/local/windows.rs
vendored
Normal file
293
third_party/rust/chrono/src/offset/local/windows.rs
vendored
Normal file
@@ -0,0 +1,293 @@
|
||||
// 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.
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr;
|
||||
|
||||
use super::win_bindings::{GetTimeZoneInformationForYear, SYSTEMTIME, TIME_ZONE_INFORMATION};
|
||||
|
||||
use crate::offset::local::{Transition, lookup_with_dst_transitions};
|
||||
use crate::{Datelike, FixedOffset, MappedLocalTime, NaiveDate, NaiveDateTime, NaiveTime, Weekday};
|
||||
|
||||
// We don't use `SystemTimeToTzSpecificLocalTime` because it doesn't support the same range of dates
|
||||
// as Chrono. Also it really isn't that difficult to work out the correct offset from the provided
|
||||
// DST rules.
|
||||
//
|
||||
// This method uses `overflowing_sub_offset` because it is no problem if the transition time in UTC
|
||||
// falls a couple of hours inside the buffer space around the `NaiveDateTime` range (although it is
|
||||
// very theoretical to have a transition at midnight around `NaiveDate::(MIN|MAX)`.
|
||||
pub(super) fn offset_from_utc_datetime(utc: &NaiveDateTime) -> MappedLocalTime<FixedOffset> {
|
||||
// Using a `TzInfo` based on the year of an UTC datetime is technically wrong, we should be
|
||||
// using the rules for the year of the corresponding local time. But this matches what
|
||||
// `SystemTimeToTzSpecificLocalTime` is documented to do.
|
||||
let tz_info = match TzInfo::for_year(utc.year()) {
|
||||
Some(tz_info) => tz_info,
|
||||
None => return MappedLocalTime::None,
|
||||
};
|
||||
let offset = match (tz_info.std_transition, tz_info.dst_transition) {
|
||||
(Some(std_transition), Some(dst_transition)) => {
|
||||
let std_transition_utc = std_transition.overflowing_sub_offset(tz_info.dst_offset);
|
||||
let dst_transition_utc = dst_transition.overflowing_sub_offset(tz_info.std_offset);
|
||||
if dst_transition_utc < std_transition_utc {
|
||||
match utc >= &dst_transition_utc && utc < &std_transition_utc {
|
||||
true => tz_info.dst_offset,
|
||||
false => tz_info.std_offset,
|
||||
}
|
||||
} else {
|
||||
match utc >= &std_transition_utc && utc < &dst_transition_utc {
|
||||
true => tz_info.std_offset,
|
||||
false => tz_info.dst_offset,
|
||||
}
|
||||
}
|
||||
}
|
||||
(Some(std_transition), None) => {
|
||||
let std_transition_utc = std_transition.overflowing_sub_offset(tz_info.dst_offset);
|
||||
match utc < &std_transition_utc {
|
||||
true => tz_info.dst_offset,
|
||||
false => tz_info.std_offset,
|
||||
}
|
||||
}
|
||||
(None, Some(dst_transition)) => {
|
||||
let dst_transition_utc = dst_transition.overflowing_sub_offset(tz_info.std_offset);
|
||||
match utc < &dst_transition_utc {
|
||||
true => tz_info.std_offset,
|
||||
false => tz_info.dst_offset,
|
||||
}
|
||||
}
|
||||
(None, None) => tz_info.std_offset,
|
||||
};
|
||||
MappedLocalTime::Single(offset)
|
||||
}
|
||||
|
||||
// We don't use `TzSpecificLocalTimeToSystemTime` because it doesn't let us choose how to handle
|
||||
// ambiguous cases (during a DST transition). Instead we get the timezone information for the
|
||||
// current year and compute it ourselves, like we do on Unix.
|
||||
pub(super) fn offset_from_local_datetime(local: &NaiveDateTime) -> MappedLocalTime<FixedOffset> {
|
||||
let tz_info = match TzInfo::for_year(local.year()) {
|
||||
Some(tz_info) => tz_info,
|
||||
None => return MappedLocalTime::None,
|
||||
};
|
||||
// Create a sorted slice of transitions and use `lookup_with_dst_transitions`.
|
||||
match (tz_info.std_transition, tz_info.dst_transition) {
|
||||
(Some(std_transition), Some(dst_transition)) => {
|
||||
let std_transition =
|
||||
Transition::new(std_transition, tz_info.dst_offset, tz_info.std_offset);
|
||||
let dst_transition =
|
||||
Transition::new(dst_transition, tz_info.std_offset, tz_info.dst_offset);
|
||||
let transitions = match std_transition.cmp(&dst_transition) {
|
||||
Ordering::Less => [std_transition, dst_transition],
|
||||
Ordering::Greater => [dst_transition, std_transition],
|
||||
Ordering::Equal => {
|
||||
// This doesn't make sense. Let's just return the standard offset.
|
||||
return MappedLocalTime::Single(tz_info.std_offset);
|
||||
}
|
||||
};
|
||||
lookup_with_dst_transitions(&transitions, *local)
|
||||
}
|
||||
(Some(std_transition), None) => {
|
||||
let transitions =
|
||||
[Transition::new(std_transition, tz_info.dst_offset, tz_info.std_offset)];
|
||||
lookup_with_dst_transitions(&transitions, *local)
|
||||
}
|
||||
(None, Some(dst_transition)) => {
|
||||
let transitions =
|
||||
[Transition::new(dst_transition, tz_info.std_offset, tz_info.dst_offset)];
|
||||
lookup_with_dst_transitions(&transitions, *local)
|
||||
}
|
||||
(None, None) => MappedLocalTime::Single(tz_info.std_offset),
|
||||
}
|
||||
}
|
||||
|
||||
// The basis for Windows timezone and DST support has been in place since Windows 2000. It does not
|
||||
// allow for complex rules like the IANA timezone database:
|
||||
// - A timezone has the same base offset the whole year.
|
||||
// - There seem to be either zero or two DST transitions (but we support having just one).
|
||||
// - As of Vista(?) only years from 2004 until a few years into the future are supported.
|
||||
// - All other years get the base settings, which seem to be that of the current year.
|
||||
//
|
||||
// These details don't matter much, we just work with the offsets and transition dates Windows
|
||||
// returns through `GetTimeZoneInformationForYear` for a particular year.
|
||||
struct TzInfo {
|
||||
// Offset from UTC during standard time.
|
||||
std_offset: FixedOffset,
|
||||
// Offset from UTC during daylight saving time.
|
||||
dst_offset: FixedOffset,
|
||||
// Transition from standard time to daylight saving time, given in local standard time.
|
||||
std_transition: Option<NaiveDateTime>,
|
||||
// Transition from daylight saving time to standard time, given in local daylight saving time.
|
||||
dst_transition: Option<NaiveDateTime>,
|
||||
}
|
||||
|
||||
impl TzInfo {
|
||||
fn for_year(year: i32) -> Option<TzInfo> {
|
||||
// The API limits years to 1601..=30827.
|
||||
// Working with timezones and daylight saving time this far into the past or future makes
|
||||
// little sense. But whatever is extrapolated for 1601 or 30827 is what can be extrapolated
|
||||
// for years beyond.
|
||||
let ref_year = year.clamp(1601, 30827) as u16;
|
||||
let tz_info = unsafe {
|
||||
let mut tz_info = MaybeUninit::<TIME_ZONE_INFORMATION>::uninit();
|
||||
if GetTimeZoneInformationForYear(ref_year, ptr::null_mut(), tz_info.as_mut_ptr()) == 0 {
|
||||
return None;
|
||||
}
|
||||
tz_info.assume_init()
|
||||
};
|
||||
let std_offset = (tz_info.Bias)
|
||||
.checked_add(tz_info.StandardBias)
|
||||
.and_then(|o| o.checked_mul(60))
|
||||
.and_then(FixedOffset::west_opt)?;
|
||||
let dst_offset = (tz_info.Bias)
|
||||
.checked_add(tz_info.DaylightBias)
|
||||
.and_then(|o| o.checked_mul(60))
|
||||
.and_then(FixedOffset::west_opt)?;
|
||||
Some(TzInfo {
|
||||
std_offset,
|
||||
dst_offset,
|
||||
std_transition: naive_date_time_from_system_time(tz_info.StandardDate, year).ok()?,
|
||||
dst_transition: naive_date_time_from_system_time(tz_info.DaylightDate, year).ok()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve a `SYSTEMTIME` object to an `Option<NaiveDateTime>`.
|
||||
///
|
||||
/// A `SYSTEMTIME` within a `TIME_ZONE_INFORMATION` struct can be zero to indicate there is no
|
||||
/// transition.
|
||||
/// If it has year, month and day values it is a concrete date.
|
||||
/// If the year is missing the `SYSTEMTIME` is a rule, which this method resolves for the provided
|
||||
/// year. A rule has a month, weekday, and nth weekday of the month as components.
|
||||
///
|
||||
/// Returns `Err` if any of the values is invalid, which should never happen.
|
||||
fn naive_date_time_from_system_time(
|
||||
st: SYSTEMTIME,
|
||||
year: i32,
|
||||
) -> Result<Option<NaiveDateTime>, ()> {
|
||||
if st.wYear == 0 && st.wMonth == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
let time = NaiveTime::from_hms_milli_opt(
|
||||
st.wHour as u32,
|
||||
st.wMinute as u32,
|
||||
st.wSecond as u32,
|
||||
st.wMilliseconds as u32,
|
||||
)
|
||||
.ok_or(())?;
|
||||
|
||||
if st.wYear != 0 {
|
||||
// We have a concrete date.
|
||||
let date =
|
||||
NaiveDate::from_ymd_opt(st.wYear as i32, st.wMonth as u32, st.wDay as u32).ok_or(())?;
|
||||
return Ok(Some(date.and_time(time)));
|
||||
}
|
||||
|
||||
// Resolve a rule with month, weekday, and nth weekday of the month to a date in the current
|
||||
// year.
|
||||
let weekday = match st.wDayOfWeek {
|
||||
0 => Weekday::Sun,
|
||||
1 => Weekday::Mon,
|
||||
2 => Weekday::Tue,
|
||||
3 => Weekday::Wed,
|
||||
4 => Weekday::Thu,
|
||||
5 => Weekday::Fri,
|
||||
6 => Weekday::Sat,
|
||||
_ => return Err(()),
|
||||
};
|
||||
let nth_day = match st.wDay {
|
||||
1..=5 => st.wDay as u8,
|
||||
_ => return Err(()),
|
||||
};
|
||||
let date = NaiveDate::from_weekday_of_month_opt(year, st.wMonth as u32, weekday, nth_day)
|
||||
.or_else(|| NaiveDate::from_weekday_of_month_opt(year, st.wMonth as u32, weekday, 4))
|
||||
.ok_or(())?; // `st.wMonth` must be invalid
|
||||
Ok(Some(date.and_time(time)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::offset::local::win_bindings::{
|
||||
FILETIME, SYSTEMTIME, SystemTimeToFileTime, TzSpecificLocalTimeToSystemTime,
|
||||
};
|
||||
use crate::{DateTime, FixedOffset, Local, NaiveDate, NaiveDateTime, TimeDelta};
|
||||
use crate::{Datelike, TimeZone, Timelike};
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr;
|
||||
|
||||
#[test]
|
||||
fn verify_against_tz_specific_local_time_to_system_time() {
|
||||
// The implementation in Windows itself is the source of truth on how to work with the OS
|
||||
// timezone information. This test compares for every hour over a period of 125 years our
|
||||
// implementation to `TzSpecificLocalTimeToSystemTime`.
|
||||
//
|
||||
// This uses parts of a previous Windows `Local` implementation in chrono.
|
||||
fn from_local_time(dt: &NaiveDateTime) -> DateTime<Local> {
|
||||
let st = system_time_from_naive_date_time(dt);
|
||||
let utc_time = local_to_utc_time(&st);
|
||||
let utc_secs = system_time_as_unix_seconds(&utc_time);
|
||||
let local_secs = system_time_as_unix_seconds(&st);
|
||||
let offset = (local_secs - utc_secs) as i32;
|
||||
let offset = FixedOffset::east_opt(offset).unwrap();
|
||||
DateTime::from_naive_utc_and_offset(*dt - offset, offset)
|
||||
}
|
||||
fn system_time_from_naive_date_time(dt: &NaiveDateTime) -> SYSTEMTIME {
|
||||
SYSTEMTIME {
|
||||
// Valid values: 1601-30827
|
||||
wYear: dt.year() as u16,
|
||||
// Valid values:1-12
|
||||
wMonth: dt.month() as u16,
|
||||
// Valid values: 0-6, starting Sunday.
|
||||
// NOTE: enum returns 1-7, starting Monday, so we are
|
||||
// off here, but this is not currently used in local.
|
||||
wDayOfWeek: dt.weekday() as u16,
|
||||
// Valid values: 1-31
|
||||
wDay: dt.day() as u16,
|
||||
// Valid values: 0-23
|
||||
wHour: dt.hour() as u16,
|
||||
// Valid values: 0-59
|
||||
wMinute: dt.minute() as u16,
|
||||
// Valid values: 0-59
|
||||
wSecond: dt.second() as u16,
|
||||
// Valid values: 0-999
|
||||
wMilliseconds: 0,
|
||||
}
|
||||
}
|
||||
fn local_to_utc_time(local: &SYSTEMTIME) -> SYSTEMTIME {
|
||||
let mut sys_time = MaybeUninit::<SYSTEMTIME>::uninit();
|
||||
unsafe { TzSpecificLocalTimeToSystemTime(ptr::null(), local, sys_time.as_mut_ptr()) };
|
||||
// SAFETY: TzSpecificLocalTimeToSystemTime must have succeeded at this point, so we can
|
||||
// assume the value is initialized.
|
||||
unsafe { sys_time.assume_init() }
|
||||
}
|
||||
const HECTONANOSECS_IN_SEC: i64 = 10_000_000;
|
||||
const HECTONANOSEC_TO_UNIX_EPOCH: i64 = 11_644_473_600 * HECTONANOSECS_IN_SEC;
|
||||
fn system_time_as_unix_seconds(st: &SYSTEMTIME) -> i64 {
|
||||
let mut init = MaybeUninit::<FILETIME>::uninit();
|
||||
unsafe {
|
||||
SystemTimeToFileTime(st, init.as_mut_ptr());
|
||||
}
|
||||
// SystemTimeToFileTime must have succeeded at this point, so we can assume the value is
|
||||
// initialized.
|
||||
let filetime = unsafe { init.assume_init() };
|
||||
let bit_shift =
|
||||
((filetime.dwHighDateTime as u64) << 32) | (filetime.dwLowDateTime as u64);
|
||||
(bit_shift as i64 - HECTONANOSEC_TO_UNIX_EPOCH) / HECTONANOSECS_IN_SEC
|
||||
}
|
||||
|
||||
let mut date = NaiveDate::from_ymd_opt(1975, 1, 1).unwrap().and_hms_opt(0, 30, 0).unwrap();
|
||||
|
||||
while date.year() < 2078 {
|
||||
// Windows doesn't handle non-existing dates, it just treats it as valid.
|
||||
if let Some(our_result) = Local.from_local_datetime(&date).earliest() {
|
||||
assert_eq!(from_local_time(&date), our_result);
|
||||
}
|
||||
date += TimeDelta::try_hours(1).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
508
third_party/rust/chrono/src/offset/mod.rs
vendored
508
third_party/rust/chrono/src/offset/mod.rs
vendored
@@ -20,71 +20,144 @@
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use format::{parse, ParseResult, Parsed, StrftimeItems};
|
||||
use naive::{NaiveDate, NaiveDateTime, NaiveTime};
|
||||
use Weekday;
|
||||
use {Date, DateTime};
|
||||
use crate::Weekday;
|
||||
use crate::format::{ParseResult, Parsed, StrftimeItems, parse};
|
||||
use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime};
|
||||
#[allow(deprecated)]
|
||||
use crate::{Date, DateTime};
|
||||
|
||||
/// The conversion result from the local time to the timezone-aware datetime types.
|
||||
pub(crate) mod fixed;
|
||||
pub use self::fixed::FixedOffset;
|
||||
|
||||
#[cfg(feature = "clock")]
|
||||
pub(crate) mod local;
|
||||
#[cfg(feature = "clock")]
|
||||
pub use self::local::Local;
|
||||
|
||||
pub(crate) mod utc;
|
||||
pub use self::utc::Utc;
|
||||
|
||||
/// The result of mapping a local time to a concrete instant in a given time zone.
|
||||
///
|
||||
/// The calculation to go from a local time (wall clock time) to an instant in UTC can end up in
|
||||
/// three cases:
|
||||
/// * A single, simple result.
|
||||
/// * An ambiguous result when the clock is turned backwards during a transition due to for example
|
||||
/// DST.
|
||||
/// * No result when the clock is turned forwards during a transition due to for example DST.
|
||||
///
|
||||
/// When the clock is turned backwards it creates a _fold_ in local time, during which the local
|
||||
/// time is _ambiguous_. When the clock is turned forwards it creates a _gap_ in local time, during
|
||||
/// which the local time is _missing_, or does not exist.
|
||||
///
|
||||
/// Chrono does not return a default choice or invalid data during time zone transitions, but has
|
||||
/// the `MappedLocalTime` type to help deal with the result correctly.
|
||||
///
|
||||
/// The type of `T` is usually a [`DateTime`] but may also be only an offset.
|
||||
pub type MappedLocalTime<T> = LocalResult<T>;
|
||||
#[derive(Clone, PartialEq, Debug, Copy, Eq, Hash)]
|
||||
|
||||
/// Old name of [`MappedLocalTime`]. See that type for more documentation.
|
||||
pub enum LocalResult<T> {
|
||||
/// Given local time representation is invalid.
|
||||
/// This can occur when, for example, the positive timezone transition.
|
||||
None,
|
||||
/// Given local time representation has a single unique result.
|
||||
/// The local time maps to a single unique result.
|
||||
Single(T),
|
||||
/// Given local time representation has multiple results and thus ambiguous.
|
||||
/// This can occur when, for example, the negative timezone transition.
|
||||
Ambiguous(T /*min*/, T /*max*/),
|
||||
|
||||
/// The local time is _ambiguous_ because there is a _fold_ in the local time.
|
||||
///
|
||||
/// This variant contains the two possible results, in the order `(earliest, latest)`.
|
||||
Ambiguous(T, T),
|
||||
|
||||
/// The local time does not exist because there is a _gap_ in the local time.
|
||||
///
|
||||
/// This variant may also be returned if there was an error while resolving the local time,
|
||||
/// caused by for example missing time zone data files, an error in an OS API, or overflow.
|
||||
None,
|
||||
}
|
||||
|
||||
impl<T> LocalResult<T> {
|
||||
/// Returns `Some` only when the conversion result is unique, or `None` otherwise.
|
||||
impl<T> MappedLocalTime<T> {
|
||||
/// Returns `Some` if the time zone mapping has a single result.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if local time falls in a _fold_ or _gap_ in the local time, or if there was
|
||||
/// an error.
|
||||
#[must_use]
|
||||
pub fn single(self) -> Option<T> {
|
||||
match self {
|
||||
LocalResult::Single(t) => Some(t),
|
||||
MappedLocalTime::Single(t) => Some(t),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `Some` for the earliest possible conversion result, or `None` if none.
|
||||
/// Returns the earliest possible result of the time zone mapping.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if local time falls in a _gap_ in the local time, or if there was an error.
|
||||
#[must_use]
|
||||
pub fn earliest(self) -> Option<T> {
|
||||
match self {
|
||||
LocalResult::Single(t) | LocalResult::Ambiguous(t, _) => Some(t),
|
||||
MappedLocalTime::Single(t) | MappedLocalTime::Ambiguous(t, _) => Some(t),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `Some` for the latest possible conversion result, or `None` if none.
|
||||
/// Returns the latest possible result of the time zone mapping.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if local time falls in a _gap_ in the local time, or if there was an error.
|
||||
#[must_use]
|
||||
pub fn latest(self) -> Option<T> {
|
||||
match self {
|
||||
LocalResult::Single(t) | LocalResult::Ambiguous(_, t) => Some(t),
|
||||
MappedLocalTime::Single(t) | MappedLocalTime::Ambiguous(_, t) => Some(t),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps a `LocalResult<T>` into `LocalResult<U>` with given function.
|
||||
pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> LocalResult<U> {
|
||||
/// Maps a `MappedLocalTime<T>` into `MappedLocalTime<U>` with given function.
|
||||
#[must_use]
|
||||
pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> MappedLocalTime<U> {
|
||||
match self {
|
||||
LocalResult::None => LocalResult::None,
|
||||
LocalResult::Single(v) => LocalResult::Single(f(v)),
|
||||
LocalResult::Ambiguous(min, max) => LocalResult::Ambiguous(f(min), f(max)),
|
||||
MappedLocalTime::None => MappedLocalTime::None,
|
||||
MappedLocalTime::Single(v) => MappedLocalTime::Single(f(v)),
|
||||
MappedLocalTime::Ambiguous(min, max) => MappedLocalTime::Ambiguous(f(min), f(max)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps a `MappedLocalTime<T>` into `MappedLocalTime<U>` with given function.
|
||||
///
|
||||
/// Returns `MappedLocalTime::None` if the function returns `None`.
|
||||
#[must_use]
|
||||
pub(crate) fn and_then<U, F: FnMut(T) -> Option<U>>(self, mut f: F) -> MappedLocalTime<U> {
|
||||
match self {
|
||||
MappedLocalTime::None => MappedLocalTime::None,
|
||||
MappedLocalTime::Single(v) => match f(v) {
|
||||
Some(new) => MappedLocalTime::Single(new),
|
||||
None => MappedLocalTime::None,
|
||||
},
|
||||
MappedLocalTime::Ambiguous(min, max) => match (f(min), f(max)) {
|
||||
(Some(min), Some(max)) => MappedLocalTime::Ambiguous(min, max),
|
||||
_ => MappedLocalTime::None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> LocalResult<Date<Tz>> {
|
||||
#[allow(deprecated)]
|
||||
impl<Tz: TimeZone> MappedLocalTime<Date<Tz>> {
|
||||
/// Makes a new `DateTime` from the current date and given `NaiveTime`.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Propagates any error. Ambiguous result would be discarded.
|
||||
#[inline]
|
||||
pub fn and_time(self, time: NaiveTime) -> LocalResult<DateTime<Tz>> {
|
||||
#[must_use]
|
||||
pub fn and_time(self, time: NaiveTime) -> MappedLocalTime<DateTime<Tz>> {
|
||||
match self {
|
||||
LocalResult::Single(d) => {
|
||||
d.and_time(time).map_or(LocalResult::None, LocalResult::Single)
|
||||
MappedLocalTime::Single(d) => {
|
||||
d.and_time(time).map_or(MappedLocalTime::None, MappedLocalTime::Single)
|
||||
}
|
||||
_ => LocalResult::None,
|
||||
_ => MappedLocalTime::None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,12 +166,13 @@ impl<Tz: TimeZone> LocalResult<Date<Tz>> {
|
||||
///
|
||||
/// Propagates any error. Ambiguous result would be discarded.
|
||||
#[inline]
|
||||
pub fn and_hms_opt(self, hour: u32, min: u32, sec: u32) -> LocalResult<DateTime<Tz>> {
|
||||
#[must_use]
|
||||
pub fn and_hms_opt(self, hour: u32, min: u32, sec: u32) -> MappedLocalTime<DateTime<Tz>> {
|
||||
match self {
|
||||
LocalResult::Single(d) => {
|
||||
d.and_hms_opt(hour, min, sec).map_or(LocalResult::None, LocalResult::Single)
|
||||
MappedLocalTime::Single(d) => {
|
||||
d.and_hms_opt(hour, min, sec).map_or(MappedLocalTime::None, MappedLocalTime::Single)
|
||||
}
|
||||
_ => LocalResult::None,
|
||||
_ => MappedLocalTime::None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,18 +182,19 @@ impl<Tz: TimeZone> LocalResult<Date<Tz>> {
|
||||
///
|
||||
/// Propagates any error. Ambiguous result would be discarded.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn and_hms_milli_opt(
|
||||
self,
|
||||
hour: u32,
|
||||
min: u32,
|
||||
sec: u32,
|
||||
milli: u32,
|
||||
) -> LocalResult<DateTime<Tz>> {
|
||||
) -> MappedLocalTime<DateTime<Tz>> {
|
||||
match self {
|
||||
LocalResult::Single(d) => d
|
||||
MappedLocalTime::Single(d) => d
|
||||
.and_hms_milli_opt(hour, min, sec, milli)
|
||||
.map_or(LocalResult::None, LocalResult::Single),
|
||||
_ => LocalResult::None,
|
||||
.map_or(MappedLocalTime::None, MappedLocalTime::Single),
|
||||
_ => MappedLocalTime::None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,18 +204,19 @@ impl<Tz: TimeZone> LocalResult<Date<Tz>> {
|
||||
///
|
||||
/// Propagates any error. Ambiguous result would be discarded.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn and_hms_micro_opt(
|
||||
self,
|
||||
hour: u32,
|
||||
min: u32,
|
||||
sec: u32,
|
||||
micro: u32,
|
||||
) -> LocalResult<DateTime<Tz>> {
|
||||
) -> MappedLocalTime<DateTime<Tz>> {
|
||||
match self {
|
||||
LocalResult::Single(d) => d
|
||||
MappedLocalTime::Single(d) => d
|
||||
.and_hms_micro_opt(hour, min, sec, micro)
|
||||
.map_or(LocalResult::None, LocalResult::Single),
|
||||
_ => LocalResult::None,
|
||||
.map_or(MappedLocalTime::None, MappedLocalTime::Single),
|
||||
_ => MappedLocalTime::None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,29 +226,41 @@ impl<Tz: TimeZone> LocalResult<Date<Tz>> {
|
||||
///
|
||||
/// Propagates any error. Ambiguous result would be discarded.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn and_hms_nano_opt(
|
||||
self,
|
||||
hour: u32,
|
||||
min: u32,
|
||||
sec: u32,
|
||||
nano: u32,
|
||||
) -> LocalResult<DateTime<Tz>> {
|
||||
) -> MappedLocalTime<DateTime<Tz>> {
|
||||
match self {
|
||||
LocalResult::Single(d) => d
|
||||
MappedLocalTime::Single(d) => d
|
||||
.and_hms_nano_opt(hour, min, sec, nano)
|
||||
.map_or(LocalResult::None, LocalResult::Single),
|
||||
_ => LocalResult::None,
|
||||
.map_or(MappedLocalTime::None, MappedLocalTime::Single),
|
||||
_ => MappedLocalTime::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> LocalResult<T> {
|
||||
/// Returns the single unique conversion result, or panics accordingly.
|
||||
impl<T: fmt::Debug> MappedLocalTime<T> {
|
||||
/// Returns a single unique conversion result or panics.
|
||||
///
|
||||
/// `unwrap()` is best combined with time zone types where the mapping can never fail like
|
||||
/// [`Utc`] and [`FixedOffset`]. Note that for [`FixedOffset`] there is a rare case where a
|
||||
/// resulting [`DateTime`] can be out of range.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the local time falls within a _fold_ or a _gap_ in the local time, and on any
|
||||
/// error that may have been returned by the type implementing [`TimeZone`].
|
||||
#[must_use]
|
||||
#[track_caller]
|
||||
pub fn unwrap(self) -> T {
|
||||
match self {
|
||||
LocalResult::None => panic!("No such local time"),
|
||||
LocalResult::Single(t) => t,
|
||||
LocalResult::Ambiguous(t1, t2) => {
|
||||
MappedLocalTime::None => panic!("No such local time"),
|
||||
MappedLocalTime::Single(t) => t,
|
||||
MappedLocalTime::Ambiguous(t1, t2) => {
|
||||
panic!("Ambiguous local time, ranging from {:?} to {:?}", t1, t2)
|
||||
}
|
||||
}
|
||||
@@ -187,14 +275,34 @@ pub trait Offset: Sized + Clone + fmt::Debug {
|
||||
|
||||
/// The time zone.
|
||||
///
|
||||
/// The methods here are the primarily constructors for [`Date`](../struct.Date.html) and
|
||||
/// [`DateTime`](../struct.DateTime.html) types.
|
||||
/// The methods here are the primary constructors for the [`DateTime`] type.
|
||||
pub trait TimeZone: Sized + Clone {
|
||||
/// An associated offset type.
|
||||
/// This type is used to store the actual offset in date and time types.
|
||||
/// The original `TimeZone` value can be recovered via `TimeZone::from_offset`.
|
||||
type Offset: Offset;
|
||||
|
||||
/// Make a new `DateTime` from year, month, day, time components and current time zone.
|
||||
///
|
||||
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
|
||||
///
|
||||
/// Returns `MappedLocalTime::None` on invalid input data.
|
||||
fn with_ymd_and_hms(
|
||||
&self,
|
||||
year: i32,
|
||||
month: u32,
|
||||
day: u32,
|
||||
hour: u32,
|
||||
min: u32,
|
||||
sec: u32,
|
||||
) -> MappedLocalTime<DateTime<Self>> {
|
||||
match NaiveDate::from_ymd_opt(year, month, day).and_then(|d| d.and_hms_opt(hour, min, sec))
|
||||
{
|
||||
Some(dt) => self.from_local_datetime(&dt),
|
||||
None => MappedLocalTime::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `Date` from year, month, day and the current time zone.
|
||||
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
|
||||
///
|
||||
@@ -202,14 +310,8 @@ pub trait TimeZone: Sized + Clone {
|
||||
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||
///
|
||||
/// Panics on the out-of-range date, invalid month and/or day.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{Utc, TimeZone};
|
||||
///
|
||||
/// assert_eq!(Utc.ymd(2015, 5, 15).to_string(), "2015-05-15UTC");
|
||||
/// ~~~~
|
||||
#[deprecated(since = "0.4.23", note = "use `with_ymd_and_hms()` instead")]
|
||||
#[allow(deprecated)]
|
||||
fn ymd(&self, year: i32, month: u32, day: u32) -> Date<Self> {
|
||||
self.ymd_opt(year, month, day).unwrap()
|
||||
}
|
||||
@@ -221,19 +323,12 @@ pub trait TimeZone: Sized + Clone {
|
||||
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||
///
|
||||
/// Returns `None` on the out-of-range date, invalid month and/or day.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{Utc, LocalResult, TimeZone};
|
||||
///
|
||||
/// assert_eq!(Utc.ymd_opt(2015, 5, 15).unwrap().to_string(), "2015-05-15UTC");
|
||||
/// assert_eq!(Utc.ymd_opt(2000, 0, 0), LocalResult::None);
|
||||
/// ~~~~
|
||||
fn ymd_opt(&self, year: i32, month: u32, day: u32) -> LocalResult<Date<Self>> {
|
||||
#[deprecated(since = "0.4.23", note = "use `with_ymd_and_hms()` instead")]
|
||||
#[allow(deprecated)]
|
||||
fn ymd_opt(&self, year: i32, month: u32, day: u32) -> MappedLocalTime<Date<Self>> {
|
||||
match NaiveDate::from_ymd_opt(year, month, day) {
|
||||
Some(d) => self.from_local_date(&d),
|
||||
None => LocalResult::None,
|
||||
None => MappedLocalTime::None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,14 +339,11 @@ pub trait TimeZone: Sized + Clone {
|
||||
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||
///
|
||||
/// Panics on the out-of-range date and/or invalid DOY.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{Utc, TimeZone};
|
||||
///
|
||||
/// assert_eq!(Utc.yo(2015, 135).to_string(), "2015-05-15UTC");
|
||||
/// ~~~~
|
||||
#[deprecated(
|
||||
since = "0.4.23",
|
||||
note = "use `from_local_datetime()` with a `NaiveDateTime` instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
fn yo(&self, year: i32, ordinal: u32) -> Date<Self> {
|
||||
self.yo_opt(year, ordinal).unwrap()
|
||||
}
|
||||
@@ -263,10 +355,15 @@ pub trait TimeZone: Sized + Clone {
|
||||
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||
///
|
||||
/// Returns `None` on the out-of-range date and/or invalid DOY.
|
||||
fn yo_opt(&self, year: i32, ordinal: u32) -> LocalResult<Date<Self>> {
|
||||
#[deprecated(
|
||||
since = "0.4.23",
|
||||
note = "use `from_local_datetime()` with a `NaiveDateTime` instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
fn yo_opt(&self, year: i32, ordinal: u32) -> MappedLocalTime<Date<Self>> {
|
||||
match NaiveDate::from_yo_opt(year, ordinal) {
|
||||
Some(d) => self.from_local_date(&d),
|
||||
None => LocalResult::None,
|
||||
None => MappedLocalTime::None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,14 +376,11 @@ pub trait TimeZone: Sized + Clone {
|
||||
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||
///
|
||||
/// Panics on the out-of-range date and/or invalid week number.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{Utc, Weekday, TimeZone};
|
||||
///
|
||||
/// assert_eq!(Utc.isoywd(2015, 20, Weekday::Fri).to_string(), "2015-05-15UTC");
|
||||
/// ~~~~
|
||||
#[deprecated(
|
||||
since = "0.4.23",
|
||||
note = "use `from_local_datetime()` with a `NaiveDateTime` instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
fn isoywd(&self, year: i32, week: u32, weekday: Weekday) -> Date<Self> {
|
||||
self.isoywd_opt(year, week, weekday).unwrap()
|
||||
}
|
||||
@@ -300,10 +394,15 @@ pub trait TimeZone: Sized + Clone {
|
||||
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||
///
|
||||
/// Returns `None` on the out-of-range date and/or invalid week number.
|
||||
fn isoywd_opt(&self, year: i32, week: u32, weekday: Weekday) -> LocalResult<Date<Self>> {
|
||||
#[deprecated(
|
||||
since = "0.4.23",
|
||||
note = "use `from_local_datetime()` with a `NaiveDateTime` instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
fn isoywd_opt(&self, year: i32, week: u32, weekday: Weekday) -> MappedLocalTime<Date<Self>> {
|
||||
match NaiveDate::from_isoywd_opt(year, week, weekday) {
|
||||
Some(d) => self.from_local_date(&d),
|
||||
None => LocalResult::None,
|
||||
None => MappedLocalTime::None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,16 +410,15 @@ pub trait TimeZone: Sized + Clone {
|
||||
/// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp")
|
||||
/// and the number of nanoseconds since the last whole non-leap second.
|
||||
///
|
||||
/// The nanosecond part can exceed 1,000,000,000 in order to represent a
|
||||
/// [leap second](crate::NaiveTime#leap-second-handling), but only when `secs % 60 == 59`.
|
||||
/// (The true "UNIX timestamp" cannot represent a leap second unambiguously.)
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics on the out-of-range number of seconds and/or invalid nanosecond,
|
||||
/// for a non-panicking version see [`timestamp_opt`](#method.timestamp_opt).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{Utc, TimeZone};
|
||||
///
|
||||
/// assert_eq!(Utc.timestamp(1431648000, 0).to_string(), "2015-05-15 00:00:00 UTC");
|
||||
/// ~~~~
|
||||
#[deprecated(since = "0.4.23", note = "use `timestamp_opt()` instead")]
|
||||
fn timestamp(&self, secs: i64, nsecs: u32) -> DateTime<Self> {
|
||||
self.timestamp_opt(secs, nsecs).unwrap()
|
||||
}
|
||||
@@ -329,12 +427,26 @@ pub trait TimeZone: Sized + Clone {
|
||||
/// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp")
|
||||
/// and the number of nanoseconds since the last whole non-leap second.
|
||||
///
|
||||
/// Returns `LocalResult::None` on out-of-range number of seconds and/or
|
||||
/// invalid nanosecond, otherwise always returns `LocalResult::Single`.
|
||||
fn timestamp_opt(&self, secs: i64, nsecs: u32) -> LocalResult<DateTime<Self>> {
|
||||
match NaiveDateTime::from_timestamp_opt(secs, nsecs) {
|
||||
Some(dt) => LocalResult::Single(self.from_utc_datetime(&dt)),
|
||||
None => LocalResult::None,
|
||||
/// The nanosecond part can exceed 1,000,000,000 in order to represent a
|
||||
/// [leap second](crate::NaiveTime#leap-second-handling), but only when `secs % 60 == 59`.
|
||||
/// (The true "UNIX timestamp" cannot represent a leap second unambiguously.)
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `MappedLocalTime::None` on out-of-range number of seconds and/or
|
||||
/// invalid nanosecond, otherwise always returns `MappedLocalTime::Single`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use chrono::{TimeZone, Utc};
|
||||
///
|
||||
/// assert_eq!(Utc.timestamp_opt(1431648000, 0).unwrap().to_string(), "2015-05-15 00:00:00 UTC");
|
||||
/// ```
|
||||
fn timestamp_opt(&self, secs: i64, nsecs: u32) -> MappedLocalTime<DateTime<Self>> {
|
||||
match DateTime::from_timestamp(secs, nsecs) {
|
||||
Some(dt) => MappedLocalTime::Single(self.from_utc_datetime(&dt.naive_utc())),
|
||||
None => MappedLocalTime::None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,14 +455,7 @@ pub trait TimeZone: Sized + Clone {
|
||||
///
|
||||
/// Panics on out-of-range number of milliseconds for a non-panicking
|
||||
/// version see [`timestamp_millis_opt`](#method.timestamp_millis_opt).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{Utc, TimeZone};
|
||||
///
|
||||
/// assert_eq!(Utc.timestamp_millis(1431648000).timestamp(), 1431648);
|
||||
/// ~~~~
|
||||
#[deprecated(since = "0.4.23", note = "use `timestamp_millis_opt()` instead")]
|
||||
fn timestamp_millis(&self, millis: i64) -> DateTime<Self> {
|
||||
self.timestamp_millis_opt(millis).unwrap()
|
||||
}
|
||||
@@ -359,60 +464,78 @@ pub trait TimeZone: Sized + Clone {
|
||||
/// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp").
|
||||
///
|
||||
///
|
||||
/// Returns `LocalResult::None` on out-of-range number of milliseconds
|
||||
/// Returns `MappedLocalTime::None` on out-of-range number of milliseconds
|
||||
/// and/or invalid nanosecond, otherwise always returns
|
||||
/// `LocalResult::Single`.
|
||||
/// `MappedLocalTime::Single`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{Utc, TimeZone, LocalResult};
|
||||
/// ```
|
||||
/// use chrono::{MappedLocalTime, TimeZone, Utc};
|
||||
/// match Utc.timestamp_millis_opt(1431648000) {
|
||||
/// LocalResult::Single(dt) => assert_eq!(dt.timestamp(), 1431648),
|
||||
/// MappedLocalTime::Single(dt) => assert_eq!(dt.timestamp(), 1431648),
|
||||
/// _ => panic!("Incorrect timestamp_millis"),
|
||||
/// };
|
||||
/// ~~~~
|
||||
fn timestamp_millis_opt(&self, millis: i64) -> LocalResult<DateTime<Self>> {
|
||||
let (mut secs, mut millis) = (millis / 1000, millis % 1000);
|
||||
if millis < 0 {
|
||||
secs -= 1;
|
||||
millis += 1000;
|
||||
/// ```
|
||||
fn timestamp_millis_opt(&self, millis: i64) -> MappedLocalTime<DateTime<Self>> {
|
||||
match DateTime::from_timestamp_millis(millis) {
|
||||
Some(dt) => MappedLocalTime::Single(self.from_utc_datetime(&dt.naive_utc())),
|
||||
None => MappedLocalTime::None,
|
||||
}
|
||||
self.timestamp_opt(secs, millis as u32 * 1_000_000)
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` from the number of non-leap nanoseconds
|
||||
/// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp").
|
||||
///
|
||||
/// Unlike [`timestamp_millis`](#method.timestamp_millis), this never
|
||||
/// panics.
|
||||
/// Unlike [`timestamp_millis_opt`](#method.timestamp_millis_opt), this never fails.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{Utc, TimeZone};
|
||||
/// ```
|
||||
/// use chrono::{TimeZone, Utc};
|
||||
///
|
||||
/// assert_eq!(Utc.timestamp_nanos(1431648000000000).timestamp(), 1431648);
|
||||
/// ~~~~
|
||||
/// ```
|
||||
fn timestamp_nanos(&self, nanos: i64) -> DateTime<Self> {
|
||||
let (mut secs, mut nanos) = (nanos / 1_000_000_000, nanos % 1_000_000_000);
|
||||
if nanos < 0 {
|
||||
secs -= 1;
|
||||
nanos += 1_000_000_000;
|
||||
}
|
||||
self.timestamp_opt(secs, nanos as u32).unwrap()
|
||||
self.from_utc_datetime(&DateTime::from_timestamp_nanos(nanos).naive_utc())
|
||||
}
|
||||
|
||||
/// Parses a string with the specified format string and
|
||||
/// returns a `DateTime` with the current offset.
|
||||
/// See the [`format::strftime` module](../format/strftime/index.html)
|
||||
/// on the supported escape sequences.
|
||||
/// Makes a new `DateTime` from the number of non-leap microseconds
|
||||
/// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp").
|
||||
///
|
||||
/// If the format does not include offsets, the current offset is assumed;
|
||||
/// otherwise the input should have a matching UTC offset.
|
||||
/// # Example
|
||||
///
|
||||
/// See also `DateTime::parse_from_str` which gives a local `DateTime`
|
||||
/// with parsed `FixedOffset`.
|
||||
/// ```
|
||||
/// use chrono::{TimeZone, Utc};
|
||||
///
|
||||
/// assert_eq!(Utc.timestamp_micros(1431648000000).unwrap().timestamp(), 1431648);
|
||||
/// ```
|
||||
fn timestamp_micros(&self, micros: i64) -> MappedLocalTime<DateTime<Self>> {
|
||||
match DateTime::from_timestamp_micros(micros) {
|
||||
Some(dt) => MappedLocalTime::Single(self.from_utc_datetime(&dt.naive_utc())),
|
||||
None => MappedLocalTime::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a string with the specified format string and returns a
|
||||
/// `DateTime` with the current offset.
|
||||
///
|
||||
/// See the [`crate::format::strftime`] module on the
|
||||
/// supported escape sequences.
|
||||
///
|
||||
/// If the to-be-parsed string includes an offset, it *must* match the
|
||||
/// offset of the TimeZone, otherwise an error will be returned.
|
||||
///
|
||||
/// See also [`DateTime::parse_from_str`] which gives a [`DateTime`] with
|
||||
/// parsed [`FixedOffset`].
|
||||
///
|
||||
/// See also [`NaiveDateTime::parse_from_str`] which gives a [`NaiveDateTime`] without
|
||||
/// an offset, but can be converted to a [`DateTime`] with [`NaiveDateTime::and_utc`] or
|
||||
/// [`NaiveDateTime::and_local_timezone`].
|
||||
#[deprecated(
|
||||
since = "0.4.29",
|
||||
note = "use `DateTime::parse_from_str` or `NaiveDateTime::parse_from_str` with `and_utc()` or `and_local_timezone()` instead"
|
||||
)]
|
||||
fn datetime_from_str(&self, s: &str, fmt: &str) -> ParseResult<DateTime<Self>> {
|
||||
let mut parsed = Parsed::new();
|
||||
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
|
||||
@@ -423,13 +546,16 @@ pub trait TimeZone: Sized + Clone {
|
||||
fn from_offset(offset: &Self::Offset) -> Self;
|
||||
|
||||
/// Creates the offset(s) for given local `NaiveDate` if possible.
|
||||
fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<Self::Offset>;
|
||||
fn offset_from_local_date(&self, local: &NaiveDate) -> MappedLocalTime<Self::Offset>;
|
||||
|
||||
/// Creates the offset(s) for given local `NaiveDateTime` if possible.
|
||||
fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<Self::Offset>;
|
||||
fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> MappedLocalTime<Self::Offset>;
|
||||
|
||||
/// Converts the local `NaiveDate` to the timezone-aware `Date` if possible.
|
||||
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Self>> {
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
#[deprecated(since = "0.4.23", note = "use `from_local_datetime()` instead")]
|
||||
#[allow(deprecated)]
|
||||
fn from_local_date(&self, local: &NaiveDate) -> MappedLocalTime<Date<Self>> {
|
||||
self.offset_from_local_date(local).map(|offset| {
|
||||
// since FixedOffset is within +/- 1 day, the date is never affected
|
||||
Date::from_utc(*local, offset)
|
||||
@@ -437,9 +563,13 @@ pub trait TimeZone: Sized + Clone {
|
||||
}
|
||||
|
||||
/// Converts the local `NaiveDateTime` to the timezone-aware `DateTime` if possible.
|
||||
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Self>> {
|
||||
self.offset_from_local_datetime(local)
|
||||
.map(|offset| DateTime::from_utc(*local - offset.fix(), offset))
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn from_local_datetime(&self, local: &NaiveDateTime) -> MappedLocalTime<DateTime<Self>> {
|
||||
self.offset_from_local_datetime(local).and_then(|off| {
|
||||
local
|
||||
.checked_sub_offset(off.fix())
|
||||
.map(|dt| DateTime::from_naive_utc_and_offset(dt, off))
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates the offset for given UTC `NaiveDate`. This cannot fail.
|
||||
@@ -450,48 +580,68 @@ pub trait TimeZone: Sized + Clone {
|
||||
|
||||
/// Converts the UTC `NaiveDate` to the local time.
|
||||
/// The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
#[deprecated(since = "0.4.23", note = "use `from_utc_datetime()` instead")]
|
||||
#[allow(deprecated)]
|
||||
fn from_utc_date(&self, utc: &NaiveDate) -> Date<Self> {
|
||||
Date::from_utc(*utc, self.offset_from_utc_date(utc))
|
||||
}
|
||||
|
||||
/// Converts the UTC `NaiveDateTime` to the local time.
|
||||
/// The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Self> {
|
||||
DateTime::from_utc(*utc, self.offset_from_utc_datetime(utc))
|
||||
DateTime::from_naive_utc_and_offset(*utc, self.offset_from_utc_datetime(utc))
|
||||
}
|
||||
}
|
||||
|
||||
mod fixed;
|
||||
#[cfg(feature = "clock")]
|
||||
mod local;
|
||||
mod utc;
|
||||
|
||||
pub use self::fixed::FixedOffset;
|
||||
#[cfg(feature = "clock")]
|
||||
pub use self::local::Local;
|
||||
pub use self::utc::Utc;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_fixed_offset_min_max_dates() {
|
||||
for offset_hour in -23..=23 {
|
||||
dbg!(offset_hour);
|
||||
let offset = FixedOffset::east_opt(offset_hour * 60 * 60).unwrap();
|
||||
|
||||
let local_max = offset.from_utc_datetime(&NaiveDateTime::MAX);
|
||||
assert_eq!(local_max.naive_utc(), NaiveDateTime::MAX);
|
||||
let local_min = offset.from_utc_datetime(&NaiveDateTime::MIN);
|
||||
assert_eq!(local_min.naive_utc(), NaiveDateTime::MIN);
|
||||
|
||||
let local_max = offset.from_local_datetime(&NaiveDateTime::MAX);
|
||||
if offset_hour >= 0 {
|
||||
assert_eq!(local_max.unwrap().naive_local(), NaiveDateTime::MAX);
|
||||
} else {
|
||||
assert_eq!(local_max, MappedLocalTime::None);
|
||||
}
|
||||
let local_min = offset.from_local_datetime(&NaiveDateTime::MIN);
|
||||
if offset_hour <= 0 {
|
||||
assert_eq!(local_min.unwrap().naive_local(), NaiveDateTime::MIN);
|
||||
} else {
|
||||
assert_eq!(local_min, MappedLocalTime::None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negative_millis() {
|
||||
let dt = Utc.timestamp_millis(-1000);
|
||||
let dt = Utc.timestamp_millis_opt(-1000).unwrap();
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:59 UTC");
|
||||
let dt = Utc.timestamp_millis(-7000);
|
||||
let dt = Utc.timestamp_millis_opt(-7000).unwrap();
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:53 UTC");
|
||||
let dt = Utc.timestamp_millis(-7001);
|
||||
let dt = Utc.timestamp_millis_opt(-7001).unwrap();
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:52.999 UTC");
|
||||
let dt = Utc.timestamp_millis(-7003);
|
||||
let dt = Utc.timestamp_millis_opt(-7003).unwrap();
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:52.997 UTC");
|
||||
let dt = Utc.timestamp_millis(-999);
|
||||
let dt = Utc.timestamp_millis_opt(-999).unwrap();
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:59.001 UTC");
|
||||
let dt = Utc.timestamp_millis(-1);
|
||||
let dt = Utc.timestamp_millis_opt(-1).unwrap();
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:59.999 UTC");
|
||||
let dt = Utc.timestamp_millis(-60000);
|
||||
let dt = Utc.timestamp_millis_opt(-60000).unwrap();
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:00 UTC");
|
||||
let dt = Utc.timestamp_millis(-3600000);
|
||||
let dt = Utc.timestamp_millis_opt(-3600000).unwrap();
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:00:00 UTC");
|
||||
|
||||
for (millis, expected) in &[
|
||||
@@ -500,7 +650,7 @@ mod tests {
|
||||
(-7003, "1969-12-31 23:59:52.997 UTC"),
|
||||
] {
|
||||
match Utc.timestamp_millis_opt(*millis) {
|
||||
LocalResult::Single(dt) => {
|
||||
MappedLocalTime::Single(dt) => {
|
||||
assert_eq!(dt.to_string(), *expected);
|
||||
}
|
||||
e => panic!("Got {:?} instead of an okay answer", e),
|
||||
@@ -524,8 +674,22 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_nanos_never_panics() {
|
||||
Utc.timestamp_nanos(i64::max_value());
|
||||
Utc.timestamp_nanos(i64::MAX);
|
||||
Utc.timestamp_nanos(i64::default());
|
||||
Utc.timestamp_nanos(i64::min_value());
|
||||
Utc.timestamp_nanos(i64::MIN);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negative_micros() {
|
||||
let dt = Utc.timestamp_micros(-1_000_000).unwrap();
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:59 UTC");
|
||||
let dt = Utc.timestamp_micros(-999_999).unwrap();
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:59.000001 UTC");
|
||||
let dt = Utc.timestamp_micros(-1).unwrap();
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:59.999999 UTC");
|
||||
let dt = Utc.timestamp_micros(-60_000_000).unwrap();
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:00 UTC");
|
||||
let dt = Utc.timestamp_micros(-3_600_000_000).unwrap();
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:00:00 UTC");
|
||||
}
|
||||
}
|
||||
|
||||
104
third_party/rust/chrono/src/offset/utc.rs
vendored
104
third_party/rust/chrono/src/offset/utc.rs
vendored
@@ -4,16 +4,24 @@
|
||||
//! The UTC (Coordinated Universal Time) time zone.
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use super::{FixedOffset, LocalResult, Offset, TimeZone};
|
||||
use naive::{NaiveDate, NaiveDateTime};
|
||||
#[cfg(all(
|
||||
feature = "clock",
|
||||
not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))
|
||||
feature = "now",
|
||||
not(all(
|
||||
target_arch = "wasm32",
|
||||
feature = "wasmbind",
|
||||
not(any(target_os = "emscripten", target_os = "wasi"))
|
||||
))
|
||||
))]
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
#[cfg(feature = "clock")]
|
||||
use {Date, DateTime};
|
||||
|
||||
#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))]
|
||||
use rkyv::{Archive, Deserialize, Serialize};
|
||||
|
||||
use super::{FixedOffset, MappedLocalTime, Offset, TimeZone};
|
||||
use crate::naive::{NaiveDate, NaiveDateTime};
|
||||
#[cfg(feature = "now")]
|
||||
#[allow(deprecated)]
|
||||
use crate::{Date, DateTime};
|
||||
|
||||
/// The UTC time zone. This is the most efficient time zone when you don't need the local time.
|
||||
/// It is also used as an offset (which is also a dummy type).
|
||||
@@ -24,35 +32,79 @@ use {Date, DateTime};
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{DateTime, TimeZone, NaiveDateTime, Utc};
|
||||
/// ```
|
||||
/// use chrono::{DateTime, TimeZone, Utc};
|
||||
///
|
||||
/// let dt = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(61, 0), Utc);
|
||||
/// let dt = DateTime::from_timestamp(61, 0).unwrap();
|
||||
///
|
||||
/// assert_eq!(Utc.timestamp(61, 0), dt);
|
||||
/// assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 1, 1), dt);
|
||||
/// ~~~~
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
/// assert_eq!(Utc.timestamp_opt(61, 0).unwrap(), dt);
|
||||
/// assert_eq!(Utc.with_ymd_and_hms(1970, 1, 1, 0, 1, 1).unwrap(), dt);
|
||||
/// ```
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(
|
||||
any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"),
|
||||
derive(Archive, Deserialize, Serialize),
|
||||
archive(compare(PartialEq)),
|
||||
archive_attr(derive(Clone, Copy, PartialEq, Eq, Debug, Hash))
|
||||
)]
|
||||
#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
|
||||
#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))]
|
||||
pub struct Utc;
|
||||
|
||||
#[cfg(feature = "clock")]
|
||||
#[cfg(feature = "now")]
|
||||
impl Utc {
|
||||
/// Returns a `Date` which corresponds to the current date.
|
||||
#[deprecated(
|
||||
since = "0.4.23",
|
||||
note = "use `Utc::now()` instead, potentially with `.date_naive()`"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
#[must_use]
|
||||
pub fn today() -> Date<Utc> {
|
||||
Utc::now().date()
|
||||
}
|
||||
|
||||
/// Returns a `DateTime` which corresponds to the current date.
|
||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
|
||||
/// Returns a `DateTime<Utc>` which corresponds to the current date and time in UTC.
|
||||
///
|
||||
/// See also the similar [`Local::now()`] which returns `DateTime<Local>`, i.e. the local date
|
||||
/// and time including offset from UTC.
|
||||
///
|
||||
/// [`Local::now()`]: crate::Local::now
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # #![allow(unused_variables)]
|
||||
/// # use chrono::{FixedOffset, Utc};
|
||||
/// // Current time in UTC
|
||||
/// let now_utc = Utc::now();
|
||||
///
|
||||
/// // Current date in UTC
|
||||
/// let today_utc = now_utc.date_naive();
|
||||
///
|
||||
/// // Current time in some timezone (let's use +05:00)
|
||||
/// let offset = FixedOffset::east_opt(5 * 60 * 60).unwrap();
|
||||
/// let now_with_offset = Utc::now().with_timezone(&offset);
|
||||
/// ```
|
||||
#[cfg(not(all(
|
||||
target_arch = "wasm32",
|
||||
feature = "wasmbind",
|
||||
not(any(target_os = "emscripten", target_os = "wasi"))
|
||||
)))]
|
||||
#[must_use]
|
||||
pub fn now() -> DateTime<Utc> {
|
||||
let now =
|
||||
SystemTime::now().duration_since(UNIX_EPOCH).expect("system time before Unix epoch");
|
||||
let naive = NaiveDateTime::from_timestamp(now.as_secs() as i64, now.subsec_nanos() as u32);
|
||||
DateTime::from_utc(naive, Utc)
|
||||
DateTime::from_timestamp(now.as_secs() as i64, now.subsec_nanos()).unwrap()
|
||||
}
|
||||
|
||||
/// Returns a `DateTime` which corresponds to the current date.
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
|
||||
/// Returns a `DateTime` which corresponds to the current date and time.
|
||||
#[cfg(all(
|
||||
target_arch = "wasm32",
|
||||
feature = "wasmbind",
|
||||
not(any(target_os = "emscripten", target_os = "wasi"))
|
||||
))]
|
||||
#[must_use]
|
||||
pub fn now() -> DateTime<Utc> {
|
||||
let now = js_sys::Date::new_0();
|
||||
DateTime::<Utc>::from(now)
|
||||
@@ -66,11 +118,11 @@ impl TimeZone for Utc {
|
||||
Utc
|
||||
}
|
||||
|
||||
fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult<Utc> {
|
||||
LocalResult::Single(Utc)
|
||||
fn offset_from_local_date(&self, _local: &NaiveDate) -> MappedLocalTime<Utc> {
|
||||
MappedLocalTime::Single(Utc)
|
||||
}
|
||||
fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<Utc> {
|
||||
LocalResult::Single(Utc)
|
||||
fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> MappedLocalTime<Utc> {
|
||||
MappedLocalTime::Single(Utc)
|
||||
}
|
||||
|
||||
fn offset_from_utc_date(&self, _utc: &NaiveDate) -> Utc {
|
||||
@@ -83,7 +135,7 @@ impl TimeZone for Utc {
|
||||
|
||||
impl Offset for Utc {
|
||||
fn fix(&self) -> FixedOffset {
|
||||
FixedOffset::east(0)
|
||||
FixedOffset::east_opt(0).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
684
third_party/rust/chrono/src/oldtime.rs
vendored
684
third_party/rust/chrono/src/oldtime.rs
vendored
@@ -1,684 +0,0 @@
|
||||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <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 core::ops::{Add, Div, Mul, Neg, Sub};
|
||||
use core::time::Duration as StdDuration;
|
||||
use core::{fmt, i64};
|
||||
#[cfg(any(feature = "std", test))]
|
||||
use std::error::Error;
|
||||
|
||||
/// The number of nanoseconds in a microsecond.
|
||||
const NANOS_PER_MICRO: i32 = 1000;
|
||||
/// The number of nanoseconds in a millisecond.
|
||||
const NANOS_PER_MILLI: i32 = 1000_000;
|
||||
/// The number of nanoseconds in seconds.
|
||||
const NANOS_PER_SEC: i32 = 1_000_000_000;
|
||||
/// The number of microseconds per second.
|
||||
const MICROS_PER_SEC: i64 = 1000_000;
|
||||
/// The number of milliseconds per second.
|
||||
const MILLIS_PER_SEC: i64 = 1000;
|
||||
/// The number of seconds in a minute.
|
||||
const SECS_PER_MINUTE: i64 = 60;
|
||||
/// The number of seconds in an hour.
|
||||
const SECS_PER_HOUR: i64 = 3600;
|
||||
/// The number of (non-leap) seconds in days.
|
||||
const SECS_PER_DAY: i64 = 86400;
|
||||
/// The number of (non-leap) seconds in a week.
|
||||
const SECS_PER_WEEK: i64 = 604800;
|
||||
|
||||
macro_rules! try_opt {
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
Some(v) => v,
|
||||
None => return None,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// ISO 8601 time duration with nanosecond precision.
|
||||
/// This also allows for the negative duration; see individual methods for details.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
pub struct Duration {
|
||||
secs: i64,
|
||||
nanos: i32, // Always 0 <= nanos < NANOS_PER_SEC
|
||||
}
|
||||
|
||||
/// The minimum possible `Duration`: `i64::MIN` milliseconds.
|
||||
pub const MIN: Duration = Duration {
|
||||
secs: i64::MIN / MILLIS_PER_SEC - 1,
|
||||
nanos: NANOS_PER_SEC + (i64::MIN % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI,
|
||||
};
|
||||
|
||||
/// The maximum possible `Duration`: `i64::MAX` milliseconds.
|
||||
pub const MAX: Duration = Duration {
|
||||
secs: i64::MAX / MILLIS_PER_SEC,
|
||||
nanos: (i64::MAX % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI,
|
||||
};
|
||||
|
||||
impl Duration {
|
||||
/// Makes a new `Duration` with given number of weeks.
|
||||
/// Equivalent to `Duration::seconds(weeks * 7 * 24 * 60 * 60)` with overflow checks.
|
||||
/// Panics when the duration is out of bounds.
|
||||
#[inline]
|
||||
pub fn weeks(weeks: i64) -> Duration {
|
||||
let secs = weeks.checked_mul(SECS_PER_WEEK).expect("Duration::weeks out of bounds");
|
||||
Duration::seconds(secs)
|
||||
}
|
||||
|
||||
/// Makes a new `Duration` with given number of days.
|
||||
/// Equivalent to `Duration::seconds(days * 24 * 60 * 60)` with overflow checks.
|
||||
/// Panics when the duration is out of bounds.
|
||||
#[inline]
|
||||
pub fn days(days: i64) -> Duration {
|
||||
let secs = days.checked_mul(SECS_PER_DAY).expect("Duration::days out of bounds");
|
||||
Duration::seconds(secs)
|
||||
}
|
||||
|
||||
/// Makes a new `Duration` with given number of hours.
|
||||
/// Equivalent to `Duration::seconds(hours * 60 * 60)` with overflow checks.
|
||||
/// Panics when the duration is out of bounds.
|
||||
#[inline]
|
||||
pub fn hours(hours: i64) -> Duration {
|
||||
let secs = hours.checked_mul(SECS_PER_HOUR).expect("Duration::hours ouf of bounds");
|
||||
Duration::seconds(secs)
|
||||
}
|
||||
|
||||
/// Makes a new `Duration` with given number of minutes.
|
||||
/// Equivalent to `Duration::seconds(minutes * 60)` with overflow checks.
|
||||
/// Panics when the duration is out of bounds.
|
||||
#[inline]
|
||||
pub fn minutes(minutes: i64) -> Duration {
|
||||
let secs = minutes.checked_mul(SECS_PER_MINUTE).expect("Duration::minutes out of bounds");
|
||||
Duration::seconds(secs)
|
||||
}
|
||||
|
||||
/// Makes a new `Duration` with given number of seconds.
|
||||
/// Panics when the duration is more than `i64::MAX` seconds
|
||||
/// or less than `i64::MIN` seconds.
|
||||
#[inline]
|
||||
pub fn seconds(seconds: i64) -> Duration {
|
||||
let d = Duration { secs: seconds, nanos: 0 };
|
||||
if d < MIN || d > MAX {
|
||||
panic!("Duration::seconds out of bounds");
|
||||
}
|
||||
d
|
||||
}
|
||||
|
||||
/// Makes a new `Duration` with given number of milliseconds.
|
||||
#[inline]
|
||||
pub fn milliseconds(milliseconds: i64) -> Duration {
|
||||
let (secs, millis) = div_mod_floor_64(milliseconds, MILLIS_PER_SEC);
|
||||
let nanos = millis as i32 * NANOS_PER_MILLI;
|
||||
Duration { secs: secs, nanos: nanos }
|
||||
}
|
||||
|
||||
/// Makes a new `Duration` with given number of microseconds.
|
||||
#[inline]
|
||||
pub fn microseconds(microseconds: i64) -> Duration {
|
||||
let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
|
||||
let nanos = micros as i32 * NANOS_PER_MICRO;
|
||||
Duration { secs: secs, nanos: nanos }
|
||||
}
|
||||
|
||||
/// Makes a new `Duration` with given number of nanoseconds.
|
||||
#[inline]
|
||||
pub fn nanoseconds(nanos: i64) -> Duration {
|
||||
let (secs, nanos) = div_mod_floor_64(nanos, NANOS_PER_SEC as i64);
|
||||
Duration { secs: secs, nanos: nanos as i32 }
|
||||
}
|
||||
|
||||
/// Returns the total number of whole weeks in the duration.
|
||||
#[inline]
|
||||
pub fn num_weeks(&self) -> i64 {
|
||||
self.num_days() / 7
|
||||
}
|
||||
|
||||
/// Returns the total number of whole days in the duration.
|
||||
pub fn num_days(&self) -> i64 {
|
||||
self.num_seconds() / SECS_PER_DAY
|
||||
}
|
||||
|
||||
/// Returns the total number of whole hours in the duration.
|
||||
#[inline]
|
||||
pub fn num_hours(&self) -> i64 {
|
||||
self.num_seconds() / SECS_PER_HOUR
|
||||
}
|
||||
|
||||
/// Returns the total number of whole minutes in the duration.
|
||||
#[inline]
|
||||
pub fn num_minutes(&self) -> i64 {
|
||||
self.num_seconds() / SECS_PER_MINUTE
|
||||
}
|
||||
|
||||
/// Returns the total number of whole seconds in the duration.
|
||||
pub fn num_seconds(&self) -> i64 {
|
||||
// If secs is negative, nanos should be subtracted from the duration.
|
||||
if self.secs < 0 && self.nanos > 0 {
|
||||
self.secs + 1
|
||||
} else {
|
||||
self.secs
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of nanoseconds such that
|
||||
/// `nanos_mod_sec() + num_seconds() * NANOS_PER_SEC` is the total number of
|
||||
/// nanoseconds in the duration.
|
||||
fn nanos_mod_sec(&self) -> i32 {
|
||||
if self.secs < 0 && self.nanos > 0 {
|
||||
self.nanos - NANOS_PER_SEC
|
||||
} else {
|
||||
self.nanos
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the total number of whole milliseconds in the duration,
|
||||
pub fn num_milliseconds(&self) -> i64 {
|
||||
// A proper Duration will not overflow, because MIN and MAX are defined
|
||||
// such that the range is exactly i64 milliseconds.
|
||||
let secs_part = self.num_seconds() * MILLIS_PER_SEC;
|
||||
let nanos_part = self.nanos_mod_sec() / NANOS_PER_MILLI;
|
||||
secs_part + nanos_part as i64
|
||||
}
|
||||
|
||||
/// Returns the total number of whole microseconds in the duration,
|
||||
/// or `None` on overflow (exceeding 2^63 microseconds in either direction).
|
||||
pub fn num_microseconds(&self) -> Option<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^63 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)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the duration as an absolute (non-negative) value.
|
||||
#[inline]
|
||||
pub fn abs(&self) -> Duration {
|
||||
Duration { secs: self.secs.abs(), nanos: self.nanos }
|
||||
}
|
||||
|
||||
/// 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))
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Source duration value is out of range for the target type")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
impl Error for OutOfRangeError {
|
||||
#[allow(deprecated)]
|
||||
fn description(&self) -> &str {
|
||||
"out of range error"
|
||||
}
|
||||
}
|
||||
|
||||
// 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, OutOfRangeError, MAX, MIN};
|
||||
use std::time::Duration as StdDuration;
|
||||
use std::{i32, i64};
|
||||
|
||||
#[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(()))
|
||||
);
|
||||
}
|
||||
}
|
||||
928
third_party/rust/chrono/src/round.rs
vendored
928
third_party/rust/chrono/src/round.rs
vendored
File diff suppressed because it is too large
Load Diff
126
third_party/rust/chrono/src/sys.rs
vendored
126
third_party/rust/chrono/src/sys.rs
vendored
@@ -1,126 +0,0 @@
|
||||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <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.
|
||||
|
||||
//! Platform wrappers for converting UTC times to and from the local time zone.
|
||||
//!
|
||||
//! This code was rescued from v0.1 of the time crate, which is no longer
|
||||
//! maintained. It has been substantially stripped down to the bare minimum
|
||||
//! required by chrono.
|
||||
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
#[cfg(any(target_arch = "wasm32", target_env = "sgx"))]
|
||||
#[path = "sys/stub.rs"]
|
||||
mod inner;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[path = "sys/unix.rs"]
|
||||
mod inner;
|
||||
|
||||
#[cfg(windows)]
|
||||
#[path = "sys/windows.rs"]
|
||||
mod inner;
|
||||
|
||||
/// 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}.
|
||||
pub struct Timespec {
|
||||
pub sec: i64,
|
||||
pub nsec: i32,
|
||||
}
|
||||
|
||||
impl Timespec {
|
||||
/// Constructs a timespec representing the current time in UTC.
|
||||
pub fn now() -> Timespec {
|
||||
let st =
|
||||
SystemTime::now().duration_since(UNIX_EPOCH).expect("system time before Unix epoch");
|
||||
Timespec { sec: st.as_secs() as i64, nsec: st.subsec_nanos() as i32 }
|
||||
}
|
||||
|
||||
/// Converts this timespec into the system's local time.
|
||||
pub fn local(self) -> Tm {
|
||||
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,
|
||||
};
|
||||
inner::time_to_local_tm(self.sec, &mut tm);
|
||||
tm.tm_nsec = self.nsec;
|
||||
tm
|
||||
}
|
||||
}
|
||||
|
||||
/// 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?
|
||||
#[cfg(feature = "clock")]
|
||||
#[repr(C)]
|
||||
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 Tm {
|
||||
/// Convert time to the seconds from January 1, 1970
|
||||
pub fn to_timespec(&self) -> Timespec {
|
||||
let sec = match self.tm_utcoff {
|
||||
0 => inner::utc_tm_to_time(self),
|
||||
_ => inner::local_tm_to_time(self),
|
||||
};
|
||||
Timespec { sec: sec, nsec: self.tm_nsec }
|
||||
}
|
||||
}
|
||||
80
third_party/rust/chrono/src/sys/stub.rs
vendored
80
third_party/rust/chrono/src/sys/stub.rs
vendored
@@ -1,80 +0,0 @@
|
||||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <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.
|
||||
|
||||
use super::Tm;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
126
third_party/rust/chrono/src/sys/unix.rs
vendored
126
third_party/rust/chrono/src/sys/unix.rs
vendored
@@ -1,126 +0,0 @@
|
||||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <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.
|
||||
|
||||
use super::Tm;
|
||||
use libc::{self, time_t};
|
||||
use std::io;
|
||||
use std::mem;
|
||||
|
||||
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
||||
extern "C" {
|
||||
static timezone: time_t;
|
||||
static altzone: time_t;
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
||||
fn tzset() {
|
||||
extern "C" {
|
||||
fn tzset();
|
||||
}
|
||||
unsafe { tzset() }
|
||||
}
|
||||
|
||||
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::{remove_var, set_var, var_os};
|
||||
extern "C" {
|
||||
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_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(not(any(
|
||||
all(target_os = "android", target_pointer_width = "32"),
|
||||
target_os = "nacl",
|
||||
target_os = "solaris",
|
||||
target_os = "illumos"
|
||||
)))]
|
||||
use libc::timegm;
|
||||
#[cfg(all(target_os = "android", target_pointer_width = "32"))]
|
||||
use libc::timegm64 as 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 }
|
||||
}
|
||||
131
third_party/rust/chrono/src/sys/windows.rs
vendored
131
third_party/rust/chrono/src/sys/windows.rs
vendored
@@ -1,131 +0,0 @@
|
||||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <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.
|
||||
|
||||
use super::Tm;
|
||||
use std::io;
|
||||
use std::mem;
|
||||
|
||||
use winapi::shared::minwindef::*;
|
||||
use winapi::um::minwinbase::SYSTEMTIME;
|
||||
use winapi::um::timezoneapi::*;
|
||||
|
||||
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_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_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)
|
||||
}
|
||||
}
|
||||
1354
third_party/rust/chrono/src/time_delta.rs
vendored
Normal file
1354
third_party/rust/chrono/src/time_delta.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
393
third_party/rust/chrono/src/traits.rs
vendored
Normal file
393
third_party/rust/chrono/src/traits.rs
vendored
Normal file
@@ -0,0 +1,393 @@
|
||||
use crate::{IsoWeek, Weekday};
|
||||
|
||||
/// The common set of methods for date component.
|
||||
///
|
||||
/// Methods such as [`year`], [`month`], [`day`] and [`weekday`] can be used to get basic
|
||||
/// information about the date.
|
||||
///
|
||||
/// The `with_*` methods can change the date.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// The `with_*` methods can be convenient to change a single component of a date, but they must be
|
||||
/// used with some care. Examples to watch out for:
|
||||
///
|
||||
/// - [`with_year`] changes the year component of a year-month-day value. Don't use this method if
|
||||
/// you want the ordinal to stay the same after changing the year, of if you want the week and
|
||||
/// weekday values to stay the same.
|
||||
/// - Don't combine two `with_*` methods to change two components of the date. For example to
|
||||
/// change both the year and month components of a date. This could fail because an intermediate
|
||||
/// value does not exist, while the final date would be valid.
|
||||
///
|
||||
/// For more complex changes to a date, it is best to use the methods on [`NaiveDate`] to create a
|
||||
/// new value instead of altering an existing date.
|
||||
///
|
||||
/// [`year`]: Datelike::year
|
||||
/// [`month`]: Datelike::month
|
||||
/// [`day`]: Datelike::day
|
||||
/// [`weekday`]: Datelike::weekday
|
||||
/// [`with_year`]: Datelike::with_year
|
||||
/// [`NaiveDate`]: crate::NaiveDate
|
||||
pub trait Datelike: Sized {
|
||||
/// Returns the year number in the [calendar date](./naive/struct.NaiveDate.html#calendar-date).
|
||||
fn year(&self) -> i32;
|
||||
|
||||
/// Returns the absolute year number starting from 1 with a boolean flag,
|
||||
/// which is false when the year predates the epoch (BCE/BC) and true otherwise (CE/AD).
|
||||
#[inline]
|
||||
fn year_ce(&self) -> (bool, u32) {
|
||||
let year = self.year();
|
||||
if year < 1 { (false, (1 - year) as u32) } else { (true, year as u32) }
|
||||
}
|
||||
|
||||
/// Returns the quarter number starting from 1.
|
||||
///
|
||||
/// The return value ranges from 1 to 4.
|
||||
#[inline]
|
||||
fn quarter(&self) -> u32 {
|
||||
(self.month() - 1).div_euclid(3) + 1
|
||||
}
|
||||
|
||||
/// Returns the month number starting from 1.
|
||||
///
|
||||
/// The return value ranges from 1 to 12.
|
||||
fn month(&self) -> u32;
|
||||
|
||||
/// Returns the month number starting from 0.
|
||||
///
|
||||
/// The return value ranges from 0 to 11.
|
||||
fn month0(&self) -> u32;
|
||||
|
||||
/// Returns the day of month starting from 1.
|
||||
///
|
||||
/// The return value ranges from 1 to 31. (The last day of month differs by months.)
|
||||
fn day(&self) -> u32;
|
||||
|
||||
/// Returns the day of month starting from 0.
|
||||
///
|
||||
/// The return value ranges from 0 to 30. (The last day of month differs by months.)
|
||||
fn day0(&self) -> u32;
|
||||
|
||||
/// Returns the day of year starting from 1.
|
||||
///
|
||||
/// The return value ranges from 1 to 366. (The last day of year differs by years.)
|
||||
fn ordinal(&self) -> u32;
|
||||
|
||||
/// Returns the day of year starting from 0.
|
||||
///
|
||||
/// The return value ranges from 0 to 365. (The last day of year differs by years.)
|
||||
fn ordinal0(&self) -> u32;
|
||||
|
||||
/// Returns the day of week.
|
||||
fn weekday(&self) -> Weekday;
|
||||
|
||||
/// Returns the ISO week.
|
||||
fn iso_week(&self) -> IsoWeek;
|
||||
|
||||
/// Makes a new value with the year number changed, while keeping the same month and day.
|
||||
///
|
||||
/// This method assumes you want to work on the date as a year-month-day value. Don't use it if
|
||||
/// you want the ordinal to stay the same after changing the year, of if you want the week and
|
||||
/// weekday values to stay the same.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` when:
|
||||
///
|
||||
/// - The resulting date does not exist (February 29 in a non-leap year).
|
||||
/// - The year is out of range for [`NaiveDate`].
|
||||
/// - In case of [`DateTime<Tz>`] if the resulting date and time fall within a timezone
|
||||
/// transition such as from DST to standard time.
|
||||
///
|
||||
/// [`NaiveDate`]: crate::NaiveDate
|
||||
/// [`DateTime<Tz>`]: crate::DateTime
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use chrono::{Datelike, NaiveDate};
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// NaiveDate::from_ymd_opt(2020, 5, 13).unwrap().with_year(2023).unwrap(),
|
||||
/// NaiveDate::from_ymd_opt(2023, 5, 13).unwrap()
|
||||
/// );
|
||||
/// // Resulting date 2023-02-29 does not exist:
|
||||
/// assert!(NaiveDate::from_ymd_opt(2020, 2, 29).unwrap().with_year(2023).is_none());
|
||||
///
|
||||
/// // Don't use `with_year` if you want the ordinal date to stay the same:
|
||||
/// assert_ne!(
|
||||
/// NaiveDate::from_yo_opt(2020, 100).unwrap().with_year(2023).unwrap(),
|
||||
/// NaiveDate::from_yo_opt(2023, 100).unwrap() // result is 2023-101
|
||||
/// );
|
||||
/// ```
|
||||
fn with_year(&self, year: i32) -> Option<Self>;
|
||||
|
||||
/// Makes a new value with the month number (starting from 1) changed.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` when:
|
||||
///
|
||||
/// - The resulting date does not exist (for example `month(4)` when day of the month is 31).
|
||||
/// - In case of [`DateTime<Tz>`] if the resulting date and time fall within a timezone
|
||||
/// transition such as from DST to standard time.
|
||||
/// - The value for `month` is out of range.
|
||||
///
|
||||
/// [`DateTime<Tz>`]: crate::DateTime
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use chrono::{Datelike, NaiveDate};
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// NaiveDate::from_ymd_opt(2023, 5, 12).unwrap().with_month(9).unwrap(),
|
||||
/// NaiveDate::from_ymd_opt(2023, 9, 12).unwrap()
|
||||
/// );
|
||||
/// // Resulting date 2023-09-31 does not exist:
|
||||
/// assert!(NaiveDate::from_ymd_opt(2023, 5, 31).unwrap().with_month(9).is_none());
|
||||
/// ```
|
||||
///
|
||||
/// Don't combine multiple `Datelike::with_*` methods. The intermediate value may not exist.
|
||||
/// ```
|
||||
/// use chrono::{Datelike, NaiveDate};
|
||||
///
|
||||
/// fn with_year_month(date: NaiveDate, year: i32, month: u32) -> Option<NaiveDate> {
|
||||
/// date.with_year(year)?.with_month(month)
|
||||
/// }
|
||||
/// let d = NaiveDate::from_ymd_opt(2020, 2, 29).unwrap();
|
||||
/// assert!(with_year_month(d, 2019, 1).is_none()); // fails because of invalid intermediate value
|
||||
///
|
||||
/// // Correct version:
|
||||
/// fn with_year_month_fixed(date: NaiveDate, year: i32, month: u32) -> Option<NaiveDate> {
|
||||
/// NaiveDate::from_ymd_opt(year, month, date.day())
|
||||
/// }
|
||||
/// let d = NaiveDate::from_ymd_opt(2020, 2, 29).unwrap();
|
||||
/// assert_eq!(with_year_month_fixed(d, 2019, 1), NaiveDate::from_ymd_opt(2019, 1, 29));
|
||||
/// ```
|
||||
fn with_month(&self, month: u32) -> Option<Self>;
|
||||
|
||||
/// Makes a new value with the month number (starting from 0) changed.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` when:
|
||||
///
|
||||
/// - The resulting date does not exist (for example `month0(3)` when day of the month is 31).
|
||||
/// - In case of [`DateTime<Tz>`] if the resulting date and time fall within a timezone
|
||||
/// transition such as from DST to standard time.
|
||||
/// - The value for `month0` is out of range.
|
||||
///
|
||||
/// [`DateTime<Tz>`]: crate::DateTime
|
||||
fn with_month0(&self, month0: u32) -> Option<Self>;
|
||||
|
||||
/// Makes a new value with the day of month (starting from 1) changed.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` when:
|
||||
///
|
||||
/// - The resulting date does not exist (for example `day(31)` in April).
|
||||
/// - In case of [`DateTime<Tz>`] if the resulting date and time fall within a timezone
|
||||
/// transition such as from DST to standard time.
|
||||
/// - The value for `day` is out of range.
|
||||
///
|
||||
/// [`DateTime<Tz>`]: crate::DateTime
|
||||
fn with_day(&self, day: u32) -> Option<Self>;
|
||||
|
||||
/// Makes a new value with the day of month (starting from 0) changed.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` when:
|
||||
///
|
||||
/// - The resulting date does not exist (for example `day0(30)` in April).
|
||||
/// - In case of [`DateTime<Tz>`] if the resulting date and time fall within a timezone
|
||||
/// transition such as from DST to standard time.
|
||||
/// - The value for `day0` is out of range.
|
||||
///
|
||||
/// [`DateTime<Tz>`]: crate::DateTime
|
||||
fn with_day0(&self, day0: u32) -> Option<Self>;
|
||||
|
||||
/// Makes a new value with the day of year (starting from 1) changed.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` when:
|
||||
///
|
||||
/// - The resulting date does not exist (`with_ordinal(366)` in a non-leap year).
|
||||
/// - In case of [`DateTime<Tz>`] if the resulting date and time fall within a timezone
|
||||
/// transition such as from DST to standard time.
|
||||
/// - The value for `ordinal` is out of range.
|
||||
///
|
||||
/// [`DateTime<Tz>`]: crate::DateTime
|
||||
fn with_ordinal(&self, ordinal: u32) -> Option<Self>;
|
||||
|
||||
/// Makes a new value with the day of year (starting from 0) changed.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` when:
|
||||
///
|
||||
/// - The resulting date does not exist (`with_ordinal0(365)` in a non-leap year).
|
||||
/// - In case of [`DateTime<Tz>`] if the resulting date and time fall within a timezone
|
||||
/// transition such as from DST to standard time.
|
||||
/// - The value for `ordinal0` is out of range.
|
||||
///
|
||||
/// [`DateTime<Tz>`]: crate::DateTime
|
||||
fn with_ordinal0(&self, ordinal0: u32) -> Option<Self>;
|
||||
|
||||
/// Counts the days in the proleptic Gregorian calendar, with January 1, Year 1 (CE) as day 1.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use chrono::{Datelike, NaiveDate};
|
||||
///
|
||||
/// assert_eq!(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().num_days_from_ce(), 719_163);
|
||||
/// assert_eq!(NaiveDate::from_ymd_opt(2, 1, 1).unwrap().num_days_from_ce(), 366);
|
||||
/// assert_eq!(NaiveDate::from_ymd_opt(1, 1, 1).unwrap().num_days_from_ce(), 1);
|
||||
/// assert_eq!(NaiveDate::from_ymd_opt(0, 1, 1).unwrap().num_days_from_ce(), -365);
|
||||
/// ```
|
||||
fn num_days_from_ce(&self) -> i32 {
|
||||
// See test_num_days_from_ce_against_alternative_impl below for a more straightforward
|
||||
// implementation.
|
||||
|
||||
// we know this wouldn't overflow since year is limited to 1/2^13 of i32's full range.
|
||||
let mut year = self.year() - 1;
|
||||
let mut ndays = 0;
|
||||
if year < 0 {
|
||||
let excess = 1 + (-year) / 400;
|
||||
year += excess * 400;
|
||||
ndays -= excess * 146_097;
|
||||
}
|
||||
let div_100 = year / 100;
|
||||
ndays += ((year * 1461) >> 2) - div_100 + (div_100 >> 2);
|
||||
ndays + self.ordinal() as i32
|
||||
}
|
||||
}
|
||||
|
||||
/// The common set of methods for time component.
|
||||
pub trait Timelike: Sized {
|
||||
/// Returns the hour number from 0 to 23.
|
||||
fn hour(&self) -> u32;
|
||||
|
||||
/// Returns the hour number from 1 to 12 with a boolean flag,
|
||||
/// which is false for AM and true for PM.
|
||||
#[inline]
|
||||
fn hour12(&self) -> (bool, u32) {
|
||||
let hour = self.hour();
|
||||
let mut hour12 = hour % 12;
|
||||
if hour12 == 0 {
|
||||
hour12 = 12;
|
||||
}
|
||||
(hour >= 12, hour12)
|
||||
}
|
||||
|
||||
/// Returns the minute number from 0 to 59.
|
||||
fn minute(&self) -> u32;
|
||||
|
||||
/// Returns the second number from 0 to 59.
|
||||
fn second(&self) -> u32;
|
||||
|
||||
/// Returns the number of nanoseconds since the whole non-leap second.
|
||||
/// The range from 1,000,000,000 to 1,999,999,999 represents
|
||||
/// the [leap second](./naive/struct.NaiveTime.html#leap-second-handling).
|
||||
fn nanosecond(&self) -> u32;
|
||||
|
||||
/// Makes a new value with the hour number changed.
|
||||
///
|
||||
/// Returns `None` when the resulting value would be invalid.
|
||||
fn with_hour(&self, hour: u32) -> Option<Self>;
|
||||
|
||||
/// Makes a new value with the minute number changed.
|
||||
///
|
||||
/// Returns `None` when the resulting value would be invalid.
|
||||
fn with_minute(&self, min: u32) -> Option<Self>;
|
||||
|
||||
/// Makes a new value with the second number changed.
|
||||
///
|
||||
/// Returns `None` when the resulting value would be invalid.
|
||||
/// As with the [`second`](#tymethod.second) method,
|
||||
/// the input range is restricted to 0 through 59.
|
||||
fn with_second(&self, sec: u32) -> Option<Self>;
|
||||
|
||||
/// Makes a new value with nanoseconds since the whole non-leap second changed.
|
||||
///
|
||||
/// Returns `None` when the resulting value would be invalid.
|
||||
/// As with the [`nanosecond`](#tymethod.nanosecond) method,
|
||||
/// the input range can exceed 1,000,000,000 for leap seconds.
|
||||
fn with_nanosecond(&self, nano: u32) -> Option<Self>;
|
||||
|
||||
/// Returns the number of non-leap seconds past the last midnight.
|
||||
///
|
||||
/// Every value in 00:00:00-23:59:59 maps to an integer in 0-86399.
|
||||
///
|
||||
/// This method is not intended to provide the real number of seconds since midnight on a given
|
||||
/// day. It does not take things like DST transitions into account.
|
||||
#[inline]
|
||||
fn num_seconds_from_midnight(&self) -> u32 {
|
||||
self.hour() * 3600 + self.minute() * 60 + self.second()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Datelike;
|
||||
use crate::{Days, NaiveDate};
|
||||
|
||||
/// Tests `Datelike::num_days_from_ce` against an alternative implementation.
|
||||
///
|
||||
/// The alternative implementation is not as short as the current one but it is simpler to
|
||||
/// understand, with less unexplained magic constants.
|
||||
#[test]
|
||||
fn test_num_days_from_ce_against_alternative_impl() {
|
||||
/// Returns the number of multiples of `div` in the range `start..end`.
|
||||
///
|
||||
/// If the range `start..end` is back-to-front, i.e. `start` is greater than `end`, the
|
||||
/// behaviour is defined by the following equation:
|
||||
/// `in_between(start, end, div) == - in_between(end, start, div)`.
|
||||
///
|
||||
/// When `div` is 1, this is equivalent to `end - start`, i.e. the length of `start..end`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `div` is not positive.
|
||||
fn in_between(start: i32, end: i32, div: i32) -> i32 {
|
||||
assert!(div > 0, "in_between: nonpositive div = {}", div);
|
||||
let start = (start.div_euclid(div), start.rem_euclid(div));
|
||||
let end = (end.div_euclid(div), end.rem_euclid(div));
|
||||
// The lowest multiple of `div` greater than or equal to `start`, divided.
|
||||
let start = start.0 + (start.1 != 0) as i32;
|
||||
// The lowest multiple of `div` greater than or equal to `end`, divided.
|
||||
let end = end.0 + (end.1 != 0) as i32;
|
||||
end - start
|
||||
}
|
||||
|
||||
/// Alternative implementation to `Datelike::num_days_from_ce`
|
||||
fn num_days_from_ce<Date: Datelike>(date: &Date) -> i32 {
|
||||
let year = date.year();
|
||||
let diff = move |div| in_between(1, year, div);
|
||||
// 365 days a year, one more in leap years. In the gregorian calendar, leap years are all
|
||||
// the multiples of 4 except multiples of 100 but including multiples of 400.
|
||||
date.ordinal() as i32 + 365 * diff(1) + diff(4) - diff(100) + diff(400)
|
||||
}
|
||||
|
||||
for year in NaiveDate::MIN.year()..=NaiveDate::MAX.year() {
|
||||
let jan1_year = NaiveDate::from_ymd_opt(year, 1, 1).unwrap();
|
||||
assert_eq!(
|
||||
jan1_year.num_days_from_ce(),
|
||||
num_days_from_ce(&jan1_year),
|
||||
"on {:?}",
|
||||
jan1_year
|
||||
);
|
||||
let mid_year = jan1_year + Days::new(133);
|
||||
assert_eq!(
|
||||
mid_year.num_days_from_ce(),
|
||||
num_days_from_ce(&mid_year),
|
||||
"on {:?}",
|
||||
mid_year
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
408
third_party/rust/chrono/src/weekday.rs
vendored
Normal file
408
third_party/rust/chrono/src/weekday.rs
vendored
Normal file
@@ -0,0 +1,408 @@
|
||||
use core::fmt;
|
||||
|
||||
#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))]
|
||||
use rkyv::{Archive, Deserialize, Serialize};
|
||||
|
||||
use crate::OutOfRange;
|
||||
|
||||
/// The day of week.
|
||||
///
|
||||
/// The order of the days of week depends on the context.
|
||||
/// (This is why this type does *not* implement `PartialOrd` or `Ord` traits.)
|
||||
/// One should prefer `*_from_monday` or `*_from_sunday` methods to get the correct result.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use chrono::Weekday;
|
||||
///
|
||||
/// let monday = "Monday".parse::<Weekday>().unwrap();
|
||||
/// assert_eq!(monday, Weekday::Mon);
|
||||
///
|
||||
/// let sunday = Weekday::try_from(6).unwrap();
|
||||
/// assert_eq!(sunday, Weekday::Sun);
|
||||
///
|
||||
/// assert_eq!(sunday.num_days_from_monday(), 6); // starts counting with Monday = 0
|
||||
/// assert_eq!(sunday.number_from_monday(), 7); // starts counting with Monday = 1
|
||||
/// assert_eq!(sunday.num_days_from_sunday(), 0); // starts counting with Sunday = 0
|
||||
/// assert_eq!(sunday.number_from_sunday(), 1); // starts counting with Sunday = 1
|
||||
///
|
||||
/// assert_eq!(sunday.succ(), monday);
|
||||
/// assert_eq!(sunday.pred(), Weekday::Sat);
|
||||
/// ```
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
|
||||
#[cfg_attr(
|
||||
any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"),
|
||||
derive(Archive, Deserialize, Serialize),
|
||||
archive(compare(PartialEq)),
|
||||
archive_attr(derive(Clone, Copy, PartialEq, Eq, Debug, Hash))
|
||||
)]
|
||||
#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
|
||||
#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))]
|
||||
pub enum Weekday {
|
||||
/// Monday.
|
||||
Mon = 0,
|
||||
/// Tuesday.
|
||||
Tue = 1,
|
||||
/// Wednesday.
|
||||
Wed = 2,
|
||||
/// Thursday.
|
||||
Thu = 3,
|
||||
/// Friday.
|
||||
Fri = 4,
|
||||
/// Saturday.
|
||||
Sat = 5,
|
||||
/// Sunday.
|
||||
Sun = 6,
|
||||
}
|
||||
|
||||
impl Weekday {
|
||||
/// The next day in the week.
|
||||
///
|
||||
/// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun`
|
||||
/// ----------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
|
||||
/// `w.succ()`: | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` | `Mon`
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn succ(&self) -> Weekday {
|
||||
match *self {
|
||||
Weekday::Mon => Weekday::Tue,
|
||||
Weekday::Tue => Weekday::Wed,
|
||||
Weekday::Wed => Weekday::Thu,
|
||||
Weekday::Thu => Weekday::Fri,
|
||||
Weekday::Fri => Weekday::Sat,
|
||||
Weekday::Sat => Weekday::Sun,
|
||||
Weekday::Sun => Weekday::Mon,
|
||||
}
|
||||
}
|
||||
|
||||
/// The previous day in the week.
|
||||
///
|
||||
/// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun`
|
||||
/// ----------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
|
||||
/// `w.pred()`: | `Sun` | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat`
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn pred(&self) -> Weekday {
|
||||
match *self {
|
||||
Weekday::Mon => Weekday::Sun,
|
||||
Weekday::Tue => Weekday::Mon,
|
||||
Weekday::Wed => Weekday::Tue,
|
||||
Weekday::Thu => Weekday::Wed,
|
||||
Weekday::Fri => Weekday::Thu,
|
||||
Weekday::Sat => Weekday::Fri,
|
||||
Weekday::Sun => Weekday::Sat,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a day-of-week number starting from Monday = 1. (ISO 8601 weekday number)
|
||||
///
|
||||
/// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun`
|
||||
/// ------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
|
||||
/// `w.number_from_monday()`: | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
||||
#[inline]
|
||||
pub const fn number_from_monday(&self) -> u32 {
|
||||
self.days_since(Weekday::Mon) + 1
|
||||
}
|
||||
|
||||
/// Returns a day-of-week number starting from Sunday = 1.
|
||||
///
|
||||
/// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun`
|
||||
/// ------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
|
||||
/// `w.number_from_sunday()`: | 2 | 3 | 4 | 5 | 6 | 7 | 1
|
||||
#[inline]
|
||||
pub const fn number_from_sunday(&self) -> u32 {
|
||||
self.days_since(Weekday::Sun) + 1
|
||||
}
|
||||
|
||||
/// Returns a day-of-week number starting from Monday = 0.
|
||||
///
|
||||
/// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun`
|
||||
/// --------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
|
||||
/// `w.num_days_from_monday()`: | 0 | 1 | 2 | 3 | 4 | 5 | 6
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "clock")] {
|
||||
/// # use chrono::{Local, Datelike};
|
||||
/// // MTWRFSU is occasionally used as a single-letter abbreviation of the weekdays.
|
||||
/// // Use `num_days_from_monday` to index into the array.
|
||||
/// const MTWRFSU: [char; 7] = ['M', 'T', 'W', 'R', 'F', 'S', 'U'];
|
||||
///
|
||||
/// let today = Local::now().weekday();
|
||||
/// println!("{}", MTWRFSU[today.num_days_from_monday() as usize]);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn num_days_from_monday(&self) -> u32 {
|
||||
self.days_since(Weekday::Mon)
|
||||
}
|
||||
|
||||
/// Returns a day-of-week number starting from Sunday = 0.
|
||||
///
|
||||
/// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun`
|
||||
/// --------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
|
||||
/// `w.num_days_from_sunday()`: | 1 | 2 | 3 | 4 | 5 | 6 | 0
|
||||
#[inline]
|
||||
pub const fn num_days_from_sunday(&self) -> u32 {
|
||||
self.days_since(Weekday::Sun)
|
||||
}
|
||||
|
||||
/// The number of days since the given day.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use chrono::Weekday::*;
|
||||
/// assert_eq!(Mon.days_since(Mon), 0);
|
||||
/// assert_eq!(Sun.days_since(Tue), 5);
|
||||
/// assert_eq!(Wed.days_since(Sun), 3);
|
||||
/// ```
|
||||
pub const fn days_since(&self, other: Weekday) -> u32 {
|
||||
let lhs = *self as u32;
|
||||
let rhs = other as u32;
|
||||
if lhs < rhs { 7 + lhs - rhs } else { lhs - rhs }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Weekday {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.pad(match *self {
|
||||
Weekday::Mon => "Mon",
|
||||
Weekday::Tue => "Tue",
|
||||
Weekday::Wed => "Wed",
|
||||
Weekday::Thu => "Thu",
|
||||
Weekday::Fri => "Fri",
|
||||
Weekday::Sat => "Sat",
|
||||
Weekday::Sun => "Sun",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Any weekday can be represented as an integer from 0 to 6, which equals to
|
||||
/// [`Weekday::num_days_from_monday`](#method.num_days_from_monday) in this implementation.
|
||||
/// Do not heavily depend on this though; use explicit methods whenever possible.
|
||||
impl TryFrom<u8> for Weekday {
|
||||
type Error = OutOfRange;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(Weekday::Mon),
|
||||
1 => Ok(Weekday::Tue),
|
||||
2 => Ok(Weekday::Wed),
|
||||
3 => Ok(Weekday::Thu),
|
||||
4 => Ok(Weekday::Fri),
|
||||
5 => Ok(Weekday::Sat),
|
||||
6 => Ok(Weekday::Sun),
|
||||
_ => Err(OutOfRange::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Any weekday can be represented as an integer from 0 to 6, which equals to
|
||||
/// [`Weekday::num_days_from_monday`](#method.num_days_from_monday) in this implementation.
|
||||
/// Do not heavily depend on this though; use explicit methods whenever possible.
|
||||
impl num_traits::FromPrimitive for Weekday {
|
||||
#[inline]
|
||||
fn from_i64(n: i64) -> Option<Weekday> {
|
||||
match n {
|
||||
0 => Some(Weekday::Mon),
|
||||
1 => Some(Weekday::Tue),
|
||||
2 => Some(Weekday::Wed),
|
||||
3 => Some(Weekday::Thu),
|
||||
4 => Some(Weekday::Fri),
|
||||
5 => Some(Weekday::Sat),
|
||||
6 => Some(Weekday::Sun),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_u64(n: u64) -> Option<Weekday> {
|
||||
match n {
|
||||
0 => Some(Weekday::Mon),
|
||||
1 => Some(Weekday::Tue),
|
||||
2 => Some(Weekday::Wed),
|
||||
3 => Some(Weekday::Thu),
|
||||
4 => Some(Weekday::Fri),
|
||||
5 => Some(Weekday::Sat),
|
||||
6 => Some(Weekday::Sun),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An error resulting from reading `Weekday` value with `FromStr`.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct ParseWeekdayError {
|
||||
pub(crate) _dummy: (),
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::error::Error for ParseWeekdayError {}
|
||||
|
||||
impl fmt::Display for ParseWeekdayError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_fmt(format_args!("{:?}", self))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ParseWeekdayError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "ParseWeekdayError {{ .. }}")
|
||||
}
|
||||
}
|
||||
|
||||
// the actual `FromStr` implementation is in the `format` module to leverage the existing code
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
mod weekday_serde {
|
||||
use super::Weekday;
|
||||
use core::fmt;
|
||||
use serde::{de, ser};
|
||||
|
||||
impl ser::Serialize for Weekday {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ser::Serializer,
|
||||
{
|
||||
serializer.collect_str(&self)
|
||||
}
|
||||
}
|
||||
|
||||
struct WeekdayVisitor;
|
||||
|
||||
impl de::Visitor<'_> for WeekdayVisitor {
|
||||
type Value = Weekday;
|
||||
|
||||
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("Weekday")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
value.parse().map_err(|_| E::custom("short or long weekday names expected"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> de::Deserialize<'de> for Weekday {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_str(WeekdayVisitor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Weekday;
|
||||
|
||||
#[test]
|
||||
fn test_days_since() {
|
||||
for i in 0..7 {
|
||||
let base_day = Weekday::try_from(i).unwrap();
|
||||
|
||||
assert_eq!(base_day.num_days_from_monday(), base_day.days_since(Weekday::Mon));
|
||||
assert_eq!(base_day.num_days_from_sunday(), base_day.days_since(Weekday::Sun));
|
||||
|
||||
assert_eq!(base_day.days_since(base_day), 0);
|
||||
|
||||
assert_eq!(base_day.days_since(base_day.pred()), 1);
|
||||
assert_eq!(base_day.days_since(base_day.pred().pred()), 2);
|
||||
assert_eq!(base_day.days_since(base_day.pred().pred().pred()), 3);
|
||||
assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred()), 4);
|
||||
assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred().pred()), 5);
|
||||
assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred().pred().pred()), 6);
|
||||
|
||||
assert_eq!(base_day.days_since(base_day.succ()), 6);
|
||||
assert_eq!(base_day.days_since(base_day.succ().succ()), 5);
|
||||
assert_eq!(base_day.days_since(base_day.succ().succ().succ()), 4);
|
||||
assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ()), 3);
|
||||
assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ().succ()), 2);
|
||||
assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ().succ().succ()), 1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_formatting_alignment() {
|
||||
// No exhaustive testing here as we just delegate the
|
||||
// implementation to Formatter::pad. Just some basic smoke
|
||||
// testing to ensure that it's in fact being done.
|
||||
assert_eq!(format!("{:x>7}", Weekday::Mon), "xxxxMon");
|
||||
assert_eq!(format!("{:^7}", Weekday::Mon), " Mon ");
|
||||
assert_eq!(format!("{:Z<7}", Weekday::Mon), "MonZZZZ");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "serde")]
|
||||
fn test_serde_serialize() {
|
||||
use Weekday::*;
|
||||
use serde_json::to_string;
|
||||
|
||||
let cases: Vec<(Weekday, &str)> = vec![
|
||||
(Mon, "\"Mon\""),
|
||||
(Tue, "\"Tue\""),
|
||||
(Wed, "\"Wed\""),
|
||||
(Thu, "\"Thu\""),
|
||||
(Fri, "\"Fri\""),
|
||||
(Sat, "\"Sat\""),
|
||||
(Sun, "\"Sun\""),
|
||||
];
|
||||
|
||||
for (weekday, expected_str) in cases {
|
||||
let string = to_string(&weekday).unwrap();
|
||||
assert_eq!(string, expected_str);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "serde")]
|
||||
fn test_serde_deserialize() {
|
||||
use Weekday::*;
|
||||
use serde_json::from_str;
|
||||
|
||||
let cases: Vec<(&str, Weekday)> = vec![
|
||||
("\"mon\"", Mon),
|
||||
("\"MONDAY\"", Mon),
|
||||
("\"MonDay\"", Mon),
|
||||
("\"mOn\"", Mon),
|
||||
("\"tue\"", Tue),
|
||||
("\"tuesday\"", Tue),
|
||||
("\"wed\"", Wed),
|
||||
("\"wednesday\"", Wed),
|
||||
("\"thu\"", Thu),
|
||||
("\"thursday\"", Thu),
|
||||
("\"fri\"", Fri),
|
||||
("\"friday\"", Fri),
|
||||
("\"sat\"", Sat),
|
||||
("\"saturday\"", Sat),
|
||||
("\"sun\"", Sun),
|
||||
("\"sunday\"", Sun),
|
||||
];
|
||||
|
||||
for (str, expected_weekday) in cases {
|
||||
let weekday = from_str::<Weekday>(str).unwrap();
|
||||
assert_eq!(weekday, expected_weekday);
|
||||
}
|
||||
|
||||
let errors: Vec<&str> =
|
||||
vec!["\"not a weekday\"", "\"monDAYs\"", "\"mond\"", "mon", "\"thur\"", "\"thurs\""];
|
||||
|
||||
for str in errors {
|
||||
from_str::<Weekday>(str).unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "rkyv-validation")]
|
||||
fn test_rkyv_validation() {
|
||||
let mon = Weekday::Mon;
|
||||
let bytes = rkyv::to_bytes::<_, 1>(&mon).unwrap();
|
||||
|
||||
assert_eq!(rkyv::from_bytes::<Weekday>(&bytes).unwrap(), mon);
|
||||
}
|
||||
}
|
||||
165
third_party/rust/chrono/tests/dateutils.rs
vendored
Normal file
165
third_party/rust/chrono/tests/dateutils.rs
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
#![cfg(all(unix, feature = "clock", feature = "std"))]
|
||||
|
||||
use std::{path, process, thread};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use chrono::Days;
|
||||
use chrono::{Datelike, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Timelike};
|
||||
|
||||
fn verify_against_date_command_local(path: &'static str, dt: NaiveDateTime) {
|
||||
let output = process::Command::new(path)
|
||||
.arg("-d")
|
||||
.arg(format!("{}-{:02}-{:02} {:02}:05:01", dt.year(), dt.month(), dt.day(), dt.hour()))
|
||||
.arg("+%Y-%m-%d %H:%M:%S %:z")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
let date_command_str = String::from_utf8(output.stdout).unwrap();
|
||||
|
||||
// The below would be preferred. At this stage neither earliest() or latest()
|
||||
// seems to be consistent with the output of the `date` command, so we simply
|
||||
// compare both.
|
||||
// let local = Local
|
||||
// .with_ymd_and_hms(year, month, day, hour, 5, 1)
|
||||
// // looks like the "date" command always returns a given time when it is ambiguous
|
||||
// .earliest();
|
||||
|
||||
// if let Some(local) = local {
|
||||
// assert_eq!(format!("{}\n", local), date_command_str);
|
||||
// } else {
|
||||
// // we are in a "Spring forward gap" due to DST, and so date also returns ""
|
||||
// assert_eq!("", date_command_str);
|
||||
// }
|
||||
|
||||
// This is used while a decision is made whether the `date` output needs to
|
||||
// be exactly matched, or whether MappedLocalTime::Ambiguous should be handled
|
||||
// differently
|
||||
|
||||
let date = NaiveDate::from_ymd_opt(dt.year(), dt.month(), dt.day()).unwrap();
|
||||
match Local.from_local_datetime(&date.and_hms_opt(dt.hour(), 5, 1).unwrap()) {
|
||||
chrono::MappedLocalTime::Ambiguous(a, b) => assert!(
|
||||
format!("{}\n", a) == date_command_str || format!("{}\n", b) == date_command_str
|
||||
),
|
||||
chrono::MappedLocalTime::Single(a) => {
|
||||
assert_eq!(format!("{}\n", a), date_command_str);
|
||||
}
|
||||
chrono::MappedLocalTime::None => {
|
||||
assert_eq!("", date_command_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// path to Unix `date` command. Should work on most Linux and Unixes. Not the
|
||||
/// path for MacOS (/bin/date) which uses a different version of `date` with
|
||||
/// different arguments (so it won't run which is okay).
|
||||
/// for testing only
|
||||
#[allow(dead_code)]
|
||||
#[cfg(not(target_os = "aix"))]
|
||||
const DATE_PATH: &str = "/usr/bin/date";
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "aix")]
|
||||
const DATE_PATH: &str = "/opt/freeware/bin/date";
|
||||
|
||||
#[cfg(test)]
|
||||
/// test helper to sanity check the date command behaves as expected
|
||||
/// asserts the command succeeded
|
||||
fn assert_run_date_version() {
|
||||
// note environment variable `LANG`
|
||||
match std::env::var_os("LANG") {
|
||||
Some(lang) => eprintln!("LANG: {:?}", lang),
|
||||
None => eprintln!("LANG not set"),
|
||||
}
|
||||
let out = process::Command::new(DATE_PATH).arg("--version").output().unwrap();
|
||||
let stdout = String::from_utf8(out.stdout).unwrap();
|
||||
let stderr = String::from_utf8(out.stderr).unwrap();
|
||||
// note the `date` binary version
|
||||
eprintln!("command: {:?} --version\nstdout: {:?}\nstderr: {:?}", DATE_PATH, stdout, stderr);
|
||||
assert!(out.status.success(), "command failed: {:?} --version", DATE_PATH);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_verify_against_date_command() {
|
||||
if !path::Path::new(DATE_PATH).exists() {
|
||||
eprintln!("date command {:?} not found, skipping", DATE_PATH);
|
||||
return;
|
||||
}
|
||||
assert_run_date_version();
|
||||
|
||||
eprintln!(
|
||||
"Run command {:?} for every hour from 1975 to 2077, skipping some years...",
|
||||
DATE_PATH,
|
||||
);
|
||||
|
||||
let mut children = vec![];
|
||||
for year in [1975, 1976, 1977, 2020, 2021, 2022, 2073, 2074, 2075, 2076, 2077].iter() {
|
||||
children.push(thread::spawn(|| {
|
||||
let mut date = NaiveDate::from_ymd_opt(*year, 1, 1).unwrap().and_time(NaiveTime::MIN);
|
||||
let end = NaiveDate::from_ymd_opt(*year + 1, 1, 1).unwrap().and_time(NaiveTime::MIN);
|
||||
while date <= end {
|
||||
verify_against_date_command_local(DATE_PATH, date);
|
||||
date += chrono::TimeDelta::try_hours(1).unwrap();
|
||||
}
|
||||
}));
|
||||
}
|
||||
for child in children {
|
||||
// Wait for the thread to finish. Returns a result.
|
||||
let _ = child.join();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn verify_against_date_command_format_local(path: &'static str, dt: NaiveDateTime) {
|
||||
let required_format =
|
||||
"d%d D%D F%F H%H I%I j%j k%k l%l m%m M%M q%q S%S T%T u%u U%U w%w W%W X%X y%y Y%Y z%:z";
|
||||
// a%a - depends from localization
|
||||
// A%A - depends from localization
|
||||
// b%b - depends from localization
|
||||
// B%B - depends from localization
|
||||
// h%h - depends from localization
|
||||
// c%c - depends from localization
|
||||
// p%p - depends from localization
|
||||
// r%r - depends from localization
|
||||
// x%x - fails, date is dd/mm/yyyy, chrono is dd/mm/yy, same as %D
|
||||
// Z%Z - too many ways to represent it, will most likely fail
|
||||
|
||||
let output = process::Command::new(path)
|
||||
.env("LANG", "c")
|
||||
.env("LC_ALL", "c")
|
||||
.arg("-d")
|
||||
.arg(format!(
|
||||
"{}-{:02}-{:02} {:02}:{:02}:{:02}",
|
||||
dt.year(),
|
||||
dt.month(),
|
||||
dt.day(),
|
||||
dt.hour(),
|
||||
dt.minute(),
|
||||
dt.second()
|
||||
))
|
||||
.arg(format!("+{}", required_format))
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
let date_command_str = String::from_utf8(output.stdout).unwrap();
|
||||
let date = NaiveDate::from_ymd_opt(dt.year(), dt.month(), dt.day()).unwrap();
|
||||
let ldt = Local
|
||||
.from_local_datetime(&date.and_hms_opt(dt.hour(), dt.minute(), dt.second()).unwrap())
|
||||
.unwrap();
|
||||
let formatted_date = format!("{}\n", ldt.format(required_format));
|
||||
assert_eq!(date_command_str, formatted_date);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "linux")]
|
||||
fn try_verify_against_date_command_format() {
|
||||
if !path::Path::new(DATE_PATH).exists() {
|
||||
eprintln!("date command {:?} not found, skipping", DATE_PATH);
|
||||
return;
|
||||
}
|
||||
assert_run_date_version();
|
||||
|
||||
let mut date = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_opt(12, 11, 13).unwrap();
|
||||
while date.year() < 2008 {
|
||||
verify_against_date_command_format_local(DATE_PATH, date);
|
||||
date = date + Days::new(55);
|
||||
}
|
||||
}
|
||||
142
third_party/rust/chrono/tests/wasm.rs
vendored
142
third_party/rust/chrono/tests/wasm.rs
vendored
@@ -1,67 +1,89 @@
|
||||
#[cfg(all(test, feature = "wasmbind"))]
|
||||
mod test {
|
||||
extern crate chrono;
|
||||
extern crate wasm_bindgen_test;
|
||||
//! Run this test with:
|
||||
//! `env TZ="$(date +%z)" NOW="$(date +%s)" wasm-pack test --node -- --features wasmbind`
|
||||
//!
|
||||
//! The `TZ` and `NOW` variables are used to compare the results inside the WASM environment with
|
||||
//! the host system.
|
||||
//! The check will fail if the local timezone does not match one of the timezones defined below.
|
||||
|
||||
use self::chrono::prelude::*;
|
||||
use self::wasm_bindgen_test::*;
|
||||
#![cfg(all(
|
||||
target_arch = "wasm32",
|
||||
feature = "wasmbind",
|
||||
feature = "clock",
|
||||
not(any(target_os = "emscripten", target_os = "wasi"))
|
||||
))]
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn now() {
|
||||
let utc: DateTime<Utc> = Utc::now();
|
||||
let local: DateTime<Local> = Local::now();
|
||||
use chrono::prelude::*;
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
// Ensure time set by the test script is correct
|
||||
let now = env!("NOW");
|
||||
let actual = Utc.datetime_from_str(&now, "%s").unwrap();
|
||||
let diff = utc - actual;
|
||||
assert!(
|
||||
diff < chrono::Duration::minutes(5),
|
||||
"expected {} - {} == {} < 5m (env var: {})",
|
||||
utc,
|
||||
actual,
|
||||
diff,
|
||||
now,
|
||||
);
|
||||
#[wasm_bindgen_test]
|
||||
fn now() {
|
||||
let utc: DateTime<Utc> = Utc::now();
|
||||
let local: DateTime<Local> = Local::now();
|
||||
|
||||
let tz = env!("TZ");
|
||||
eprintln!("testing with tz={}", tz);
|
||||
// Ensure time set by the test script is correct
|
||||
let now = env!("NOW");
|
||||
let actual = NaiveDateTime::parse_from_str(&now, "%s").unwrap().and_utc();
|
||||
let diff = utc - actual;
|
||||
assert!(
|
||||
diff < chrono::TimeDelta::try_minutes(5).unwrap(),
|
||||
"expected {} - {} == {} < 5m (env var: {})",
|
||||
utc,
|
||||
actual,
|
||||
diff,
|
||||
now,
|
||||
);
|
||||
|
||||
// Ensure offset retrieved when getting local time is correct
|
||||
let expected_offset = match tz {
|
||||
"ACST-9:30" => FixedOffset::east(19 * 30 * 60),
|
||||
"Asia/Katmandu" => FixedOffset::east(23 * 15 * 60), // No DST thankfully
|
||||
"EDT" | "EST4" | "-0400" => FixedOffset::east(-4 * 60 * 60),
|
||||
"EST" | "-0500" => FixedOffset::east(-5 * 60 * 60),
|
||||
"UTC0" | "+0000" => FixedOffset::east(0),
|
||||
tz => panic!("unexpected TZ {}", tz),
|
||||
};
|
||||
assert_eq!(
|
||||
&expected_offset,
|
||||
local.offset(),
|
||||
"expected: {:?} local: {:?}",
|
||||
expected_offset,
|
||||
local.offset(),
|
||||
);
|
||||
}
|
||||
let tz = env!("TZ");
|
||||
eprintln!("testing with tz={}", tz);
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn from_is_exact() {
|
||||
let now = js_sys::Date::new_0();
|
||||
|
||||
let dt = DateTime::<Utc>::from(now.clone());
|
||||
|
||||
assert_eq!(now.get_time() as i64, dt.timestamp_millis());
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn local_from_local_datetime() {
|
||||
let now = Local::now();
|
||||
let ndt = now.naive_local();
|
||||
let res = match Local.from_local_datetime(&ndt).single() {
|
||||
Some(v) => v,
|
||||
None => panic! {"Required for test!"},
|
||||
};
|
||||
assert_eq!(now, res);
|
||||
}
|
||||
// Ensure offset retrieved when getting local time is correct
|
||||
let expected_offset = match tz {
|
||||
"ACST-9:30" => FixedOffset::east_opt(19 * 30 * 60).unwrap(),
|
||||
"Asia/Katmandu" => FixedOffset::east_opt(23 * 15 * 60).unwrap(), // No DST thankfully
|
||||
"EDT" | "EST4" | "-0400" => FixedOffset::east_opt(-4 * 60 * 60).unwrap(),
|
||||
"EST" | "-0500" => FixedOffset::east_opt(-5 * 60 * 60).unwrap(),
|
||||
"UTC0" | "+0000" => FixedOffset::east_opt(0).unwrap(),
|
||||
tz => panic!("unexpected TZ {}", tz),
|
||||
};
|
||||
assert_eq!(
|
||||
&expected_offset,
|
||||
local.offset(),
|
||||
"expected: {:?} local: {:?}",
|
||||
expected_offset,
|
||||
local.offset(),
|
||||
);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn from_is_exact() {
|
||||
let now = js_sys::Date::new_0();
|
||||
|
||||
let dt = DateTime::<Utc>::from(now.clone());
|
||||
|
||||
assert_eq!(now.get_time() as i64, dt.timestamp_millis());
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn local_from_local_datetime() {
|
||||
let now = Local::now();
|
||||
let ndt = now.naive_local();
|
||||
let res = match Local.from_local_datetime(&ndt).single() {
|
||||
Some(v) => v,
|
||||
None => panic! {"Required for test!"},
|
||||
};
|
||||
assert_eq!(now, res);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn convert_all_parts_with_milliseconds() {
|
||||
let time: DateTime<Utc> = "2020-12-01T03:01:55.974Z".parse().unwrap();
|
||||
let js_date = js_sys::Date::from(time);
|
||||
|
||||
assert_eq!(js_date.get_utc_full_year(), 2020);
|
||||
assert_eq!(js_date.get_utc_month(), 11); // months are numbered 0..=11
|
||||
assert_eq!(js_date.get_utc_date(), 1);
|
||||
assert_eq!(js_date.get_utc_hours(), 3);
|
||||
assert_eq!(js_date.get_utc_minutes(), 1);
|
||||
assert_eq!(js_date.get_utc_seconds(), 55);
|
||||
assert_eq!(js_date.get_utc_milliseconds(), 974);
|
||||
}
|
||||
|
||||
28
third_party/rust/chrono/tests/win_bindings.rs
vendored
Normal file
28
third_party/rust/chrono/tests/win_bindings.rs
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
#![cfg(all(windows, feature = "clock", feature = "std"))]
|
||||
|
||||
use std::fs;
|
||||
use windows_bindgen::bindgen;
|
||||
|
||||
#[test]
|
||||
fn gen_bindings() {
|
||||
let input = "src/offset/local/win_bindings.txt";
|
||||
let output = "src/offset/local/win_bindings.rs";
|
||||
let existing = fs::read_to_string(output).unwrap();
|
||||
|
||||
bindgen(["--no-deps", "--etc", input]);
|
||||
|
||||
// Check the output is the same as before.
|
||||
// Depending on the git configuration the file may have been checked out with `\r\n` newlines or
|
||||
// with `\n`. Compare line-by-line to ignore this difference.
|
||||
let mut new = fs::read_to_string(output).unwrap();
|
||||
if existing.contains("\r\n") && !new.contains("\r\n") {
|
||||
new = new.replace("\n", "\r\n");
|
||||
} else if !existing.contains("\r\n") && new.contains("\r\n") {
|
||||
new = new.replace("\r\n", "\n");
|
||||
}
|
||||
|
||||
similar_asserts::assert_eq!(existing, new);
|
||||
if !new.lines().eq(existing.lines()) {
|
||||
panic!("generated file `{}` is changed.", output);
|
||||
}
|
||||
}
|
||||
1
third_party/rust/iana-time-zone-haiku/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/iana-time-zone-haiku/.cargo-checksum.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"files":{"Cargo.toml":"8f06eca1c0e108d0422687eeb87030520ae7bd956efc92352599cc9cf079d9a3","LICENSE-APACHE":"696759d65dfe558ff7d9f031c76db19ec5c0767470fb67c4e8d990820d1e99c9","LICENSE-MIT":"da28ccc6b158fc2d8cccc74e99794b1cff1d29bd7bbeb019442fcf0c04c6cad9","README.md":"5b1ad9309b716374cc1bdcd025f525fac31b2f413e6c4d311e207fa6b1f96a83","build.rs":"10304831100a60c1c2b990762dcfeb47dae8342cf9b54595bec94884e7de5784","src/implementation.cc":"66d2ecfe58ec543e27a6fb3a96526a07cd1ac43a2370344f856529e5a112ce0f","src/lib.rs":"e58db019554bd372f0a187f8f51f96624cdf21bcef507de2093e1d49ca0787cd"},"package":"f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"}
|
||||
34
third_party/rust/iana-time-zone-haiku/Cargo.toml
vendored
Normal file
34
third_party/rust/iana-time-zone-haiku/Cargo.toml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
authors = ["René Kijewski <crates.io@k6i.de>"]
|
||||
description = "iana-time-zone support crate for Haiku OS"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"IANA",
|
||||
"time",
|
||||
]
|
||||
categories = [
|
||||
"date-and-time",
|
||||
"internationalization",
|
||||
"os",
|
||||
]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/strawlab/iana-time-zone"
|
||||
|
||||
[dependencies]
|
||||
|
||||
[build-dependencies.cc]
|
||||
version = "1.0.79"
|
||||
201
third_party/rust/iana-time-zone-haiku/LICENSE-APACHE
vendored
Normal file
201
third_party/rust/iana-time-zone-haiku/LICENSE-APACHE
vendored
Normal file
@@ -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 2020 Andrew Straw
|
||||
|
||||
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.
|
||||
25
third_party/rust/iana-time-zone-haiku/LICENSE-MIT
vendored
Normal file
25
third_party/rust/iana-time-zone-haiku/LICENSE-MIT
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2020 Andrew D. Straw
|
||||
|
||||
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.
|
||||
8
third_party/rust/iana-time-zone-haiku/README.md
vendored
Normal file
8
third_party/rust/iana-time-zone-haiku/README.md
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# iana-time-zone-haiku
|
||||
|
||||
[](https://crates.io/crates/iana-time-zone-haiku)
|
||||
[](https://docs.rs/iana-time-zone/)
|
||||
[](https://crates.io/crates/iana-time-zone-haiku)
|
||||
[](https://github.com/strawlab/iana-time-zone/actions?query=branch%3Amain)
|
||||
|
||||
[iana-time-zone](https://github.com/strawlab/iana-time-zone) support crate for Haiku OS.
|
||||
22
third_party/rust/iana-time-zone-haiku/build.rs
vendored
Normal file
22
third_party/rust/iana-time-zone-haiku/build.rs
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
cc::Build::new()
|
||||
.warnings(false)
|
||||
.cpp(true)
|
||||
.file("src/implementation.cc")
|
||||
.flag_if_supported("-std=c++11")
|
||||
.compile("tz_haiku");
|
||||
|
||||
println!("cargo:rerun-if-changed=src/lib.rs");
|
||||
println!("cargo:rerun-if-changed=src/implementation.cc");
|
||||
println!("cargo:rerun-if-changed=src/interface.h");
|
||||
|
||||
let target = env::var_os("TARGET").expect("cargo should set TARGET env var");
|
||||
let target = target
|
||||
.to_str()
|
||||
.expect("TARGET env var should be valid UTF-8");
|
||||
if target.contains("haiku") {
|
||||
println!("cargo:rustc-link-lib=be");
|
||||
}
|
||||
}
|
||||
67
third_party/rust/iana-time-zone-haiku/src/implementation.cc
vendored
Normal file
67
third_party/rust/iana-time-zone-haiku/src/implementation.cc
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
#include <cstddef>
|
||||
|
||||
#ifdef __HAIKU__
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <Errors.h>
|
||||
#include <LocaleRoster.h>
|
||||
#include <String.h>
|
||||
#include <TimeZone.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
size_t iana_time_zone_haiku_get_tz(char *buf, size_t buf_size) {
|
||||
try {
|
||||
static_assert(sizeof(char) == sizeof(uint8_t), "Illegal char size");
|
||||
|
||||
if (buf_size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// `BLocaleRoster::Default()` returns a reference to a statically allocated object.
|
||||
// https://github.com/haiku/haiku/blob/8f16317/src/kits/locale/LocaleRoster.cpp#L143-L147
|
||||
BLocaleRoster *locale_roster(BLocaleRoster::Default());
|
||||
if (!locale_roster) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
BTimeZone tz(NULL, NULL);
|
||||
if (locale_roster->GetDefaultTimeZone(&tz) != B_OK) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
BString bname(tz.ID());
|
||||
int32_t ilength(bname.Length());
|
||||
if (ilength <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t length(ilength);
|
||||
if (length > buf_size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// BString::String() returns a borrowed string.
|
||||
// https://www.haiku-os.org/docs/api/classBString.html#ae4fe78b06c8e3310093b80305e14ba87
|
||||
const char *sname(bname.String());
|
||||
if (!sname) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::memcpy(buf, sname, length);
|
||||
return length;
|
||||
} catch (...) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} // extern "C"
|
||||
|
||||
#else
|
||||
|
||||
extern "C" {
|
||||
|
||||
size_t iana_time_zone_haiku_get_tz(char *buf, size_t buf_size) { return 0; }
|
||||
} // extern "C"
|
||||
|
||||
#endif
|
||||
71
third_party/rust/iana-time-zone-haiku/src/lib.rs
vendored
Normal file
71
third_party/rust/iana-time-zone-haiku/src/lib.rs
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
#![warn(clippy::all)]
|
||||
#![warn(clippy::cargo)]
|
||||
#![warn(clippy::undocumented_unsafe_blocks)]
|
||||
#![allow(unknown_lints)]
|
||||
#![warn(missing_copy_implementations)]
|
||||
#![warn(missing_debug_implementations)]
|
||||
#![warn(missing_docs)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
#![warn(trivial_casts, trivial_numeric_casts)]
|
||||
#![warn(unsafe_op_in_unsafe_fn)]
|
||||
#![warn(unused_qualifications)]
|
||||
#![warn(variant_size_differences)]
|
||||
|
||||
//! # iana-time-zone-haiku
|
||||
//!
|
||||
//! [](https://crates.io/crates/iana-time-zone-haiku)
|
||||
//! [](https://docs.rs/iana-time-zone/)
|
||||
//! [](https://crates.io/crates/iana-time-zone-haiku)
|
||||
//! [](https://github.com/strawlab/iana-time-zone/actions?query=branch%3Amain)
|
||||
//!
|
||||
//! [iana-time-zone](https://github.com/strawlab/iana-time-zone) support crate for Haiku OS.
|
||||
|
||||
use std::os::raw::c_char;
|
||||
|
||||
extern "C" {
|
||||
fn iana_time_zone_haiku_get_tz(buf: *mut c_char, buf_size: usize) -> usize;
|
||||
}
|
||||
|
||||
/// Get the current IANA time zone as a string.
|
||||
///
|
||||
/// On Haiku platforms this function will return [`Some`] with the timezone string
|
||||
/// or [`None`] if an error occurs. On all other platforms, [`None`] is returned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// let timezone = iana_time_zone_haiku::get_timezone();
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn get_timezone() -> Option<String> {
|
||||
// The longest name in the IANA time zone database is 25 ASCII characters long.
|
||||
let mut buf = [0u8; 32];
|
||||
// SAFETY: a valid, aligned, non-NULL pointer and length are given which
|
||||
// point to a single allocation.
|
||||
let len = unsafe {
|
||||
let buf_size = buf.len();
|
||||
let buf_ptr = buf.as_mut_ptr().cast::<c_char>();
|
||||
iana_time_zone_haiku_get_tz(buf_ptr, buf_size)
|
||||
};
|
||||
// The name should not be empty, or excessively long.
|
||||
match buf.get(..len)? {
|
||||
b"" => None,
|
||||
s => Some(std::str::from_utf8(s).ok()?.to_owned()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
#[cfg(not(target_os = "haiku"))]
|
||||
fn test_fallback_on_non_haiku_platforms() {
|
||||
assert!(super::get_timezone().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "haiku")]
|
||||
fn test_retrieve_time_zone_on_haiku_platforms() {
|
||||
let timezone = super::get_timezone().unwrap();
|
||||
assert!(!timezone.is_empty());
|
||||
}
|
||||
}
|
||||
1
third_party/rust/iana-time-zone/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/iana-time-zone/.cargo-checksum.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"files":{"CHANGELOG.md":"467a75b20956c8201acdad52b44b1cd146913a4df9f5282e47bf144dea2878da","Cargo.lock":"0cd9cc06007ba8411bb0c1202ac7a587b261aa355287f54737ca0d5c0554f160","Cargo.toml":"b5fcc4b21e07e9f085a5ed540134e01f107e0af2e9358e3cb49aaebb2d70bfae","LICENSE-APACHE":"696759d65dfe558ff7d9f031c76db19ec5c0767470fb67c4e8d990820d1e99c9","LICENSE-MIT":"da28ccc6b158fc2d8cccc74e99794b1cff1d29bd7bbeb019442fcf0c04c6cad9","README.md":"22f9a823adec27aca10eb6dfb5e4e1e9d485e1d5f78348f788bfc81dbee2787f","bindings.txt":"6e9b2e58051bf22fb39b38dc04e54bf75b6204fbd97df06bc58617b1b5d14461","deny.toml":"bde861b1ca6304017f8a57268058aa99ad33d89b1498bba68086e65cca7fe7a2","examples/get_timezone.rs":"c4db7b1cc71c7b3728ddd70e76c0dbd40239c6b1b8c705cb63476757d3177dec","examples/get_timezone_loop.rs":"5e9da42eabd529f5f8d04acc5c1eb84575b9ed38a9226e76e91a1717089b016e","examples/stress-test.rs":"3ad469de5a650389699c9ffe5fd78af2bdd46e7140cc05391c60d793fc6f8e98","src/ffi_utils.rs":"4a49637f60e4d2ab1afecb168e884cee8137a32037d440f9f032196c9fd6f159","src/lib.rs":"0982d7d2e2228b3b4d6a62a4dd5e28ea22086074edce0f0b07ce72fefccacdbf","src/platform.rs":"7847381ca6976d9f39ce84759a3b0d7aabe31eae7a88bc20d7fb99bc376782b0","src/tz_aix.rs":"86fd048ff14062c53d1aa770ee715961f7b1099337453ca2eb297a74fc59f673","src/tz_android.rs":"3da37f1b87f87a0ed215734f2b373b2d187bcf49386adfe8bfff207f9a5e8fe2","src/tz_darwin.rs":"8938e34f1f7c4cf813deb8d008ea6ee130dcb30ada6db3d068c3f0990f81165b","src/tz_freebsd.rs":"4247af5c6dd0712705186ed54d789193c64139f707af316d4fde86aa1e2a1b13","src/tz_haiku.rs":"761afd80301683a44bf937bbf6b13c5c792af42ed7037623bbeccbab6d0aa8fc","src/tz_illumos.rs":"375ae951d1417f63e6d77c9add7f7f646f24c0054cb8407bd4b9f06907494888","src/tz_linux.rs":"fc62ae0275e745c4fc6552c25f70812e37e91d78a16d9a40e5688331a7af04ed","src/tz_netbsd.rs":"ec278bbe1cb5f648c063ec23bff6081146454b9f6aa3918b9ca50b8804d5838f","src/tz_ohos.rs":"f1db259b38f6890ec3c7b357e15d10f2aa166dc84394bac58fa62cb31d4559dc","src/tz_wasm32_unknown.rs":"10aa33caa86645a16e2126fe1a86dda2b57f63caa9addcd726245fbc9657dc1b","src/tz_windows.rs":"804c11a3c638c93626459612cd444df64e9962668d1e8cd6f0774301c00c9529","src/windows_bindings.rs":"d58d11d01f5f5335febb91e83f32956801824553e2b26216bfd16e6a0c87803c"},"package":"b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"}
|
||||
353
third_party/rust/iana-time-zone/CHANGELOG.md
vendored
Normal file
353
third_party/rust/iana-time-zone/CHANGELOG.md
vendored
Normal file
@@ -0,0 +1,353 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.1.63] - 2025-03-31
|
||||
### Changes
|
||||
- Bump MSRV (minimum supported rust version) to 1.62 ([#131](https://github.com/strawlab/iana-time-zone/pull/131))
|
||||
- Bump `windows-core` to `0.56-0.61` range ([#131](https://github.com/strawlab/iana-time-zone/pull/131), [#133](https://github.com/strawlab/iana-time-zone/pull/133))
|
||||
|
||||
## [0.1.62] - 2025-03-24
|
||||
### Changed
|
||||
- Bump MSRV (minimum supported rust version) to 1.61 ([#157](https://github.com/strawlab/iana-time-zone/pull/157))
|
||||
- Update to rust edition 2021 ([#161](https://github.com/strawlab/iana-time-zone/pull/161))
|
||||
- Address high and medium severity zizmor findings ([#163](https://github.com/strawlab/iana-time-zone/pull/163))
|
||||
|
||||
### Added
|
||||
- Added support for tvOS, watchOS and visionOS ([#146](https://github.com/strawlab/iana-time-zone/pull/146)).
|
||||
- implement OpenHarmony support ([#150](https://github.com/strawlab/iana-time-zone/pull/150))
|
||||
|
||||
## [0.1.61] - 2024-09-16
|
||||
### Changed
|
||||
|
||||
- Depend on wasm-bindgen 0.2.89 or higher ([#134](https://github.com/strawlab/iana-time-zone/pull/134))
|
||||
- Do not use wasm_bindgen in wasm32-unknown-emscripten environment ([#130](https://github.com/strawlab/iana-time-zone/pull/130))
|
||||
|
||||
## [0.1.60] - 2024-02-03
|
||||
### Changed
|
||||
- correct `windows-core` dependency version ([#127](https://github.com/strawlab/iana-time-zone/pull/127))
|
||||
|
||||
## [0.1.59] - 2023-12-30
|
||||
### Changed
|
||||
- update `windows` dependency ([#125](https://github.com/strawlab/iana-time-zone/pull/125))
|
||||
|
||||
## [0.1.58] - 2023-10-17
|
||||
### Added
|
||||
- use windows-core with embedded bindings via windows-bindgen ([#117](https://github.com/strawlab/iana-time-zone/pull/117))
|
||||
- implement GNU Hurd support ([#121](https://github.com/strawlab/iana-time-zone/pull/121))
|
||||
- implement AIX support ([#57](https://github.com/strawlab/iana-time-zone/pull/57))
|
||||
|
||||
## [0.1.57] - 2023-06-07
|
||||
### Added
|
||||
- implement OpenWRT support ([#109](https://github.com/strawlab/iana-time-zone/pull/109))
|
||||
|
||||
## [0.1.56] - 2023-04-03
|
||||
### Changed
|
||||
- update `windows` dependency ([#102](https://github.com/strawlab/iana-time-zone/pull/102))
|
||||
|
||||
## [0.1.55] - 2023-03-30
|
||||
### Changed
|
||||
- update `windows` dependency ([#101](https://github.com/strawlab/iana-time-zone/pull/101))
|
||||
|
||||
## [0.1.54] - 2023-03-21
|
||||
### Changed
|
||||
- replace `winapi` dependency with `windows` ([#97](https://github.com/strawlab/iana-time-zone/pull/97))
|
||||
- bump msrv to 1.48 ([#91](https://github.com/strawlab/iana-time-zone/pull/91))
|
||||
|
||||
## [0.1.53] - 2022-10-28
|
||||
### Fixed
|
||||
- remove lint causing breakage on rust 1.45-1.51 ([#84](https://github.com/strawlab/iana-time-zone/pull/84))
|
||||
|
||||
## [0.1.52] - 2022-10-28
|
||||
### Fixed
|
||||
- fix for NixOS ([#81](https://github.com/strawlab/iana-time-zone/pull/81))
|
||||
|
||||
### Changed
|
||||
- allow building the haiku crate on other hosts([#75](https://github.com/strawlab/iana-time-zone/pull/75))
|
||||
- various improvements in continuous integration and source quality
|
||||
([#76](https://github.com/strawlab/iana-time-zone/pull/76)),
|
||||
([#77](https://github.com/strawlab/iana-time-zone/pull/77)),
|
||||
([#78](https://github.com/strawlab/iana-time-zone/pull/78)),
|
||||
([#81](https://github.com/strawlab/iana-time-zone/pull/81))
|
||||
|
||||
## [0.1.51] - 2022-10-08
|
||||
### Changed
|
||||
- bump MSRV to 1.38 ([#70](https://github.com/strawlab/iana-time-zone/pull/70))
|
||||
- Refactor Android property key CStr construction to add tests ([#69](https://github.com/strawlab/iana-time-zone/pull/69))
|
||||
- Refactor MacOS implementation a lot ([#67](https://github.com/strawlab/iana-time-zone/pull/67))
|
||||
|
||||
### Added
|
||||
- Implement for Haiku ([#66](https://github.com/strawlab/iana-time-zone/pull/66))
|
||||
|
||||
### Fixed
|
||||
- Fix spelling of 'initialized' in sync::Once statics ([#63](https://github.com/strawlab/iana-time-zone/pull/63))
|
||||
|
||||
## [0.1.50] - 2022-09-23
|
||||
### Fixed
|
||||
- Reduce MSRV for Android again ([#62](https://github.com/strawlab/iana-time-zone/pull/62))
|
||||
|
||||
## [0.1.49] - 2022-09-22
|
||||
### Changed
|
||||
- `once_cell` dependency is not needed ([#61](https://github.com/strawlab/iana-time-zone/pull/61))
|
||||
|
||||
## [0.1.48] - 2022-09-12
|
||||
### Changed
|
||||
- Downgrade requirements for WASM dependencies ([#58](https://github.com/strawlab/iana-time-zone/pull/58))
|
||||
- Reduce MSRV for Tier 1 platforms to 1.31 ([#59](https://github.com/strawlab/iana-time-zone/pull/59))
|
||||
|
||||
## [0.1.47] - 2022-08-30
|
||||
### Changed
|
||||
- Update `android_system_properties` to v0.1.5 to run 9786% faster (YMMV) ([#56](https://github.com/strawlab/iana-time-zone/pull/56))
|
||||
|
||||
## [0.1.46] - 2022-08-18
|
||||
### Added
|
||||
- Implement for Solaris ([#55](https://github.com/strawlab/iana-time-zone/pull/55))
|
||||
|
||||
## [0.1.45] - 2022-08-16
|
||||
### Fixed
|
||||
- Fix potential use after free in MacOS / iOS ([#54](https://github.com/strawlab/iana-time-zone/pull/54), [RUSTSEC-2022-0049](https://rustsec.org/advisories/RUSTSEC-2022-0049.html))
|
||||
- Fix typos in README ([#53](https://github.com/strawlab/iana-time-zone/pull/53))
|
||||
|
||||
## [0.1.44] - 2022-08-11
|
||||
### Fixed
|
||||
- "/etc/localtime" may be relative link ([#49](https://github.com/strawlab/iana-time-zone/pull/49))
|
||||
|
||||
## [0.1.43] - 2022-08-11
|
||||
### Changed
|
||||
- Use `core-foundation-sys` instead of `core-foundation` ([#50](https://github.com/strawlab/iana-time-zone/pull/50))
|
||||
|
||||
## [0.1.42] - 2022-08-10
|
||||
### Fixed
|
||||
- Fix implementation for Redhat based distros ([#48](https://github.com/strawlab/iana-time-zone/pull/48))
|
||||
|
||||
## [0.1.41] - 2022-08-02
|
||||
### Added
|
||||
- Add `fallback` feature ([#46](https://github.com/strawlab/iana-time-zone/pull/46))
|
||||
|
||||
## [0.1.40] - 2022-07-29
|
||||
### Added
|
||||
- Implement for Android ([#45](https://github.com/strawlab/iana-time-zone/pull/45))
|
||||
|
||||
## [0.1.38] - 2022-07-27
|
||||
### Added
|
||||
- Implement illumos ([#44](https://github.com/strawlab/iana-time-zone/pull/44))
|
||||
### Changed
|
||||
- Update examples in README
|
||||
|
||||
## [0.1.37] - 2022-07-23
|
||||
### Added
|
||||
- Support iOS ([#41](https://github.com/strawlab/iana-time-zone/pull/41))
|
||||
### Changed
|
||||
- Implement `std::err::source()`, format `IoError` ([#42](https://github.com/strawlab/iana-time-zone/pull/42))
|
||||
|
||||
## [0.1.36] - 2022-07-21
|
||||
### Fixed
|
||||
- Fail to compile for WASI ([#40](https://github.com/strawlab/iana-time-zone/pull/40))
|
||||
|
||||
## [0.1.35] - 2022-06-29
|
||||
### Added
|
||||
- Implement for FreeBSD, NetBSD, OpenBSD and Dragonfly ([#39](https://github.com/strawlab/iana-time-zone/pull/39))
|
||||
|
||||
## [0.1.34] - 2022-06-29
|
||||
### Added
|
||||
- Implement for wasm32 ([#38](https://github.com/strawlab/iana-time-zone/pull/38))
|
||||
|
||||
## [0.1.33] - 2022-04-15
|
||||
### Changed
|
||||
- Use `winapi` crate instead of `windows` crate ([#35](https://github.com/strawlab/iana-time-zone/pull/35))
|
||||
|
||||
## [0.1.32] - 2022-04-06
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.34 to 0.35 ([#34](https://github.com/strawlab/iana-time-zone/pull/34))
|
||||
|
||||
## [0.1.31] - 2022-03-16
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.33 to 0.34 ([#33](https://github.com/strawlab/iana-time-zone/pull/33))
|
||||
|
||||
## [0.1.30] - 2022-02-28
|
||||
### Changed
|
||||
- Fewer string allocations ([#32](https://github.com/strawlab/iana-time-zone/pull/32))
|
||||
|
||||
## [0.1.29] - 2022-02-25
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.32 to 0.33 ([#31](https://github.com/strawlab/iana-time-zone/pull/31))
|
||||
|
||||
## [0.1.28] - 2022-02-04
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.30 to 0.32 ([#30](https://github.com/strawlab/iana-time-zone/pull/30))
|
||||
|
||||
## [0.1.27] - 2022-01-14
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.29 to 0.30 ([#29](https://github.com/strawlab/iana-time-zone/pull/29))
|
||||
|
||||
## [0.1.26] - 2021-12-23
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.28 to 0.29 ([#28](https://github.com/strawlab/iana-time-zone/pull/28))
|
||||
|
||||
## [0.1.25] - 2021-11-18
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.27 to 0.28 ([#27](https://github.com/strawlab/iana-time-zone/pull/27))
|
||||
|
||||
## [0.1.24] - 2021-11-16
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.26 to 0.27 ([#26](https://github.com/strawlab/iana-time-zone/pull/26))
|
||||
|
||||
## [0.1.23] - 2021-11-12
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.25 to 0.26 ([#25](https://github.com/strawlab/iana-time-zone/pull/25))
|
||||
|
||||
## [0.1.22] - 2021-11-08
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.24 to 0.25 ([#24](https://github.com/strawlab/iana-time-zone/pull/24))
|
||||
|
||||
## [0.1.21] - 2021-11-02
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.23 to 0.24 ([#23](https://github.com/strawlab/iana-time-zone/pull/23))
|
||||
|
||||
## [0.1.20] - 2021-10-29
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.21 to 0.23 ([#22](https://github.com/strawlab/iana-time-zone/pull/22))
|
||||
|
||||
## [0.1.19] - 2021-09-27
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.19 to 0.21 ([#18](https://github.com/strawlab/iana-time-zone/pull/18), [#20](https://github.com/strawlab/iana-time-zone/pull/20))
|
||||
- Update `chrono-tz` requirement from 0.5 to 0.6 ([#19](https://github.com/strawlab/iana-time-zone/pull/19))
|
||||
|
||||
## [0.1.18] - 2021-08-23
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.18 to 0.19 ([#17](https://github.com/strawlab/iana-time-zone/pull/17))
|
||||
|
||||
## [0.1.16] - 2021-07-26
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.17 to 0.18 ([#16](https://github.com/strawlab/iana-time-zone/pull/16))
|
||||
|
||||
## [0.1.15] - 2021-07-08
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.14 to 0.17 ([#15](https://github.com/strawlab/iana-time-zone/pull/15))
|
||||
|
||||
## [0.1.14] - 2021-07-07
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.13 to 0.14 ([#14](https://github.com/strawlab/iana-time-zone/pull/14))
|
||||
|
||||
## [0.1.13] - 2021-06-28
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.12 to 0.13 ([#13](https://github.com/strawlab/iana-time-zone/pull/13))
|
||||
|
||||
## [0.1.12] - 2021-06-28
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.11 to 0.12 ([#12](https://github.com/strawlab/iana-time-zone/pull/12))
|
||||
|
||||
## [0.1.11] - 2021-06-12
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.10 to 0.11 ([#11](https://github.com/strawlab/iana-time-zone/pull/11))
|
||||
|
||||
## [0.1.10] - 2021-05-13
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.9 to 0.10 ([#10](https://github.com/strawlab/iana-time-zone/pull/10))
|
||||
|
||||
## [0.1.9] - 2021-04-28
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.8 to 0.9 ([#8](https://github.com/strawlab/iana-time-zone/pull/8))
|
||||
|
||||
## [0.1.8] - 2021-04-13
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.7 to 0.8 ([#7](https://github.com/strawlab/iana-time-zone/pull/7))
|
||||
|
||||
## [0.1.7] - 2021-03-30
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.6 to 0.7 ([#6](https://github.com/strawlab/iana-time-zone/pull/6))
|
||||
|
||||
## [0.1.6] - 2021-03-24
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.5 to 0.6 ([#5](https://github.com/strawlab/iana-time-zone/pull/5))
|
||||
|
||||
## [0.1.5] - 2021-03-20
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.4 to 0.5 ([#4](https://github.com/strawlab/iana-time-zone/pull/4))
|
||||
|
||||
## [0.1.4] - 2021-03-11
|
||||
### Changed
|
||||
- Update `windows` requirement from 0.3 to 0.4 ([#3](https://github.com/strawlab/iana-time-zone/pull/3))
|
||||
|
||||
## [0.1.3] - 2021-02-22
|
||||
### Changed
|
||||
- Use `windows` crate instead of `winrt`
|
||||
|
||||
## [0.1.2] - 2020-10-09
|
||||
### Changed
|
||||
- Update `core-foundation` requirement from 0.7 to 0.9 ([#1](https://github.com/strawlab/iana-time-zone/pull/1))
|
||||
|
||||
## [0.1.1] - 2020-06-27
|
||||
### Changed
|
||||
- Update `core-foundation` requirement from 0.5 to 0.7
|
||||
|
||||
## [0.1.0] - 2020-06-27
|
||||
### Added
|
||||
- Implement for Linux, Windows, MacOS
|
||||
|
||||
[0.1.63]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.63
|
||||
[0.1.62]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.62
|
||||
[0.1.61]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.61
|
||||
[0.1.60]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.60
|
||||
[0.1.59]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.59
|
||||
[0.1.58]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.58
|
||||
[0.1.57]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.57
|
||||
[0.1.56]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.56
|
||||
[0.1.55]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.55
|
||||
[0.1.54]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.54
|
||||
[0.1.53]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.53
|
||||
[0.1.52]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.52
|
||||
[0.1.51]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.51
|
||||
[0.1.50]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.50
|
||||
[0.1.49]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.49
|
||||
[0.1.48]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.48
|
||||
[0.1.47]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.47
|
||||
[0.1.46]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.46
|
||||
[0.1.45]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.45
|
||||
[0.1.44]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.44
|
||||
[0.1.43]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.43
|
||||
[0.1.42]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.42
|
||||
[0.1.41]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.41
|
||||
[0.1.40]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.40
|
||||
[0.1.39]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.39
|
||||
[0.1.38]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.38
|
||||
[0.1.37]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.37
|
||||
[0.1.36]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.36
|
||||
[0.1.35]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.35
|
||||
[0.1.34]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.34
|
||||
[0.1.33]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.33
|
||||
[0.1.32]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.32
|
||||
[0.1.31]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.31
|
||||
[0.1.30]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.30
|
||||
[0.1.29]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.29
|
||||
[0.1.28]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.28
|
||||
[0.1.27]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.27
|
||||
[0.1.26]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.26
|
||||
[0.1.25]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.25
|
||||
[0.1.24]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.24
|
||||
[0.1.23]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.23
|
||||
[0.1.22]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.22
|
||||
[0.1.21]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.21
|
||||
[0.1.20]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.20
|
||||
[0.1.19]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.19
|
||||
[0.1.18]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.18
|
||||
[0.1.17]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.17
|
||||
[0.1.16]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.16
|
||||
[0.1.15]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.15
|
||||
[0.1.14]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.14
|
||||
[0.1.13]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.13
|
||||
[0.1.12]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.12
|
||||
[0.1.11]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.11
|
||||
[0.1.10]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.10
|
||||
[0.1.9]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.9
|
||||
[0.1.8]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.8
|
||||
[0.1.7]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.7
|
||||
[0.1.6]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.6
|
||||
[0.1.5]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.5
|
||||
[0.1.4]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.4
|
||||
[0.1.3]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.3
|
||||
[0.1.2]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.2
|
||||
[0.1.1]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.1
|
||||
[0.1.0]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.0
|
||||
590
third_party/rust/iana-time-zone/Cargo.lock
generated
vendored
Normal file
590
third_party/rust/iana-time-zone/Cargo.lock
generated
vendored
Normal file
@@ -0,0 +1,590 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono-tz"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efdce149c370f133a071ca8ef6ea340b7b88748ab0810097a9e2976eaa34b4f3"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"chrono-tz-build",
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono-tz-build"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f10f8c9340e31fc120ff885fcdb54a0b48e474bbd77cab557f0c30a3e569402"
|
||||
dependencies = [
|
||||
"parse-zoneinfo",
|
||||
"phf_codegen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.63"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"chrono-tz",
|
||||
"core-foundation-sys",
|
||||
"getrandom",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-test",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.171"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "minicov"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "parse-zoneinfo"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24"
|
||||
dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test"
|
||||
version = "0.3.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"minicov",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-bindgen-test-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test-macro"
|
||||
version = "0.3.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.61.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
93
third_party/rust/iana-time-zone/Cargo.toml
vendored
Normal file
93
third_party/rust/iana-time-zone/Cargo.toml
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2021"
|
||||
rust-version = "1.62.0"
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.63"
|
||||
authors = [
|
||||
"Andrew Straw <strawman@astraw.com>",
|
||||
"René Kijewski <rene.kijewski@fu-berlin.de>",
|
||||
"Ryan Lopopolo <rjl@hyperbo.la>",
|
||||
]
|
||||
build = false
|
||||
autolib = false
|
||||
autobins = false
|
||||
autoexamples = false
|
||||
autotests = false
|
||||
autobenches = false
|
||||
description = "get the IANA time zone for the current system"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"IANA",
|
||||
"time",
|
||||
]
|
||||
categories = [
|
||||
"date-and-time",
|
||||
"internationalization",
|
||||
"os",
|
||||
]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/strawlab/iana-time-zone"
|
||||
|
||||
[features]
|
||||
fallback = []
|
||||
|
||||
[lib]
|
||||
name = "iana_time_zone"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[example]]
|
||||
name = "get_timezone"
|
||||
path = "examples/get_timezone.rs"
|
||||
|
||||
[[example]]
|
||||
name = "get_timezone_loop"
|
||||
path = "examples/get_timezone_loop.rs"
|
||||
|
||||
[[example]]
|
||||
name = "stress-test"
|
||||
path = "examples/stress-test.rs"
|
||||
|
||||
[dev-dependencies.chrono-tz]
|
||||
version = "0.10.1"
|
||||
|
||||
[dev-dependencies.getrandom]
|
||||
version = "0.2.1"
|
||||
|
||||
[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies.js-sys]
|
||||
version = "0.3.66"
|
||||
|
||||
[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies.log]
|
||||
version = "0.4.14"
|
||||
|
||||
[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies.wasm-bindgen]
|
||||
version = "0.2.89"
|
||||
|
||||
[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dev-dependencies.getrandom]
|
||||
version = "0.2.1"
|
||||
features = ["js"]
|
||||
|
||||
[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dev-dependencies.wasm-bindgen-test]
|
||||
version = "0.3.46"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies.android_system_properties]
|
||||
version = "0.1.5"
|
||||
|
||||
[target.'cfg(target_os = "haiku")'.dependencies.iana-time-zone-haiku]
|
||||
version = "0.1.1"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies.windows-core]
|
||||
version = ">=0.56, <=0.61"
|
||||
|
||||
[target.'cfg(target_vendor = "apple")'.dependencies.core-foundation-sys]
|
||||
version = "0.8.6"
|
||||
201
third_party/rust/iana-time-zone/LICENSE-APACHE
vendored
Normal file
201
third_party/rust/iana-time-zone/LICENSE-APACHE
vendored
Normal file
@@ -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 2020 Andrew Straw
|
||||
|
||||
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.
|
||||
25
third_party/rust/iana-time-zone/LICENSE-MIT
vendored
Normal file
25
third_party/rust/iana-time-zone/LICENSE-MIT
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2020 Andrew D. Straw
|
||||
|
||||
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.
|
||||
51
third_party/rust/iana-time-zone/README.md
vendored
Normal file
51
third_party/rust/iana-time-zone/README.md
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
# iana-time-zone - get the IANA time zone for the current system
|
||||
|
||||
[](https://crates.io/crates/iana-time-zone)
|
||||
[](https://docs.rs/iana-time-zone/)
|
||||
[](https://crates.io/crates/iana-time-zone)
|
||||
[](https://github.com/strawlab/iana-time-zone/actions?query=branch%3Amain)
|
||||
|
||||
This small utility crate gets the IANA time zone for the current system. This is
|
||||
also known as the [tz database], tzdata, the zoneinfo database, and the Olson
|
||||
database.
|
||||
|
||||
[tz database]: https://en.wikipedia.org/wiki/Tz_database
|
||||
|
||||
Example:
|
||||
|
||||
```rust
|
||||
// Get the current time zone as a string.
|
||||
let tz_str = iana_time_zone::get_timezone()?;
|
||||
println!("The current time zone is: {}", tz_str);
|
||||
```
|
||||
|
||||
You can test this is working on your platform with:
|
||||
|
||||
```
|
||||
cargo run --example get_timezone
|
||||
```
|
||||
|
||||
## Minimum supported rust version policy
|
||||
|
||||
This crate has a minimum supported rust version (MSRV) of 1.62.0 for [Tier 1]
|
||||
platforms.
|
||||
|
||||
[tier 1]: https://doc.rust-lang.org/1.62.0/rustc/platform-support.html
|
||||
|
||||
Updates to the MSRV are sometimes necessary due to the MSRV of dependencies.
|
||||
MSRV updates will not be indicated as a breaking change to the semver version.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
<http://www.apache.org/licenses/LICENSE-2.0>)
|
||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||
<http://opensource.org/licenses/MIT>)
|
||||
|
||||
at your option.
|
||||
|
||||
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.
|
||||
4
third_party/rust/iana-time-zone/bindings.txt
vendored
Normal file
4
third_party/rust/iana-time-zone/bindings.txt
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
--out src/windows_bindings.rs
|
||||
|
||||
--filter
|
||||
Windows.Globalization.Calendar
|
||||
4
third_party/rust/iana-time-zone/deny.toml
vendored
Normal file
4
third_party/rust/iana-time-zone/deny.toml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
[licenses]
|
||||
version = 2
|
||||
allow = ["Apache-2.0", "MIT", "Unicode-3.0"]
|
||||
private = { ignore = true }
|
||||
6
third_party/rust/iana-time-zone/examples/get_timezone.rs
vendored
Normal file
6
third_party/rust/iana-time-zone/examples/get_timezone.rs
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
use iana_time_zone::{get_timezone, GetTimezoneError};
|
||||
|
||||
fn main() -> Result<(), GetTimezoneError> {
|
||||
println!("{}", get_timezone()?);
|
||||
Ok(())
|
||||
}
|
||||
13
third_party/rust/iana-time-zone/examples/get_timezone_loop.rs
vendored
Normal file
13
third_party/rust/iana-time-zone/examples/get_timezone_loop.rs
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use iana_time_zone::{get_timezone, GetTimezoneError};
|
||||
|
||||
const WAIT: Duration = Duration::from_secs(1);
|
||||
|
||||
fn main() -> Result<(), GetTimezoneError> {
|
||||
loop {
|
||||
println!("{}", get_timezone()?);
|
||||
thread::sleep(WAIT);
|
||||
}
|
||||
}
|
||||
25
third_party/rust/iana-time-zone/examples/stress-test.rs
vendored
Normal file
25
third_party/rust/iana-time-zone/examples/stress-test.rs
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::thread::spawn;
|
||||
|
||||
use iana_time_zone::get_timezone;
|
||||
|
||||
const THREADS: usize = 10;
|
||||
const ITERATIONS: usize = 100_000;
|
||||
|
||||
static COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
fn main() {
|
||||
let mut threads = Vec::with_capacity(THREADS);
|
||||
for _ in 0..THREADS {
|
||||
threads.push(spawn(|| {
|
||||
for _ in 0..ITERATIONS {
|
||||
get_timezone().unwrap();
|
||||
COUNT.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
}));
|
||||
}
|
||||
for thread in threads {
|
||||
thread.join().unwrap();
|
||||
}
|
||||
assert_eq!(COUNT.load(Ordering::SeqCst), THREADS * ITERATIONS);
|
||||
}
|
||||
718
third_party/rust/iana-time-zone/src/ffi_utils.rs
vendored
Normal file
718
third_party/rust/iana-time-zone/src/ffi_utils.rs
vendored
Normal file
@@ -0,0 +1,718 @@
|
||||
//! Cross platform FFI helpers.
|
||||
|
||||
#[cfg(any(test, target_os = "android"))]
|
||||
use std::ffi::CStr;
|
||||
|
||||
/// A buffer to store the timezone name when calling the C API.
|
||||
#[cfg(any(test, target_vendor = "apple", target_env = "ohos"))]
|
||||
pub(crate) mod buffer {
|
||||
/// The longest name in the IANA time zone database is 32 ASCII characters long.
|
||||
pub const MAX_LEN: usize = 64;
|
||||
|
||||
/// Return a buffer to store the timezone name.
|
||||
///
|
||||
/// The buffer is used to store the timezone name when calling the C API.
|
||||
pub const fn tzname_buf() -> [u8; MAX_LEN] {
|
||||
[0; MAX_LEN]
|
||||
}
|
||||
}
|
||||
|
||||
// The system property named 'persist.sys.timezone' contains the name of the
|
||||
// current timezone.
|
||||
//
|
||||
// From https://android.googlesource.com/platform/bionic/+/gingerbread-release/libc/docs/OVERVIEW.TXT#79:
|
||||
//
|
||||
// > The name of the current timezone is taken from the TZ environment variable,
|
||||
// > if defined. Otherwise, the system property named 'persist.sys.timezone' is
|
||||
// > checked instead.
|
||||
//
|
||||
// TODO: Use a `c"..."` literal when MSRV is upgraded beyond 1.77.0.
|
||||
// https://doc.rust-lang.org/edition-guide/rust-2021/c-string-literals.html
|
||||
#[cfg(any(test, target_os = "android"))]
|
||||
const ANDROID_TIMEZONE_PROPERTY_NAME: &[u8] = b"persist.sys.timezone\0";
|
||||
|
||||
/// Return a [`CStr`] to access the timezone from an Android system properties
|
||||
/// environment.
|
||||
#[cfg(any(test, target_os = "android"))]
|
||||
pub(crate) fn android_timezone_property_name() -> &'static CStr {
|
||||
// In tests or debug mode, opt into extra runtime checks.
|
||||
if cfg!(any(test, debug_assertions)) {
|
||||
return CStr::from_bytes_with_nul(ANDROID_TIMEZONE_PROPERTY_NAME).unwrap();
|
||||
}
|
||||
|
||||
// SAFETY: the key is NUL-terminated and there are no other NULs, this
|
||||
// invariant is checked in tests.
|
||||
unsafe { CStr::from_bytes_with_nul_unchecked(ANDROID_TIMEZONE_PROPERTY_NAME) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::mem::size_of_val;
|
||||
use std::ffi::CStr;
|
||||
|
||||
use super::buffer::{tzname_buf, MAX_LEN};
|
||||
use super::{android_timezone_property_name, ANDROID_TIMEZONE_PROPERTY_NAME};
|
||||
|
||||
#[test]
|
||||
fn test_android_timezone_property_name_is_valid_cstr() {
|
||||
CStr::from_bytes_with_nul(ANDROID_TIMEZONE_PROPERTY_NAME).unwrap();
|
||||
|
||||
let mut invalid_property_name = ANDROID_TIMEZONE_PROPERTY_NAME.to_owned();
|
||||
invalid_property_name.push(b'\0');
|
||||
CStr::from_bytes_with_nul(&invalid_property_name).unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_android_timezone_property_name_getter() {
|
||||
let key = android_timezone_property_name().to_bytes_with_nul();
|
||||
assert_eq!(key, ANDROID_TIMEZONE_PROPERTY_NAME);
|
||||
std::str::from_utf8(key).unwrap();
|
||||
}
|
||||
|
||||
/// An exhaustive set of IANA timezone names for testing.
|
||||
///
|
||||
/// Pulled from Wikipedia as of Sat March 22, 2025:
|
||||
///
|
||||
/// - <https://en.wikipedia.org/wiki/List_of_tz_database_time_zones>
|
||||
/// - <https://en.wikipedia.org/w/index.php?title=List_of_tz_database_time_zones&oldid=1281103182>
|
||||
static KNOWN_TIMEZONE_NAMES: &[&str] = &[
|
||||
"Africa/Abidjan",
|
||||
"Africa/Accra",
|
||||
"Africa/Addis_Ababa",
|
||||
"Africa/Algiers",
|
||||
"Africa/Asmara",
|
||||
"Africa/Asmera",
|
||||
"Africa/Bamako",
|
||||
"Africa/Bangui",
|
||||
"Africa/Banjul",
|
||||
"Africa/Bissau",
|
||||
"Africa/Blantyre",
|
||||
"Africa/Brazzaville",
|
||||
"Africa/Bujumbura",
|
||||
"Africa/Cairo",
|
||||
"Africa/Casablanca",
|
||||
"Africa/Conakry",
|
||||
"Africa/Dakar",
|
||||
"Africa/Dar_es_Salaam",
|
||||
"Africa/Djibouti",
|
||||
"Africa/Douala",
|
||||
"Africa/El_Aaiun",
|
||||
"Africa/Freetown",
|
||||
"Africa/Gaborone",
|
||||
"Africa/Harare",
|
||||
"Africa/Johannesburg",
|
||||
"Africa/Juba",
|
||||
"Africa/Kampala",
|
||||
"Africa/Khartoum",
|
||||
"Africa/Kigali",
|
||||
"Africa/Libreville",
|
||||
"Africa/Lome",
|
||||
"Africa/Luanda",
|
||||
"Africa/Lusaka",
|
||||
"Africa/Malabo",
|
||||
"Africa/Maseru",
|
||||
"Africa/Mbabane",
|
||||
"Africa/Mogadishu",
|
||||
"Africa/Monrovia",
|
||||
"Africa/Nairobi",
|
||||
"Africa/Ndjamena",
|
||||
"Africa/Niamey",
|
||||
"Africa/Nouakchott",
|
||||
"Africa/Ouagadougou",
|
||||
"Africa/Porto-Novo",
|
||||
"Africa/Sao_Tome",
|
||||
"Africa/Timbuktu",
|
||||
"Africa/Tripoli",
|
||||
"Africa/Tunis",
|
||||
"Africa/Windhoek",
|
||||
"America/Anguilla",
|
||||
"America/Antigua",
|
||||
"America/Argentina/ComodRivadavia",
|
||||
"America/Aruba",
|
||||
"America/Asuncion",
|
||||
"America/Atka",
|
||||
"America/Barbados",
|
||||
"America/Belize",
|
||||
"America/Bogota",
|
||||
"America/Buenos_Aires",
|
||||
"America/Caracas",
|
||||
"America/Catamarca",
|
||||
"America/Cayenne",
|
||||
"America/Cayman",
|
||||
"America/Coral_Harbour",
|
||||
"America/Cordoba",
|
||||
"America/Costa_Rica",
|
||||
"America/Curacao",
|
||||
"America/Dominica",
|
||||
"America/El_Salvador",
|
||||
"America/Ensenada",
|
||||
"America/Fort_Wayne",
|
||||
"America/Godthab",
|
||||
"America/Grand_Turk",
|
||||
"America/Grenada",
|
||||
"America/Guadeloupe",
|
||||
"America/Guatemala",
|
||||
"America/Guyana",
|
||||
"America/Havana",
|
||||
"America/Indianapolis",
|
||||
"America/Jamaica",
|
||||
"America/Jujuy",
|
||||
"America/Knox_IN",
|
||||
"America/Kralendijk",
|
||||
"America/La_Paz",
|
||||
"America/Lima",
|
||||
"America/Louisville",
|
||||
"America/Lower_Princes",
|
||||
"America/Managua",
|
||||
"America/Marigot",
|
||||
"America/Martinique",
|
||||
"America/Mendoza",
|
||||
"America/Miquelon",
|
||||
"America/Montevideo",
|
||||
"America/Montreal",
|
||||
"America/Montserrat",
|
||||
"America/Nassau",
|
||||
"America/Nipigon",
|
||||
"America/Pangnirtung",
|
||||
"America/Paramaribo",
|
||||
"America/Port-au-Prince",
|
||||
"America/Port_of_Spain",
|
||||
"America/Porto_Acre",
|
||||
"America/Rainy_River",
|
||||
"America/Rosario",
|
||||
"America/Santa_Isabel",
|
||||
"America/Santo_Domingo",
|
||||
"America/Shiprock",
|
||||
"America/St_Barthelemy",
|
||||
"America/St_Kitts",
|
||||
"America/St_Lucia",
|
||||
"America/St_Thomas",
|
||||
"America/St_Vincent",
|
||||
"America/Tegucigalpa",
|
||||
"America/Thunder_Bay",
|
||||
"America/Tortola",
|
||||
"America/Virgin",
|
||||
"America/Yellowknife",
|
||||
"Antarctica/South_Pole",
|
||||
"Arctic/Longyearbyen",
|
||||
"Asia/Aden",
|
||||
"Asia/Amman",
|
||||
"Asia/Ashgabat",
|
||||
"Asia/Ashkhabad",
|
||||
"Asia/Baghdad",
|
||||
"Asia/Bahrain",
|
||||
"Asia/Baku",
|
||||
"Asia/Beirut",
|
||||
"Asia/Bishkek",
|
||||
"Asia/Brunei",
|
||||
"Asia/Calcutta",
|
||||
"Asia/Choibalsan",
|
||||
"Asia/Chongqing",
|
||||
"Asia/Chungking",
|
||||
"Asia/Colombo",
|
||||
"Asia/Dacca",
|
||||
"Asia/Damascus",
|
||||
"Asia/Dhaka",
|
||||
"Asia/Dili",
|
||||
"Asia/Dushanbe",
|
||||
"Asia/Harbin",
|
||||
"Asia/Hong_Kong",
|
||||
"Asia/Istanbul",
|
||||
"Asia/Jerusalem",
|
||||
"Asia/Kabul",
|
||||
"Asia/Karachi",
|
||||
"Asia/Kashgar",
|
||||
"Asia/Kathmandu",
|
||||
"Asia/Katmandu",
|
||||
"Asia/Kolkata",
|
||||
"Asia/Kuwait",
|
||||
"Asia/Macao",
|
||||
"Asia/Macau",
|
||||
"Asia/Manila",
|
||||
"Asia/Muscat",
|
||||
"Asia/Phnom_Penh",
|
||||
"Asia/Pyongyang",
|
||||
"Asia/Qatar",
|
||||
"Asia/Rangoon",
|
||||
"Asia/Saigon",
|
||||
"Asia/Seoul",
|
||||
"Asia/Taipei",
|
||||
"Asia/Tbilisi",
|
||||
"Asia/Tehran",
|
||||
"Asia/Tel_Aviv",
|
||||
"Asia/Thimbu",
|
||||
"Asia/Thimphu",
|
||||
"Asia/Ujung_Pandang",
|
||||
"Asia/Ulan_Bator",
|
||||
"Asia/Vientiane",
|
||||
"Asia/Yangon",
|
||||
"Asia/Yerevan",
|
||||
"Atlantic/Bermuda",
|
||||
"Atlantic/Cape_Verde",
|
||||
"Atlantic/Faeroe",
|
||||
"Atlantic/Faroe",
|
||||
"Atlantic/Jan_Mayen",
|
||||
"Atlantic/Reykjavik",
|
||||
"Atlantic/South_Georgia",
|
||||
"Atlantic/St_Helena",
|
||||
"Atlantic/Stanley",
|
||||
"Australia/ACT",
|
||||
"Australia/Canberra",
|
||||
"Australia/Currie",
|
||||
"Australia/LHI",
|
||||
"Australia/North",
|
||||
"Australia/NSW",
|
||||
"Australia/Queensland",
|
||||
"Australia/South",
|
||||
"Australia/Tasmania",
|
||||
"Australia/Victoria",
|
||||
"Australia/West",
|
||||
"Australia/Yancowinna",
|
||||
"Brazil/Acre",
|
||||
"Brazil/DeNoronha",
|
||||
"Brazil/East",
|
||||
"Brazil/West",
|
||||
"Canada/Atlantic",
|
||||
"Canada/Central",
|
||||
"Canada/Eastern",
|
||||
"Canada/Mountain",
|
||||
"Canada/Newfoundland",
|
||||
"Canada/Pacific",
|
||||
"Canada/Saskatchewan",
|
||||
"Canada/Yukon",
|
||||
"CET",
|
||||
"Chile/Continental",
|
||||
"Chile/EasterIsland",
|
||||
"CST6CDT",
|
||||
"Cuba",
|
||||
"EET",
|
||||
"Egypt",
|
||||
"Eire",
|
||||
"EST",
|
||||
"EST5EDT",
|
||||
"Etc/GMT",
|
||||
"Etc/GMT+0",
|
||||
"Etc/GMT+1",
|
||||
"Etc/GMT+10",
|
||||
"Etc/GMT+11",
|
||||
"Etc/GMT+12",
|
||||
"Etc/GMT+2",
|
||||
"Etc/GMT+3",
|
||||
"Etc/GMT+4",
|
||||
"Etc/GMT+5",
|
||||
"Etc/GMT+6",
|
||||
"Etc/GMT+7",
|
||||
"Etc/GMT+8",
|
||||
"Etc/GMT+9",
|
||||
"Etc/GMT-0",
|
||||
"Etc/GMT-1",
|
||||
"Etc/GMT-10",
|
||||
"Etc/GMT-11",
|
||||
"Etc/GMT-12",
|
||||
"Etc/GMT-13",
|
||||
"Etc/GMT-14",
|
||||
"Etc/GMT-2",
|
||||
"Etc/GMT-3",
|
||||
"Etc/GMT-4",
|
||||
"Etc/GMT-5",
|
||||
"Etc/GMT-6",
|
||||
"Etc/GMT-7",
|
||||
"Etc/GMT-8",
|
||||
"Etc/GMT-9",
|
||||
"Etc/GMT0",
|
||||
"Etc/Greenwich",
|
||||
"Etc/UCT",
|
||||
"Etc/Universal",
|
||||
"Etc/UTC",
|
||||
"Etc/Zulu",
|
||||
"Europe/Amsterdam",
|
||||
"Europe/Andorra",
|
||||
"Europe/Athens",
|
||||
"Europe/Belfast",
|
||||
"Europe/Belgrade",
|
||||
"Europe/Bratislava",
|
||||
"Europe/Brussels",
|
||||
"Europe/Bucharest",
|
||||
"Europe/Budapest",
|
||||
"Europe/Chisinau",
|
||||
"Europe/Copenhagen",
|
||||
"Europe/Dublin",
|
||||
"Europe/Gibraltar",
|
||||
"Europe/Guernsey",
|
||||
"Europe/Helsinki",
|
||||
"Europe/Isle_of_Man",
|
||||
"Europe/Istanbul",
|
||||
"Europe/Jersey",
|
||||
"Europe/Kiev",
|
||||
"Europe/Ljubljana",
|
||||
"Europe/London",
|
||||
"Europe/Luxembourg",
|
||||
"Europe/Malta",
|
||||
"Europe/Mariehamn",
|
||||
"Europe/Minsk",
|
||||
"Europe/Monaco",
|
||||
"Europe/Nicosia",
|
||||
"Europe/Oslo",
|
||||
"Europe/Paris",
|
||||
"Europe/Podgorica",
|
||||
"Europe/Prague",
|
||||
"Europe/Riga",
|
||||
"Europe/Rome",
|
||||
"Europe/San_Marino",
|
||||
"Europe/Sarajevo",
|
||||
"Europe/Skopje",
|
||||
"Europe/Sofia",
|
||||
"Europe/Stockholm",
|
||||
"Europe/Tallinn",
|
||||
"Europe/Tirane",
|
||||
"Europe/Tiraspol",
|
||||
"Europe/Uzhgorod",
|
||||
"Europe/Vaduz",
|
||||
"Europe/Vatican",
|
||||
"Europe/Vienna",
|
||||
"Europe/Vilnius",
|
||||
"Europe/Warsaw",
|
||||
"Europe/Zagreb",
|
||||
"Europe/Zaporozhye",
|
||||
"Factory",
|
||||
"GB",
|
||||
"GB-Eire",
|
||||
"GMT",
|
||||
"GMT+0",
|
||||
"GMT-0",
|
||||
"GMT0",
|
||||
"Greenwich",
|
||||
"Hongkong",
|
||||
"HST",
|
||||
"Iceland",
|
||||
"Indian/Antananarivo",
|
||||
"Indian/Chagos",
|
||||
"Indian/Christmas",
|
||||
"Indian/Cocos",
|
||||
"Indian/Comoro",
|
||||
"Indian/Kerguelen",
|
||||
"Indian/Mahe",
|
||||
"Indian/Mauritius",
|
||||
"Indian/Mayotte",
|
||||
"Indian/Reunion",
|
||||
"Iran",
|
||||
"Israel",
|
||||
"Jamaica",
|
||||
"Japan",
|
||||
"Kwajalein",
|
||||
"Libya",
|
||||
"MET",
|
||||
"Mexico/BajaNorte",
|
||||
"Mexico/BajaSur",
|
||||
"Mexico/General",
|
||||
"MST",
|
||||
"MST7MDT",
|
||||
"Navajo",
|
||||
"NZ",
|
||||
"NZ-CHAT",
|
||||
"Pacific/Apia",
|
||||
"Pacific/Efate",
|
||||
"Pacific/Enderbury",
|
||||
"Pacific/Fakaofo",
|
||||
"Pacific/Fiji",
|
||||
"Pacific/Funafuti",
|
||||
"Pacific/Guam",
|
||||
"Pacific/Johnston",
|
||||
"Pacific/Nauru",
|
||||
"Pacific/Niue",
|
||||
"Pacific/Norfolk",
|
||||
"Pacific/Noumea",
|
||||
"Pacific/Palau",
|
||||
"Pacific/Pitcairn",
|
||||
"Pacific/Ponape",
|
||||
"Pacific/Rarotonga",
|
||||
"Pacific/Saipan",
|
||||
"Pacific/Samoa",
|
||||
"Pacific/Tongatapu",
|
||||
"Pacific/Truk",
|
||||
"Pacific/Wallis",
|
||||
"Pacific/Yap",
|
||||
"Poland",
|
||||
"Portugal",
|
||||
"PRC",
|
||||
"PST8PDT",
|
||||
"ROC",
|
||||
"ROK",
|
||||
"Singapore",
|
||||
"Turkey",
|
||||
"UCT",
|
||||
"Universal",
|
||||
"US/Alaska",
|
||||
"US/Aleutian",
|
||||
"US/Arizona",
|
||||
"US/Central",
|
||||
"US/East-Indiana",
|
||||
"US/Eastern",
|
||||
"US/Hawaii",
|
||||
"US/Indiana-Starke",
|
||||
"US/Michigan",
|
||||
"US/Mountain",
|
||||
"US/Pacific",
|
||||
"US/Samoa",
|
||||
"UTC",
|
||||
"W-SU",
|
||||
"WET",
|
||||
"Zulu",
|
||||
"America/Rio_Branco",
|
||||
"America/Maceio",
|
||||
"America/Metlakatla",
|
||||
"America/Juneau",
|
||||
"America/Sitka",
|
||||
"America/Adak",
|
||||
"America/Yakutat",
|
||||
"America/Anchorage",
|
||||
"America/Nome",
|
||||
"America/Manaus",
|
||||
"America/Eirunepe",
|
||||
"Asia/Aqtobe",
|
||||
"America/Blanc-Sablon",
|
||||
"America/Puerto_Rico",
|
||||
"America/Goose_Bay",
|
||||
"America/Moncton",
|
||||
"America/Glace_Bay",
|
||||
"America/Halifax",
|
||||
"America/Noronha",
|
||||
"Asia/Atyrau",
|
||||
"Atlantic/Azores",
|
||||
"America/Bahia",
|
||||
"America/Bahia_Banderas",
|
||||
"America/Tijuana",
|
||||
"America/Mazatlan",
|
||||
"Asia/Hovd",
|
||||
"Asia/Shanghai",
|
||||
"Asia/Makassar",
|
||||
"Asia/Pontianak",
|
||||
"Pacific/Bougainville",
|
||||
"America/Fortaleza",
|
||||
"America/Sao_Paulo",
|
||||
"America/Argentina/Buenos_Aires",
|
||||
"Europe/Busingen",
|
||||
"Europe/Zurich",
|
||||
"America/Merida",
|
||||
"Atlantic/Canary",
|
||||
"Antarctica/Casey",
|
||||
"America/Argentina/Catamarca",
|
||||
"America/Indiana/Tell_City",
|
||||
"America/Indiana/Knox",
|
||||
"America/Menominee",
|
||||
"America/North_Dakota/Beulah",
|
||||
"America/North_Dakota/New_Salem",
|
||||
"America/North_Dakota/Center",
|
||||
"America/Rankin_Inlet",
|
||||
"America/Resolute",
|
||||
"America/Winnipeg",
|
||||
"America/Chicago",
|
||||
"Africa/Maputo",
|
||||
"America/Mexico_City",
|
||||
"Africa/Ceuta",
|
||||
"Pacific/Chatham",
|
||||
"America/Chihuahua",
|
||||
"America/Ojinaga",
|
||||
"America/Ciudad_Juarez",
|
||||
"Pacific/Chuuk",
|
||||
"America/Matamoros",
|
||||
"Europe/Simferopol",
|
||||
"Asia/Dubai",
|
||||
"America/Swift_Current",
|
||||
"America/Regina",
|
||||
"Antarctica/Davis",
|
||||
"Africa/Lubumbashi",
|
||||
"Africa/Kinshasa",
|
||||
"Antarctica/DumontDUrville",
|
||||
"America/Monterrey",
|
||||
"Pacific/Easter",
|
||||
"America/Indiana/Marengo",
|
||||
"America/Indiana/Vincennes",
|
||||
"America/Indiana/Indianapolis",
|
||||
"America/Indiana/Petersburg",
|
||||
"America/Indiana/Winamac",
|
||||
"America/Indiana/Vevay",
|
||||
"America/Kentucky/Louisville",
|
||||
"America/Kentucky/Monticello",
|
||||
"America/Detroit",
|
||||
"America/Iqaluit",
|
||||
"America/Toronto",
|
||||
"America/New_York",
|
||||
"America/Guayaquil",
|
||||
"America/Atikokan",
|
||||
"America/Panama",
|
||||
"Asia/Tokyo",
|
||||
"Pacific/Galapagos",
|
||||
"Pacific/Gambier",
|
||||
"Asia/Gaza",
|
||||
"Pacific/Tarawa",
|
||||
"Pacific/Honolulu",
|
||||
"Asia/Jakarta",
|
||||
"America/Argentina/Jujuy",
|
||||
"Indian/Maldives",
|
||||
"Pacific/Kosrae",
|
||||
"Pacific/Kwajalein",
|
||||
"America/Argentina/La_Rioja",
|
||||
"Pacific/Kiritimati",
|
||||
"Australia/Lord_Howe",
|
||||
"Antarctica/Macquarie",
|
||||
"Atlantic/Madeira",
|
||||
"Asia/Kuala_Lumpur",
|
||||
"Asia/Aqtau",
|
||||
"Pacific/Marquesas",
|
||||
"America/Cuiaba",
|
||||
"America/Campo_Grande",
|
||||
"Antarctica/Mawson",
|
||||
"America/Argentina/Mendoza",
|
||||
"Pacific/Pago_Pago",
|
||||
"Pacific/Midway",
|
||||
"America/Argentina/Cordoba",
|
||||
"America/Santiago",
|
||||
"Asia/Nicosia",
|
||||
"Europe/Berlin",
|
||||
"America/Nuuk",
|
||||
"Asia/Almaty",
|
||||
"Pacific/Majuro",
|
||||
"Asia/Ulaanbaatar",
|
||||
"Europe/Kyiv",
|
||||
"America/Edmonton",
|
||||
"America/Boise",
|
||||
"America/Inuvik",
|
||||
"America/Cambridge_Bay",
|
||||
"America/Denver",
|
||||
"Europe/Kaliningrad",
|
||||
"Europe/Kirov",
|
||||
"Europe/Moscow",
|
||||
"Europe/Volgograd",
|
||||
"Europe/Astrakhan",
|
||||
"Europe/Samara",
|
||||
"Europe/Saratov",
|
||||
"Europe/Ulyanovsk",
|
||||
"Asia/Yekaterinburg",
|
||||
"Asia/Omsk",
|
||||
"Asia/Barnaul",
|
||||
"Asia/Novokuznetsk",
|
||||
"Asia/Krasnoyarsk",
|
||||
"Asia/Novosibirsk",
|
||||
"Asia/Tomsk",
|
||||
"Asia/Irkutsk",
|
||||
"Asia/Yakutsk",
|
||||
"Asia/Khandyga",
|
||||
"Asia/Chita",
|
||||
"Asia/Vladivostok",
|
||||
"Asia/Ust-Nera",
|
||||
"Asia/Magadan",
|
||||
"Asia/Srednekolymsk",
|
||||
"Asia/Sakhalin",
|
||||
"Asia/Anadyr",
|
||||
"Asia/Kamchatka",
|
||||
"America/Phoenix",
|
||||
"America/Creston",
|
||||
"America/Dawson_Creek",
|
||||
"America/Fort_Nelson",
|
||||
"America/Whitehorse",
|
||||
"America/Dawson",
|
||||
"America/Danmarkshavn",
|
||||
"Asia/Jayapura",
|
||||
"Australia/Sydney",
|
||||
"Australia/Broken_Hill",
|
||||
"Pacific/Auckland",
|
||||
"Antarctica/McMurdo",
|
||||
"America/St_Johns",
|
||||
"Asia/Bangkok",
|
||||
"Asia/Famagusta",
|
||||
"Australia/Darwin",
|
||||
"America/Los_Angeles",
|
||||
"America/Vancouver",
|
||||
"Antarctica/Palmer",
|
||||
"Pacific/Port_Moresby",
|
||||
"America/Belem",
|
||||
"America/Santarem",
|
||||
"Asia/Singapore",
|
||||
"America/Recife",
|
||||
"Pacific/Kanton",
|
||||
"Pacific/Guadalcanal",
|
||||
"Pacific/Pohnpei",
|
||||
"Europe/Lisbon",
|
||||
"Asia/Qostanay",
|
||||
"Australia/Brisbane",
|
||||
"Australia/Lindeman",
|
||||
"America/Cancun",
|
||||
"Asia/Qyzylorda",
|
||||
"America/Punta_Arenas",
|
||||
"America/Porto_Velho",
|
||||
"America/Boa_Vista",
|
||||
"Antarctica/Rothera",
|
||||
"Asia/Kuching",
|
||||
"America/Argentina/Salta",
|
||||
"America/Argentina/San_Juan",
|
||||
"America/Argentina/San_Luis",
|
||||
"America/Argentina/Rio_Gallegos",
|
||||
"America/Scoresbysund",
|
||||
"Pacific/Tahiti",
|
||||
"America/Hermosillo",
|
||||
"Australia/Adelaide",
|
||||
"Asia/Ho_Chi_Minh",
|
||||
"Europe/Madrid",
|
||||
"Antarctica/Syowa",
|
||||
"Asia/Riyadh",
|
||||
"Australia/Hobart",
|
||||
"America/Thule",
|
||||
"America/Argentina/Ushuaia",
|
||||
"America/Araguaina",
|
||||
"Antarctica/Troll",
|
||||
"America/Argentina/Tucuman",
|
||||
"Asia/Tashkent",
|
||||
"Asia/Samarkand",
|
||||
"Australia/Melbourne",
|
||||
"Antarctica/Vostok",
|
||||
"Pacific/Wake",
|
||||
"Africa/Lagos",
|
||||
"Asia/Hebron",
|
||||
"Asia/Oral",
|
||||
"Australia/Eucla",
|
||||
"Australia/Perth",
|
||||
"Asia/Urumqi",
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_tzname_buffer_fits_all_iana_names() {
|
||||
let buf = tzname_buf();
|
||||
let max_len = buf.len();
|
||||
|
||||
let mut failed_tz_names = vec![];
|
||||
|
||||
for &tz in KNOWN_TIMEZONE_NAMES {
|
||||
// Require max_len + 1 to account for an optional NUL terminator.
|
||||
if tz.len() >= max_len {
|
||||
failed_tz_names.push(tz);
|
||||
}
|
||||
}
|
||||
|
||||
assert!(
|
||||
failed_tz_names.is_empty(),
|
||||
"One or more timezone names exceed the buffer length of {}. Max length of found timezone: {}\n{:?}",
|
||||
max_len,
|
||||
failed_tz_names.iter().map(|s| s.len()).max().unwrap(),
|
||||
failed_tz_names
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tzname_buffer_correct_size() {
|
||||
assert_eq!(
|
||||
MAX_LEN, 64,
|
||||
"Buffer length changed unexpectedly, ensure consistency with documented limit."
|
||||
);
|
||||
assert_eq!(
|
||||
tzname_buf().len(),
|
||||
MAX_LEN,
|
||||
"Buffer length changed unexpectedly, ensure consistency with documented limit."
|
||||
);
|
||||
assert_eq!(
|
||||
size_of_val(&tzname_buf()),
|
||||
MAX_LEN,
|
||||
"Buffer length changed unexpectedly, ensure consistency with documented limit."
|
||||
);
|
||||
}
|
||||
}
|
||||
119
third_party/rust/iana-time-zone/src/lib.rs
vendored
Normal file
119
third_party/rust/iana-time-zone/src/lib.rs
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
#![warn(clippy::all)]
|
||||
#![warn(clippy::cargo)]
|
||||
#![warn(clippy::undocumented_unsafe_blocks)]
|
||||
#![allow(unknown_lints)]
|
||||
#![warn(missing_copy_implementations)]
|
||||
#![warn(missing_debug_implementations)]
|
||||
#![warn(missing_docs)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
#![warn(trivial_casts, trivial_numeric_casts)]
|
||||
#![warn(unused_qualifications)]
|
||||
#![warn(variant_size_differences)]
|
||||
|
||||
//! get the IANA time zone for the current system
|
||||
//!
|
||||
//! This small utility crate provides the
|
||||
//! [`get_timezone()`](fn.get_timezone.html) function.
|
||||
//!
|
||||
//! ```rust
|
||||
//! // Get the current time zone as a string.
|
||||
//! let tz_str = iana_time_zone::get_timezone()?;
|
||||
//! println!("The current time zone is: {}", tz_str);
|
||||
//! # Ok::<(), iana_time_zone::GetTimezoneError>(())
|
||||
//! ```
|
||||
//!
|
||||
//! The resulting string can be parsed to a
|
||||
//! [`chrono-tz::Tz`](https://docs.rs/chrono-tz/latest/chrono_tz/enum.Tz.html)
|
||||
//! variant like this:
|
||||
//! ```rust
|
||||
//! let tz_str = iana_time_zone::get_timezone()?;
|
||||
//! let tz: chrono_tz::Tz = tz_str.parse()?;
|
||||
//! # Ok::<(), Box<dyn std::error::Error>>(())
|
||||
//! ```
|
||||
|
||||
#[allow(dead_code)]
|
||||
mod ffi_utils;
|
||||
|
||||
#[cfg_attr(
|
||||
any(all(target_os = "linux", not(target_env = "ohos")), target_os = "hurd"),
|
||||
path = "tz_linux.rs"
|
||||
)]
|
||||
#[cfg_attr(all(target_os = "linux", target_env = "ohos"), path = "tz_ohos.rs")]
|
||||
#[cfg_attr(target_os = "windows", path = "tz_windows.rs")]
|
||||
#[cfg_attr(target_vendor = "apple", path = "tz_darwin.rs")]
|
||||
#[cfg_attr(
|
||||
all(target_arch = "wasm32", target_os = "unknown"),
|
||||
path = "tz_wasm32_unknown.rs"
|
||||
)]
|
||||
#[cfg_attr(
|
||||
any(target_os = "freebsd", target_os = "dragonfly"),
|
||||
path = "tz_freebsd.rs"
|
||||
)]
|
||||
#[cfg_attr(
|
||||
any(target_os = "netbsd", target_os = "openbsd"),
|
||||
path = "tz_netbsd.rs"
|
||||
)]
|
||||
#[cfg_attr(
|
||||
any(target_os = "illumos", target_os = "solaris"),
|
||||
path = "tz_illumos.rs"
|
||||
)]
|
||||
#[cfg_attr(target_os = "aix", path = "tz_aix.rs")]
|
||||
#[cfg_attr(target_os = "android", path = "tz_android.rs")]
|
||||
#[cfg_attr(target_os = "haiku", path = "tz_haiku.rs")]
|
||||
mod platform;
|
||||
|
||||
/// Error types
|
||||
#[derive(Debug)]
|
||||
pub enum GetTimezoneError {
|
||||
/// Failed to parse
|
||||
FailedParsingString,
|
||||
/// Wrapped IO error
|
||||
IoError(std::io::Error),
|
||||
/// Platform-specific error from the operating system
|
||||
OsError,
|
||||
}
|
||||
|
||||
impl std::error::Error for GetTimezoneError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
GetTimezoneError::FailedParsingString => None,
|
||||
GetTimezoneError::IoError(err) => Some(err),
|
||||
GetTimezoneError::OsError => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for GetTimezoneError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
f.write_str(match self {
|
||||
GetTimezoneError::FailedParsingString => "GetTimezoneError::FailedParsingString",
|
||||
GetTimezoneError::IoError(err) => return err.fmt(f),
|
||||
GetTimezoneError::OsError => "OsError",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for GetTimezoneError {
|
||||
fn from(orig: std::io::Error) -> Self {
|
||||
GetTimezoneError::IoError(orig)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current IANA time zone as a string.
|
||||
///
|
||||
/// See the module-level documentation for a usage example and more details
|
||||
/// about this function.
|
||||
#[inline]
|
||||
pub fn get_timezone() -> Result<String, GetTimezoneError> {
|
||||
platform::get_timezone_inner()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn get_current() {
|
||||
println!("current: {}", get_timezone().unwrap());
|
||||
}
|
||||
}
|
||||
9
third_party/rust/iana-time-zone/src/platform.rs
vendored
Normal file
9
third_party/rust/iana-time-zone/src/platform.rs
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
pub fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
|
||||
Err(crate::GetTimezoneError::OsError)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "fallback"))]
|
||||
compile_error!(
|
||||
"iana-time-zone is currently implemented for Linux, Window, MacOS, FreeBSD, NetBSD, \
|
||||
OpenBSD, Dragonfly, WebAssembly (browser), iOS, Illumos, Android, AIX, Solaris and Haiku.",
|
||||
);
|
||||
7
third_party/rust/iana-time-zone/src/tz_aix.rs
vendored
Normal file
7
third_party/rust/iana-time-zone/src/tz_aix.rs
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
use std::env;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::{BufRead, BufReader};
|
||||
|
||||
pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
|
||||
env::var("TZ").map_err(|_| crate::GetTimezoneError::OsError)
|
||||
}
|
||||
27
third_party/rust/iana-time-zone/src/tz_android.rs
vendored
Normal file
27
third_party/rust/iana-time-zone/src/tz_android.rs
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
use std::sync::Once;
|
||||
|
||||
use android_system_properties::AndroidSystemProperties;
|
||||
|
||||
use crate::ffi_utils::android_timezone_property_name;
|
||||
|
||||
pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
|
||||
let key = android_timezone_property_name();
|
||||
|
||||
get_properties()
|
||||
.and_then(|properties| properties.get_from_cstr(key))
|
||||
.ok_or(crate::GetTimezoneError::OsError)
|
||||
}
|
||||
|
||||
fn get_properties() -> Option<&'static AndroidSystemProperties> {
|
||||
static INITIALIZED: Once = Once::new();
|
||||
static mut PROPERTIES: Option<AndroidSystemProperties> = None;
|
||||
|
||||
INITIALIZED.call_once(|| {
|
||||
let properties = AndroidSystemProperties::new();
|
||||
// SAFETY: `INITIALIZED` is synchronizing. The variable is only assigned to once.
|
||||
unsafe { PROPERTIES = Some(properties) };
|
||||
});
|
||||
|
||||
// SAFETY: `INITIALIZED` is synchronizing. The variable is only assigned to once.
|
||||
unsafe { PROPERTIES.as_ref() }
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user