tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 350bf46d5a9066fa74bd0508e81c1813e19cd187
parent d420ac2ce2502ebb624134fae099e36e770ad21f
Author: Erich Gubler <erichdongubler@gmail.com>
Date:   Tue, 14 Oct 2025 20:37:45 +0000

Bug 1990382 - chore: upgrade `chrono` 0.4.41 → 0.4.42, `windows-link` 0.1.1 → 0.2.1 r=supply-chain-reviewers

Some common dependencies are starting to consume `windows-link` 0.2,
like `backtrace` with
<https://github.com/rust-lang/backtrace-rs/pull/727>. This is going to
become increasingly common, as crates previously targeting
`windows-target` and `windows-sys` realize they can use `windows-link`
for the small set of Windows platform APIs they actually need.

This is the shortest path to start consuming `windows-link` 0.2, and
I think it turned out to be pretty easy to review!

Differential Revision: https://phabricator.services.mozilla.com/D265914

Diffstat:
MCargo.lock | 15++++-----------
Msupply-chain/audits.toml | 10++++++++++
Msupply-chain/imports.lock | 7+++++++
Dthird_party/rust/android-tzdata/.cargo-checksum.json | 2--
Dthird_party/rust/android-tzdata/Cargo.toml | 34----------------------------------
Dthird_party/rust/android-tzdata/LICENSE-APACHE | 201-------------------------------------------------------------------------------
Dthird_party/rust/android-tzdata/LICENSE-MIT | 21---------------------
Dthird_party/rust/android-tzdata/README.md | 20--------------------
Dthird_party/rust/android-tzdata/src/lib.rs | 29-----------------------------
Dthird_party/rust/android-tzdata/src/tzdata.rs | 166-------------------------------------------------------------------------------
Mthird_party/rust/chrono/.cargo-checksum.json | 4++--
Mthird_party/rust/chrono/Cargo.lock | 79+++++++++++++++----------------------------------------------------------------
Mthird_party/rust/chrono/Cargo.toml | 12++++--------
Mthird_party/rust/chrono/src/datetime/mod.rs | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Mthird_party/rust/chrono/src/datetime/serde.rs | 16++++------------
Mthird_party/rust/chrono/src/datetime/tests.rs | 60+++++++++++++++++++++++++++++++++++-------------------------
Mthird_party/rust/chrono/src/format/formatting.rs | 71++++++++++++++++++++++++++++++++++++++---------------------------------
Mthird_party/rust/chrono/src/format/mod.rs | 4+++-
Mthird_party/rust/chrono/src/format/parse.rs | 55++++++++++++++++++++++++++++++++++++++++++++++++-------
Mthird_party/rust/chrono/src/format/parsed.rs | 2+-
Mthird_party/rust/chrono/src/format/scan.rs | 3+--
Mthird_party/rust/chrono/src/format/strftime.rs | 587+++++++++++++++++++++++++++++++++++++------------------------------------------
Mthird_party/rust/chrono/src/lib.rs | 9++++++---
Mthird_party/rust/chrono/src/month.rs | 3+++
Mthird_party/rust/chrono/src/naive/date/mod.rs | 58++++++++++++++++++++++++++++++++++++++++++++++++++++------
Mthird_party/rust/chrono/src/naive/date/tests.rs | 71++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Mthird_party/rust/chrono/src/naive/datetime/serde.rs | 8++++----
Mthird_party/rust/chrono/src/naive/datetime/tests.rs | 17+++++++----------
Mthird_party/rust/chrono/src/naive/internals.rs | 8++++----
Mthird_party/rust/chrono/src/naive/isoweek.rs | 8++++----
Mthird_party/rust/chrono/src/naive/time/mod.rs | 2+-
Mthird_party/rust/chrono/src/naive/time/tests.rs | 17+++++++----------
Mthird_party/rust/chrono/src/offset/fixed.rs | 4++--
Mthird_party/rust/chrono/src/offset/local/mod.rs | 13+++++++------
Athird_party/rust/chrono/src/offset/local/tz_data.rs | 267+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mthird_party/rust/chrono/src/offset/local/tz_info/mod.rs | 12++++++------
Mthird_party/rust/chrono/src/offset/local/tz_info/timezone.rs | 66+++---------------------------------------------------------------
Mthird_party/rust/chrono/src/offset/local/unix.rs | 10+++++-----
Mthird_party/rust/chrono/src/offset/mod.rs | 24++++++++++++++++++++++--
Mthird_party/rust/chrono/src/offset/utc.rs | 6+++---
Mthird_party/rust/chrono/src/round.rs | 14+++++++++++++-
Mthird_party/rust/chrono/src/time_delta.rs | 8+++++---
Mthird_party/rust/chrono/src/traits.rs | 12+++---------
Mthird_party/rust/chrono/src/weekday.rs | 5++++-
Mthird_party/rust/chrono/tests/dateutils.rs | 25+++++++++++--------------
Mthird_party/rust/chrono/tests/win_bindings.rs | 4++--
Mthird_party/rust/windows-link/.cargo-checksum.json | 4++--
Mthird_party/rust/windows-link/Cargo.lock | 2+-
Mthird_party/rust/windows-link/Cargo.toml | 13++++---------
Mthird_party/rust/windows-link/readme.md | 2+-
Mthird_party/rust/windows-link/src/lib.rs | 2+-
51 files changed, 1016 insertions(+), 1143 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -72,12 +72,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -836,11 +830,10 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "android-tzdata", "iana-time-zone", "js-sys", "num-traits", @@ -8100,9 +8093,9 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-result" diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml @@ -1332,6 +1332,11 @@ criteria = "safe-to-deploy" delta = "0.4.19 -> 0.4.40" notes = "Significant refactor of both implementation and dependencies." +[[audits.chrono]] +who = "Erich Gubler <erichdongubler@gmail.com>" +criteria = "safe-to-deploy" +delta = "0.4.41 -> 0.4.42" + [[audits.chunked_transfer]] who = "Glenn Watson <git@chillybin.org>" criteria = "safe-to-deploy" @@ -7120,6 +7125,11 @@ criteria = "safe-to-deploy" version = "0.1.1" notes = "A microsoft crate allowing unsafe calls to windows apis." +[[audits.windows-link]] +who = "Erich Gubler <erichdongubler@gmail.com>" +criteria = "safe-to-deploy" +delta = "0.1.1 -> 0.2.0" + [[audits.winreg]] who = "Ray Kraesig <rkraesig@mozilla.com>" criteria = "safe-to-run" diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock @@ -922,6 +922,13 @@ user-id = 64539 user-login = "kennykerr" user-name = "Kenny Kerr" +[[publisher.windows-link]] +version = "0.2.1" +when = "2025-10-06" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + [[publisher.windows-result]] version = "0.2.0" when = "2024-07-03" diff --git a/third_party/rust/android-tzdata/.cargo-checksum.json b/third_party/rust/android-tzdata/.cargo-checksum.json @@ -1 +0,0 @@ -{"files":{"Cargo.toml":"a87d9acc9827a50c7a96a88720c5dd055cbc08b1144dff95bd572ff977d4a79a","LICENSE-APACHE":"4458503dd48e88c4e0b945fb252a08b93c40ec757309b8ffa7c594dfa1e35104","LICENSE-MIT":"002c2696d92b5c8cf956c11072baa58eaf9f6ade995c031ea635c6a1ee342ad1","README.md":"6dfe0c602dc61eebe118900ed66a2c1f7887b9fe95b36e1c2974c4e8fa7ebd4b","src/lib.rs":"8f421233df83f82e737930ca8a2ad254966334183148bcc170f9c405df230de2","src/tzdata.rs":"78920925b04219910511e9a1f036f468cd2925c0054f280d6a00b106529046e7"},"package":"e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"} -\ No newline at end of file diff --git a/third_party/rust/android-tzdata/Cargo.toml b/third_party/rust/android-tzdata/Cargo.toml @@ -1,34 +0,0 @@ -# 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" diff --git a/third_party/rust/android-tzdata/LICENSE-APACHE b/third_party/rust/android-tzdata/LICENSE-APACHE @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/third_party/rust/android-tzdata/LICENSE-MIT b/third_party/rust/android-tzdata/LICENSE-MIT @@ -1,21 +0,0 @@ -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. diff --git a/third_party/rust/android-tzdata/README.md b/third_party/rust/android-tzdata/README.md @@ -1,20 +0,0 @@ -# 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. diff --git a/third_party/rust/android-tzdata/src/lib.rs b/third_party/rust/android-tzdata/src/lib.rs @@ -1,29 +0,0 @@ -//! 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()) -} diff --git a/third_party/rust/android-tzdata/src/tzdata.rs b/third_party/rust/android-tzdata/src/tzdata.rs @@ -1,166 +0,0 @@ -//! 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")); - } -} diff --git a/third_party/rust/chrono/.cargo-checksum.json b/third_party/rust/chrono/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"CITATION.cff":"df0053b04286133fb25628eed113a585335844622d55879d11b60ebdd5040145","Cargo.lock":"58f1fb96cc949c0ddef0b9b3cd7f1355d6eccfb6430b7dd3faef4192c9ad7af2","Cargo.toml":"065640f0cb79a675783b80947472201f8f3ed2ebaa8f12f41b4161f9c8f32e1a","LICENSE.txt":"a3d4e565d73d108b07074757733e1dcd3ee20617350a135e1122935881311d50","src/date.rs":"10f03590ccab03c3efc076ef55bf218f1e93fdd5f85e4440f46b7590373619b1","src/datetime/mod.rs":"8ff1a4373b74ae0b2856a9d99679186ddcee7beb11f2de2a73638af881d0614a","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":"f4586ed6e5d7fc3f8c9b2cd95d9197503879808fbd5f8fc94982eac3602d7130","src/format/scan.rs":"687117a94f7c825a8dd9f6462f9a6dbc400adaf13774d0fb8cdf025598d21cd2","src/format/strftime.rs":"6f2cb8514503ef5d8f1cb2894ac94dc87557c382e3f17a68c69537060a388b13","src/lib.rs":"03389fad1161e475e001747f5644541484a41191ab3b2a1dcc70d4673c8b8273","src/month.rs":"0f174e6b0047a1138f1713e596889097228eed89e267aa744bfecdd0a1efc5e4","src/naive/date/mod.rs":"4076847fa7926dfd70843de2f0322790354cb1cd4c9fd2e62455bacc4f00b318","src/naive/date/tests.rs":"d90f09052d4b4bbab760450d37b19ca698153a10e27249d05f3c11dc297a3e62","src/naive/datetime/mod.rs":"777254ffb6b78ab8824b40807f9d32fc31ecfae446f226cb6223a1039e6f32c7","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":"a7d8318f490941e0253644e74044400de8728ebb81269dc27d1fb366444270ac","src/naive/time/mod.rs":"4c8f70f338add6ddb78dc38dfe282a7c4a24b6cc6b572297a4255f41b0c794c8","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":"4ce230d3c5b6621aa84500485c3a61394e85358dd1ccfe7dd9f9788ef6c96fa3","src/offset/local/unix.rs":"3d701d5d37be23b90ac97d16832553ff6c458e98d1e293616528d85885db8c57","src/offset/local/win_bindings.rs":"2bdb949f451ad9f15146712471d08e7e2660d1049250cbef9ce6c8d0f0062252","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":"13da1dde688fbbe74ca651e262fc41e93110bb1415c573b137865d7e3a067ddb","src/traits.rs":"c0ef03d9d492d3be51ec963887d028d5900e4d24285114fa77db81b8c322caae","src/weekday.rs":"6f8374bf39300fbf08ce6b3f64247afb29b2da9d60b5ff78f72b201b05272305","src/weekday_set.rs":"2bb033856d8a5140d6fc0230357f6387391a4e35cb1c9ce1cefd88db29927c94","tests/dateutils.rs":"905e0f9f8d07f7cbee1b1a36369a1402e4cf62370b52dda1557c563b7a0de758","tests/wasm.rs":"252f16aeeaacbf26ca268b9a5e53aee3560cd1f071142a7a16d1509a02758a17","tests/win_bindings.rs":"62206936e3bd2ae1cc889c45d65d6182f553dc6c2b29c83cb0d897b2406a9040"},"package":"c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"} -\ No newline at end of file +{"files":{"CITATION.cff":"df0053b04286133fb25628eed113a585335844622d55879d11b60ebdd5040145","Cargo.lock":"ee5ed4e827ad0ac4ee8a8f3b18844752de651826b3809db99c4caaec862d2dcd","Cargo.toml":"1b457bfe1cd8f57731fd10d4bb7bc47505cf64fa53e32c4138c48908d3ea042f","LICENSE.txt":"a3d4e565d73d108b07074757733e1dcd3ee20617350a135e1122935881311d50","src/date.rs":"10f03590ccab03c3efc076ef55bf218f1e93fdd5f85e4440f46b7590373619b1","src/datetime/mod.rs":"fd4d4481bb76cb8756b9270b1b759e43922578446f1b48e23cef803e9a83ce8a","src/datetime/serde.rs":"71f3ff0b9f484efa52c5abf811cbcd0da5d9f01b6bb0c9cc577886831fd6231c","src/datetime/tests.rs":"98f1d803a1ce0a3a78a0c45b2523dbb70e0d49e77563e22611956be5c0d55108","src/format/formatting.rs":"2aa1e7cd721a9d54e0824f3e68bdaf364cf1abbc1ade1331f3c1f0dbe43931c2","src/format/locales.rs":"d49de3da43941c94b1108f8fe84915cc5e1c13a1f1c84fd7356f9b7c61f19242","src/format/mod.rs":"3e2488fca1e583a7313130ac2330a67e0b5695487b784ddfd3edc9c93dbac12d","src/format/parse.rs":"8afa42c3cb3ecbff71b2906f849807cca80d8bfd39e03b142649920d2ac37634","src/format/parsed.rs":"419eff9baba77f327d50b5c52f18b489649e99f65eee1ca5322f7e784cc096e9","src/format/scan.rs":"3d4ce9e57dc952941b0d6b0b3f0e2c7ad9b913e9ce4cc68438721a29c753f426","src/format/strftime.rs":"f8ee7bc38299c115b0e74bdaf396dccab6192737c560cef8676cce42c890486d","src/lib.rs":"82ef2cd0d262bcb8e7ff84b06bf523b98fa890c9d68d9c0e15c4f7692d19a186","src/month.rs":"36b5c0b87655c0538fa57b3fb3e4ccbfafe52e7687993020e6843c7173c565fa","src/naive/date/mod.rs":"a05f32acf3c7d809f737a96306b345ea1d0279d3993928e08e83f44f60e70e5b","src/naive/date/tests.rs":"0e1c240e3df5b4b83f5a508f3742d5b59ea280c4043742a29ca47f76abafe17f","src/naive/datetime/mod.rs":"777254ffb6b78ab8824b40807f9d32fc31ecfae446f226cb6223a1039e6f32c7","src/naive/datetime/serde.rs":"0424ad2cfc4b53d5398ea2ade2825e2acd465cf9c49d7df6dbe579c38f409804","src/naive/datetime/tests.rs":"ad845811e0b5917ca1fe1224cc120005b669a2b8c482155dc39a5c538fb376d4","src/naive/internals.rs":"6586264f7b20794b659716c1426da9ef999d6f0f62b1222378365cf9055b1d53","src/naive/isoweek.rs":"0a89405fb6ab186f6fcdee3faf551b0e1c3434fcd5a4e941e46ec61bdeb61f31","src/naive/mod.rs":"a7d8318f490941e0253644e74044400de8728ebb81269dc27d1fb366444270ac","src/naive/time/mod.rs":"fe5be8fa0b2674db174139f7cd712eaeaf8eea611c22f2fbba1b2b99429cd452","src/naive/time/serde.rs":"6c260c6d0c45dec113f3dcff6056450c524511c37f7c30da9324ad80aff2288b","src/naive/time/tests.rs":"4b485a2d7f4ce7b66ca3a433699fa3facb8b9ee0ff0b3661437247c2626d4a52","src/offset/fixed.rs":"4923761c4e2f0f46e4fdcb99134f64a288f869fe6c899b14fdaa115d6272d31c","src/offset/local/mod.rs":"c7e2f1e33dfe965ae12369fdf928144ed8ac2804b0fb5ae026c8e1103f59bdfe","src/offset/local/tz_data.rs":"1c8681efab819aec0a270bc016c2416c7d7201523fce7c7e4d9cb891e74fc378","src/offset/local/tz_info/mod.rs":"aed33c3310e73d02fc1434e7c820bcd38fd76d56d7e6d348b1bb2a1a1b39fee3","src/offset/local/tz_info/parser.rs":"ec21d8739a86fb4e77551733e13af9964fbc01f80c87d7a164f6185ca9928c22","src/offset/local/tz_info/rule.rs":"bfc9e6257aeaaa23edef69b00acee58233846702eb969b8011d1b2425d15d10c","src/offset/local/tz_info/timezone.rs":"b61c4d1be526ab011027fb6a2d1c4e61cc1f4f1eb6ff56d3f7c0b425dfc64feb","src/offset/local/unix.rs":"b9900a80a669f32b3f9112087c269b1bf877d5ed1d5a388702938e1caa864d8a","src/offset/local/win_bindings.rs":"2bdb949f451ad9f15146712471d08e7e2660d1049250cbef9ce6c8d0f0062252","src/offset/local/win_bindings.txt":"d680de1e8fd07d435d20143877dcb3d719d72e24ff7fe5baae97e956c24f7fed","src/offset/local/windows.rs":"e9909f06e84c6334433fe24e1db14ca61c116889df9db6903917a3083fdd4606","src/offset/mod.rs":"3a2501e65263cd535ecea841c04d34cc8c79f15e18acd5e0ce2cdfe25ec5c2c0","src/offset/utc.rs":"8484cee18a06817f7a738bbc1f95fa198660f70f886a2f39de6e1be9ebe7f65e","src/round.rs":"bf9fbdf0a3de7ce396747583039cdade4f3e66ae6bd4053b73856a86a7975dee","src/time_delta.rs":"dab78ca34aec75bf2496d1096d925192f0a8bb6cfdb668e56c62cfeefccba50b","src/traits.rs":"40386bdfe14fdcc04c43f3fb0399624cc054dfe569e2f8136d282920824f3fce","src/weekday.rs":"f5978017e2a2ab4ba104c2b75574cbccbaf5c776e403a200889113ede02fbb70","src/weekday_set.rs":"2bb033856d8a5140d6fc0230357f6387391a4e35cb1c9ce1cefd88db29927c94","tests/dateutils.rs":"e2307421ce438e7af8fbbfde997e3920836d94c5ca5911871119ed6c13f15a0c","tests/wasm.rs":"252f16aeeaacbf26ca268b9a5e53aee3560cd1f071142a7a16d1509a02758a17","tests/win_bindings.rs":"9016f14f78581960251c67af2f0984163220c65ca632770d2b5f9d82ec401ea3"},"package":"145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"} +\ No newline at end of file diff --git a/third_party/rust/chrono/Cargo.lock b/third_party/rust/chrono/Cargo.lock @@ -14,12 +14,6 @@ dependencies = [ ] [[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" @@ -119,9 +113,8 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.41" +version = "0.4.42" dependencies = [ - "android-tzdata", "arbitrary", "bincode", "iana-time-zone", @@ -158,31 +151,6 @@ 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" @@ -194,12 +162,6 @@ dependencies = [ ] [[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" @@ -364,26 +326,6 @@ 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" @@ -703,13 +645,13 @@ dependencies = [ [[package]] name = "windows-bindgen" -version = "0.61.0" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1c59c20569610dd9ed784d5f003fb493ec57b4cf39d974eb03a84bb7156c90" +checksum = "7083d2c4d1e9f0537d4749be6b047cbfc7a245c4c794cafd77456293010d38d7" dependencies = [ - "rayon", "serde", "serde_json", + "windows-threading", ] [[package]] @@ -723,9 +665,9 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" [[package]] name = "windows-sys" @@ -753,6 +695,15 @@ dependencies = [ ] [[package]] +name = "windows-threading" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab47f085ad6932defa48855254c758cdd0e2f2d48e62a34118a268d8f345e118" +dependencies = [ + "windows-link", +] + +[[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/third_party/rust/chrono/Cargo.toml b/third_party/rust/chrono/Cargo.toml @@ -13,7 +13,7 @@ edition = "2021" rust-version = "1.62.0" name = "chrono" -version = "0.4.41" +version = "0.4.42" build = false include = [ "src/*", @@ -60,9 +60,9 @@ alloc = [] clock = [ "winapi", "iana-time-zone", - "android-tzdata", "now", ] +core-error = [] default = [ "clock", "std", @@ -160,18 +160,14 @@ 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(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" +version = "0.2" optional = true [target."cfg(windows)".dev-dependencies.windows-bindgen] -version = "0.61" +version = "0.63" diff --git a/third_party/rust/chrono/src/datetime/mod.rs b/third_party/rust/chrono/src/datetime/mod.rs @@ -714,6 +714,61 @@ impl<Tz: TimeZone> DateTime<Tz> { impl DateTime<Utc> { /// Makes a new `DateTime<Utc>` from the number of non-leap seconds + /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp"). + /// + /// This is a convenience wrapper around [`DateTime::from_timestamp`], + /// which is useful in functions like [`Iterator::map`] to avoid a closure. + /// + /// This is guaranteed to round-trip with regard to [`timestamp`](DateTime::timestamp). + /// + /// If you need to create a `DateTime` with a [`TimeZone`] different from [`Utc`], use + /// [`TimeZone::timestamp_opt`] or [`DateTime::with_timezone`]; if you need to create a + /// `DateTime` with more precision, use [`DateTime::from_timestamp_micros`], + /// [`DateTime::from_timestamp_millis`], or [`DateTime::from_timestamp_nanos`]. + /// + /// # Errors + /// + /// Returns `None` on out-of-range number of seconds, + /// otherwise returns `Some(DateTime {...})`. + /// + /// # Examples + /// + /// Using [`Option::and_then`]: + /// + /// ``` + /// # use chrono::DateTime; + /// let maybe_timestamp: Option<i64> = Some(1431648000); + /// let maybe_dt = maybe_timestamp.and_then(DateTime::from_timestamp_secs); + /// + /// assert!(maybe_dt.is_some()); + /// assert_eq!(maybe_dt.unwrap().to_string(), "2015-05-15 00:00:00 UTC"); + /// ``` + /// + /// Using [`Iterator::map`]: + /// + /// ``` + /// # use chrono::{DateTime, Utc}; + /// let v = vec![i64::MIN, 1_000_000_000, 1_234_567_890, i64::MAX]; + /// let timestamps: Vec<Option<DateTime<Utc>>> = v + /// .into_iter() + /// .map(DateTime::from_timestamp_secs) + /// .collect(); + /// + /// assert_eq!(vec![ + /// None, + /// Some(DateTime::parse_from_rfc3339("2001-09-09 01:46:40Z").unwrap().to_utc()), + /// Some(DateTime::parse_from_rfc3339("2009-02-13 23:31:30Z").unwrap().to_utc()), + /// None, + /// ], timestamps); + /// ``` + /// + #[inline] + #[must_use] + pub const fn from_timestamp_secs(secs: i64) -> Option<Self> { + Self::from_timestamp(secs, 0) + } + + /// Makes a new `DateTime<Utc>` from the number of non-leap seconds /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp") /// and the number of nanoseconds since the last whole non-leap second. /// @@ -1881,7 +1936,7 @@ impl<Tz: TimeZone> From<DateTime<Tz>> for SystemTime { #[cfg(all( target_arch = "wasm32", feature = "wasmbind", - not(any(target_os = "emscripten", target_os = "wasi")) + not(any(target_os = "emscripten", target_os = "wasi", target_os = "linux")) ))] impl From<js_sys::Date> for DateTime<Utc> { fn from(date: js_sys::Date) -> DateTime<Utc> { @@ -1892,7 +1947,7 @@ impl From<js_sys::Date> for DateTime<Utc> { #[cfg(all( target_arch = "wasm32", feature = "wasmbind", - not(any(target_os = "emscripten", target_os = "wasi")) + not(any(target_os = "emscripten", target_os = "wasi", target_os = "linux")) ))] impl From<&js_sys::Date> for DateTime<Utc> { fn from(date: &js_sys::Date) -> DateTime<Utc> { @@ -1903,7 +1958,7 @@ impl From<&js_sys::Date> for DateTime<Utc> { #[cfg(all( target_arch = "wasm32", feature = "wasmbind", - not(any(target_os = "emscripten", target_os = "wasi")) + not(any(target_os = "emscripten", target_os = "wasi", target_os = "linux")) ))] impl From<DateTime<Utc>> for js_sys::Date { /// Converts a `DateTime<Utc>` to a JS `Date`. The resulting value may be lossy, @@ -1930,12 +1985,12 @@ where } } -/// Number of days between Januari 1, 1970 and December 31, 1 BCE which we define to be day 0. +/// Number of days between January 1, 1970 and December 31, 1 BCE which we define to be day 0. /// 4 full leap year cycles until December 31, 1600 4 * 146097 = 584388 /// 1 day until January 1, 1601 1 -/// 369 years until Januari 1, 1970 369 * 365 = 134685 +/// 369 years until January 1, 1970 369 * 365 = 134685 /// of which floor(369 / 4) are leap years floor(369 / 4) = 92 /// except for 1700, 1800 and 1900 -3 + /// -------- /// 719163 -const UNIX_EPOCH_DAY: i64 = 719_163; +pub(crate) const UNIX_EPOCH_DAY: i64 = 719_163; diff --git a/third_party/rust/chrono/src/datetime/serde.rs b/third_party/rust/chrono/src/datetime/serde.rs @@ -242,11 +242,7 @@ pub mod ts_nanoseconds { where E: de::Error, { - DateTime::from_timestamp( - value.div_euclid(1_000_000_000), - (value.rem_euclid(1_000_000_000)) as u32, - ) - .ok_or_else(|| invalid_ts(value)) + Ok(DateTime::from_timestamp_nanos(value)) } /// Deserialize a timestamp in nanoseconds since the epoch @@ -524,11 +520,7 @@ pub mod ts_microseconds { where E: de::Error, { - DateTime::from_timestamp( - value.div_euclid(1_000_000), - (value.rem_euclid(1_000_000) * 1000) as u32, - ) - .ok_or_else(|| invalid_ts(value)) + DateTime::from_timestamp_micros(value).ok_or_else(|| invalid_ts(value)) } /// Deserialize a timestamp in milliseconds since the epoch @@ -1064,7 +1056,7 @@ pub mod ts_seconds { where E: de::Error, { - DateTime::from_timestamp(value, 0).ok_or_else(|| invalid_ts(value)) + DateTime::from_timestamp_secs(value).ok_or_else(|| invalid_ts(value)) } /// Deserialize a timestamp in seconds since the epoch @@ -1075,7 +1067,7 @@ pub mod ts_seconds { if value > i64::MAX as u64 { Err(invalid_ts(value)) } else { - DateTime::from_timestamp(value as i64, 0).ok_or_else(|| invalid_ts(value)) + DateTime::from_timestamp_secs(value as i64).ok_or_else(|| invalid_ts(value)) } } } diff --git a/third_party/rust/chrono/src/datetime/tests.rs b/third_party/rust/chrono/src/datetime/tests.rs @@ -86,7 +86,7 @@ impl TimeZone for DstTester { { MappedLocalTime::None } else { - panic!("Unexpected local time {}", local) + panic!("Unexpected local time {local}") } } @@ -118,7 +118,7 @@ impl TimeZone for DstTester { } else if *utc >= utc_to_winter_transition && *utc < utc_to_summer_transition { DstTester::winter_offset() } else { - panic!("Unexpected utc time {}", utc) + panic!("Unexpected utc time {utc}") } } } @@ -154,7 +154,10 @@ fn test_datetime_from_timestamp_millis() { // that of `from_timestamp_opt`. let secs_test = [0, 1, 2, 1000, 1234, 12345678, -1, -2, -1000, -12345678]; for secs in secs_test.iter().cloned() { - assert_eq!(DateTime::from_timestamp_millis(secs * 1000), DateTime::from_timestamp(secs, 0)); + assert_eq!( + DateTime::from_timestamp_millis(secs * 1000), + DateTime::from_timestamp_secs(secs) + ); } } @@ -191,7 +194,7 @@ fn test_datetime_from_timestamp_micros() { for secs in secs_test.iter().copied() { assert_eq!( DateTime::from_timestamp_micros(secs * 1_000_000), - DateTime::from_timestamp(secs, 0) + DateTime::from_timestamp_secs(secs) ); } } @@ -242,24 +245,34 @@ fn test_datetime_from_timestamp_nanos() { for secs in secs_test.iter().copied() { assert_eq!( Some(DateTime::from_timestamp_nanos(secs * 1_000_000_000)), - DateTime::from_timestamp(secs, 0) + DateTime::from_timestamp_secs(secs) ); } } #[test] +fn test_datetime_from_timestamp_secs() { + let valid = [-2208936075, 0, 119731017, 1234567890, 2034061609]; + + for timestamp_secs in valid.iter().copied() { + let datetime = DateTime::from_timestamp_secs(timestamp_secs).unwrap(); + assert_eq!(timestamp_secs, datetime.timestamp()); + assert_eq!(DateTime::from_timestamp(timestamp_secs, 0).unwrap(), datetime); + } +} + +#[test] fn test_datetime_from_timestamp() { - let from_timestamp = |secs| DateTime::from_timestamp(secs, 0); let ymdhms = |y, m, d, h, n, s| { NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap().and_utc() }; - assert_eq!(from_timestamp(-1), Some(ymdhms(1969, 12, 31, 23, 59, 59))); - assert_eq!(from_timestamp(0), Some(ymdhms(1970, 1, 1, 0, 0, 0))); - assert_eq!(from_timestamp(1), Some(ymdhms(1970, 1, 1, 0, 0, 1))); - assert_eq!(from_timestamp(1_000_000_000), Some(ymdhms(2001, 9, 9, 1, 46, 40))); - assert_eq!(from_timestamp(0x7fffffff), Some(ymdhms(2038, 1, 19, 3, 14, 7))); - assert_eq!(from_timestamp(i64::MIN), None); - assert_eq!(from_timestamp(i64::MAX), None); + assert_eq!(DateTime::from_timestamp_secs(-1), Some(ymdhms(1969, 12, 31, 23, 59, 59))); + assert_eq!(DateTime::from_timestamp_secs(0), Some(ymdhms(1970, 1, 1, 0, 0, 0))); + assert_eq!(DateTime::from_timestamp_secs(1), Some(ymdhms(1970, 1, 1, 0, 0, 1))); + assert_eq!(DateTime::from_timestamp_secs(1_000_000_000), Some(ymdhms(2001, 9, 9, 1, 46, 40))); + assert_eq!(DateTime::from_timestamp_secs(0x7fffffff), Some(ymdhms(2038, 1, 19, 3, 14, 7))); + assert_eq!(DateTime::from_timestamp_secs(i64::MIN), None); + assert_eq!(DateTime::from_timestamp_secs(i64::MAX), None); } #[test] @@ -1029,25 +1042,22 @@ fn test_parse_datetime_utc() { "+82701-05-6T15:9:60.898989898989Z", ]; for &s in &valid { - eprintln!("test_parse_datetime_utc valid {:?}", s); + eprintln!("test_parse_datetime_utc valid {s:?}"); let d = match s.parse::<DateTime<Utc>>() { Ok(d) => d, - Err(e) => panic!("parsing `{}` has failed: {}", s, e), + Err(e) => panic!("parsing `{s}` has failed: {e}"), }; - let s_ = format!("{:?}", d); + let s_ = format!("{d:?}"); // `s` and `s_` may differ, but `s.parse()` and `s_.parse()` must be same let d_ = match s_.parse::<DateTime<Utc>>() { Ok(d) => d, Err(e) => { - panic!("`{}` is parsed into `{:?}`, but reparsing that has failed: {}", s, d, e) + panic!("`{s}` is parsed into `{d:?}`, but reparsing that has failed: {e}") } }; assert!( d == d_, - "`{}` is parsed into `{:?}`, but reparsed result `{:?}` does not match", - s, - d, - d_ + "`{s}` is parsed into `{d:?}`, but reparsed result `{d_:?}` does not match" ); } @@ -1081,7 +1091,7 @@ fn test_parse_datetime_utc() { " +82701 - 05 - 6 T 15 : 9 : 60.898989898989 Z", // valid datetime, wrong format ]; for &s in &invalid { - eprintln!("test_parse_datetime_utc invalid {:?}", s); + eprintln!("test_parse_datetime_utc invalid {s:?}"); assert!(s.parse::<DateTime<Utc>>().is_err()); } } @@ -1568,7 +1578,7 @@ fn test_min_max_getters() { let offset_max = FixedOffset::east_opt(2 * 60 * 60).unwrap(); let beyond_max = offset_max.from_utc_datetime(&NaiveDateTime::MAX); - assert_eq!(format!("{:?}", beyond_min), "-262144-12-31T22:00:00-02:00"); + assert_eq!(format!("{beyond_min:?}"), "-262144-12-31T22:00:00-02:00"); // RFC 2822 doesn't support years with more than 4 digits. // assert_eq!(beyond_min.to_rfc2822(), ""); #[cfg(feature = "alloc")] @@ -1593,7 +1603,7 @@ fn test_min_max_getters() { assert_eq!(beyond_min.second(), 0); assert_eq!(beyond_min.nanosecond(), 0); - assert_eq!(format!("{:?}", beyond_max), "+262143-01-01T01:59:59.999999999+02:00"); + assert_eq!(format!("{beyond_max:?}"), "+262143-01-01T01:59:59.999999999+02:00"); // RFC 2822 doesn't support years with more than 4 digits. // assert_eq!(beyond_max.to_rfc2822(), ""); #[cfg(feature = "alloc")] @@ -1894,7 +1904,7 @@ fn nano_roundrip() { i64::MAX - 1, i64::MAX, ] { - println!("nanos: {}", nanos); + println!("nanos: {nanos}"); let dt = Utc.timestamp_nanos(nanos); let nanos2 = dt.timestamp_nanos_opt().expect("value roundtrips"); assert_eq!(nanos, nanos2); diff --git a/third_party/rust/chrono/src/format/formatting.rs b/third_party/rust/chrono/src/format/formatting.rs @@ -109,7 +109,7 @@ impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> { /// let mut buffer = String::new(); /// let _ = df.write_to(&mut buffer); /// ``` - pub fn write_to(&self, w: &mut impl Write) -> fmt::Result { + pub fn write_to(&self, w: &mut (impl Write + ?Sized)) -> fmt::Result { for item in self.items.clone() { match *item.borrow() { Item::Literal(s) | Item::Space(s) => w.write_str(s), @@ -124,14 +124,19 @@ impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> { } #[cfg(feature = "alloc")] - fn format_numeric(&self, w: &mut impl Write, spec: &Numeric, pad: Pad) -> fmt::Result { + fn format_numeric( + &self, + w: &mut (impl Write + ?Sized), + spec: &Numeric, + pad: Pad, + ) -> fmt::Result { use self::Numeric::*; - fn write_one(w: &mut impl Write, v: u8) -> fmt::Result { + fn write_one(w: &mut (impl Write + ?Sized), 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 { + fn write_two(w: &mut (impl Write + ?Sized), v: u8, pad: Pad) -> fmt::Result { let ones = b'0' + v % 10; match (v / 10, pad) { (0, Pad::None) => {} @@ -142,7 +147,7 @@ impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> { } #[inline] - fn write_year(w: &mut impl Write, year: i32, pad: Pad) -> fmt::Result { + fn write_year(w: &mut (impl Write + ?Sized), year: i32, pad: Pad) -> fmt::Result { if (1000..=9999).contains(&year) { // fast path write_hundreds(w, (year / 100) as u8)?; @@ -153,7 +158,7 @@ impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> { } fn write_n( - w: &mut impl Write, + w: &mut (impl Write + ?Sized), n: usize, v: i64, pad: Pad, @@ -161,15 +166,15 @@ impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> { ) -> fmt::Result { if always_sign { match pad { - Pad::None => write!(w, "{:+}", v), + 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), + Pad::None => write!(w, "{v}"), + Pad::Zero => write!(w, "{v:0n$}"), + Pad::Space => write!(w, "{v:n$}"), } } } @@ -214,7 +219,7 @@ impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> { } #[cfg(feature = "alloc")] - fn format_fixed(&self, w: &mut impl Write, spec: &Fixed) -> fmt::Result { + fn format_fixed(&self, w: &mut (impl Write + ?Sized), spec: &Fixed) -> fmt::Result { use Fixed::*; use InternalInternal::*; @@ -253,7 +258,7 @@ impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> { } else if nano % 1_000 == 0 { write!(w, "{:06}", nano / 1_000) } else { - write!(w, "{:09}", nano) + write!(w, "{nano:09}") } } } @@ -278,7 +283,7 @@ impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> { (Internal(InternalFixed { val: Nanosecond9NoDot }), _, Some(t), _) => { write!(w, "{:09}", t.nanosecond() % 1_000_000_000) } - (TimezoneName, _, _, Some((tz_name, _))) => write!(w, "{}", tz_name), + (TimezoneName, _, _, Some((tz_name, _))) => write!(w, "{tz_name}"), (TimezoneOffset | TimezoneOffsetZ, _, _, Some((_, off))) => { let offset_format = OffsetFormat { precision: OffsetPrecision::Minutes, @@ -387,7 +392,7 @@ pub fn format_item( #[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 { + fn format(&self, w: &mut (impl Write + ?Sized), off: FixedOffset) -> fmt::Result { let off = off.local_minus_utc(); if self.allow_zulu && off == 0 { w.write_char('Z')?; @@ -496,7 +501,7 @@ pub enum SecondsFormat { #[inline] #[cfg(any(feature = "alloc", feature = "serde"))] pub(crate) fn write_rfc3339( - w: &mut impl Write, + w: &mut (impl Write + ?Sized), dt: NaiveDateTime, off: FixedOffset, secform: SecondsFormat, @@ -508,7 +513,7 @@ pub(crate) fn write_rfc3339( write_hundreds(w, (year % 100) as u8)?; } else { // ISO 8601 requires the explicit sign for out-of-range years - write!(w, "{:+05}", year)?; + write!(w, "{year:+05}")?; } w.write_char('-')?; write_hundreds(w, dt.date().month() as u8)?; @@ -534,7 +539,7 @@ pub(crate) fn write_rfc3339( 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::Nanos => write!(w, ".{nano:09}")?, SecondsFormat::AutoSi => { if nano == 0 { } else if nano % 1_000_000 == 0 { @@ -542,7 +547,7 @@ pub(crate) fn write_rfc3339( } else if nano % 1_000 == 0 { write!(w, ".{:06}", nano / 1_000)? } else { - write!(w, ".{:09}", nano)? + write!(w, ".{nano:09}")? } } SecondsFormat::__NonExhaustive => unreachable!(), @@ -560,7 +565,7 @@ pub(crate) fn write_rfc3339( #[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, + w: &mut (impl Write + ?Sized), dt: NaiveDateTime, off: FixedOffset, ) -> fmt::Result { @@ -605,7 +610,7 @@ pub(crate) fn write_rfc2822( } /// Equivalent to `{:02}` formatting for n < 100. -pub(crate) fn write_hundreds(w: &mut impl Write, n: u8) -> fmt::Result { +pub(crate) fn write_hundreds(w: &mut (impl Write + ?Sized), n: u8) -> fmt::Result { if n >= 100 { return Err(fmt::Error); } @@ -763,31 +768,31 @@ mod tests { // 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)); + assert_eq!(" %", format!("{percent:>4}")); + assert_eq!("% ", format!("{percent:<4}")); + assert_eq!(" % ", format!("{percent:^4}")); // 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)); + assert_eq!("——2007", format!("{year:—>6}")); + assert_eq!("2007——", format!("{year:—<6}")); + assert_eq!("—2007—", format!("{year:—^6}")); // 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)); + assert_eq!(" UTC", format!("{tz:>5}")); + assert_eq!("UTC ", format!("{tz:<5}")); + assert_eq!(" UTC ", format!("{tz:^5}")); // [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)); + assert_eq!(" 2007 January 02", format!("{ymd:>17}")); + assert_eq!("2007 January 02 ", format!("{ymd:<17}")); + assert_eq!(" 2007 January 02 ", format!("{ymd:^17}")); // Truncated let time = datetime.format("%T%.6f"); - assert_eq!("12:34:56.1234", format!("{:.13}", time)); + assert_eq!("12:34:56.1234", format!("{time:.13}")); } #[test] diff --git a/third_party/rust/chrono/src/format/mod.rs b/third_party/rust/chrono/src/format/mod.rs @@ -33,6 +33,8 @@ #[cfg(all(feature = "alloc", not(feature = "std"), not(test)))] use alloc::boxed::Box; +#[cfg(all(feature = "core-error", not(feature = "std")))] +use core::error::Error; use core::fmt; use core::str::FromStr; #[cfg(feature = "std")] @@ -450,7 +452,7 @@ impl fmt::Display for ParseError { } } -#[cfg(feature = "std")] +#[cfg(any(feature = "core-error", feature = "std"))] impl Error for ParseError { #[allow(deprecated)] fn description(&self) -> &str { diff --git a/third_party/rust/chrono/src/format/parse.rs b/third_party/rust/chrono/src/format/parse.rs @@ -417,13 +417,34 @@ where s = &s[2..]; } - &Nanosecond | &Nanosecond3 | &Nanosecond6 | &Nanosecond9 => { + &Nanosecond => { if s.starts_with('.') { let nano = try_consume!(scan::nanosecond(&s[1..])); parsed.set_nanosecond(nano)?; } } + &Nanosecond3 => { + if s.starts_with('.') { + let nano = try_consume!(scan::nanosecond_fixed(&s[1..], 3)); + parsed.set_nanosecond(nano)?; + } + } + + &Nanosecond6 => { + if s.starts_with('.') { + let nano = try_consume!(scan::nanosecond_fixed(&s[1..], 6)); + parsed.set_nanosecond(nano)?; + } + } + + &Nanosecond9 => { + if s.starts_with('.') { + let nano = try_consume!(scan::nanosecond_fixed(&s[1..], 9)); + parsed.set_nanosecond(nano)?; + } + } + &Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => { if s.len() < 3 { return Err(TOO_SHORT); @@ -910,7 +931,7 @@ mod tests { #[test] fn test_parse_fixed_nanosecond() { - use crate::format::Fixed::Nanosecond; + use crate::format::Fixed::{Nanosecond, Nanosecond3, Nanosecond6, Nanosecond9}; use crate::format::InternalInternal::*; use crate::format::Item::Literal; use crate::format::Numeric::Second; @@ -944,6 +965,28 @@ mod tests { check(". 4", &[fixed(Nanosecond)], Err(INVALID)); check(" .4", &[fixed(Nanosecond)], Err(TOO_LONG)); // no automatic trimming + // fixed-length fractions of a second + check("", &[fixed(Nanosecond3)], parsed!()); // no field set, but not an error + check("4", &[fixed(Nanosecond3)], Err(TOO_LONG)); // never consumes `4` + check(".12", &[fixed(Nanosecond3)], Err(TOO_SHORT)); + check(".123", &[fixed(Nanosecond3)], parsed!(nanosecond: 123_000_000)); + check(".1234", &[fixed(Nanosecond3)], Err(TOO_LONG)); + check(".1234", &[fixed(Nanosecond3), Literal("4")], parsed!(nanosecond: 123_000_000)); + + check("", &[fixed(Nanosecond6)], parsed!()); // no field set, but not an error + check("4", &[fixed(Nanosecond6)], Err(TOO_LONG)); // never consumes `4` + check(".12345", &[fixed(Nanosecond6)], Err(TOO_SHORT)); + check(".123456", &[fixed(Nanosecond6)], parsed!(nanosecond: 123_456_000)); + check(".1234567", &[fixed(Nanosecond6)], Err(TOO_LONG)); + check(".1234567", &[fixed(Nanosecond6), Literal("7")], parsed!(nanosecond: 123_456_000)); + + check("", &[fixed(Nanosecond9)], parsed!()); // no field set, but not an error + check("4", &[fixed(Nanosecond9)], Err(TOO_LONG)); // never consumes `4` + check(".12345678", &[fixed(Nanosecond9)], Err(TOO_SHORT)); + check(".123456789", &[fixed(Nanosecond9)], parsed!(nanosecond: 123_456_789)); + check(".1234567890", &[fixed(Nanosecond9)], Err(TOO_LONG)); + check(".1234567890", &[fixed(Nanosecond9), Literal("0")], parsed!(nanosecond: 123_456_789)); + // fixed: nanoseconds without the dot check("", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_SHORT)); check(".", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_SHORT)); @@ -1677,13 +1720,12 @@ mod tests { // Test against test data above for &(date, checkdate) in testdates.iter() { #[cfg(feature = "std")] - eprintln!("Test input: {:?}\n Expect: {:?}", date, checkdate); + eprintln!("Test input: {date:?}\n Expect: {checkdate:?}"); let dt = rfc2822_to_datetime(date); // parse a date if dt != checkdate { // check for expected result panic!( - "Date conversion failed for {}\nReceived: {:?}\nExpected: {:?}", - date, dt, checkdate + "Date conversion failed for {date}\nReceived: {dt:?}\nExpected: {checkdate:?}" ); } } @@ -1836,8 +1878,7 @@ mod tests { if dt != checkdate { // check for expected result panic!( - "Date conversion failed for {}\nReceived: {:?}\nExpected: {:?}", - date, dt, checkdate + "Date conversion failed for {date}\nReceived: {dt:?}\nExpected: {checkdate:?}" ); } } diff --git a/third_party/rust/chrono/src/format/parsed.rs b/third_party/rust/chrono/src/format/parsed.rs @@ -832,7 +832,7 @@ impl Parsed { // reconstruct date and time fields from timestamp let ts = timestamp.checked_add(i64::from(offset)).ok_or(OUT_OF_RANGE)?; - let mut datetime = DateTime::from_timestamp(ts, 0).ok_or(OUT_OF_RANGE)?.naive_utc(); + let mut datetime = DateTime::from_timestamp_secs(ts).ok_or(OUT_OF_RANGE)?.naive_utc(); // fill year, ordinal, hour, minute and second fields from timestamp. // if existing fields are consistent, this will allow the full date/time reconstruction. diff --git a/third_party/rust/chrono/src/format/scan.rs b/third_party/rust/chrono/src/format/scan.rs @@ -386,8 +386,7 @@ mod tests { let actual = comment_2822(test_in).map(|(s, _)| s); assert_eq!( *expected, actual, - "{:?} expected to produce {:?}, but produced {:?}.", - test_in, expected, actual + "{test_in:?} expected to produce {expected:?}, but produced {actual:?}." ); } } diff --git a/third_party/rust/chrono/src/format/strftime.rs b/third_party/rust/chrono/src/format/strftime.rs @@ -253,8 +253,7 @@ impl<'a> StrftimeItems<'a> { /// const ITEMS: &[Item<'static>] = &[ /// Item::Numeric(Numeric::Year, Pad::Zero), /// Item::Literal("-"), - /// Item::Literal("%"), - /// Item::Literal("Q"), + /// Item::Literal("%Q"), /// ]; /// println!("{:?}", strftime_parser.clone().collect::<Vec<_>>()); /// assert!(strftime_parser.eq(ITEMS.iter().cloned())); @@ -425,327 +424,25 @@ impl<'a> StrftimeItems<'a> { }) .collect() } -} - -const HAVE_ALTERNATES: &str = "z"; - -impl<'a> Iterator for StrftimeItems<'a> { - type Item = Item<'a>; - - fn next(&mut self) -> Option<Item<'a>> { - // We have items queued to return from a specifier composed of multiple formatting items. - if let Some((item, remainder)) = self.queue.split_first() { - self.queue = remainder; - return Some(item.clone()); - } - - // We are in the middle of parsing the localized formatting string of a specifier. - #[cfg(feature = "unstable-locales")] - if !self.locale_str.is_empty() { - let (remainder, item) = self.parse_next_item(self.locale_str)?; - self.locale_str = remainder; - return Some(item); - } - - // Normal: we are parsing the formatting string. - let (remainder, item) = self.parse_next_item(self.remainder)?; - self.remainder = remainder; - Some(item) - } -} - -impl<'a> StrftimeItems<'a> { - fn error<'b>( - &mut self, - original: &'b str, - error_len: &mut usize, - ch: Option<char>, - ) -> (&'b str, Item<'b>) { - if !self.lenient { - return (&original[*error_len..], Item::Error); - } - - if let Some(c) = ch { - *error_len -= c.len_utf8(); - } - (&original[*error_len..], Item::Literal(&original[..*error_len])) - } fn parse_next_item(&mut self, mut remainder: &'a str) -> Option<(&'a str, Item<'a>)> { use InternalInternal::*; use Item::{Literal, Space}; use Numeric::*; - static D_FMT: &[Item<'static>] = - &[num0(Month), Literal("/"), num0(Day), Literal("/"), num0(YearMod100)]; - static D_T_FMT: &[Item<'static>] = &[ - fixed(Fixed::ShortWeekdayName), - Space(" "), - fixed(Fixed::ShortMonthName), - Space(" "), - nums(Day), - Space(" "), - num0(Hour), - Literal(":"), - num0(Minute), - Literal(":"), - num0(Second), - Space(" "), - num0(Year), - ]; - static T_FMT: &[Item<'static>] = - &[num0(Hour), Literal(":"), num0(Minute), Literal(":"), num0(Second)]; - static T_FMT_AMPM: &[Item<'static>] = &[ - num0(Hour12), - Literal(":"), - num0(Minute), - Literal(":"), - num0(Second), - Space(" "), - fixed(Fixed::UpperAmPm), - ]; - - match remainder.chars().next() { - // we are done - None => None, - + let (original, mut remainder) = match remainder.chars().next()? { // the next item is a specifier - Some('%') => { - let original = remainder; - remainder = &remainder[1..]; - let mut error_len = 0; - if self.lenient { - error_len += 1; - } - - macro_rules! next { - () => { - match remainder.chars().next() { - Some(x) => { - remainder = &remainder[x.len_utf8()..]; - if self.lenient { - error_len += x.len_utf8(); - } - x - } - None => return Some(self.error(original, &mut error_len, None)), // premature end of string - } - }; - } - - let spec = next!(); - let pad_override = match spec { - '-' => Some(Pad::None), - '0' => Some(Pad::Zero), - '_' => Some(Pad::Space), - _ => None, - }; - let is_alternate = spec == '#'; - let spec = if pad_override.is_some() || is_alternate { next!() } else { spec }; - if is_alternate && !HAVE_ALTERNATES.contains(spec) { - return Some(self.error(original, &mut error_len, Some(spec))); - } - - macro_rules! queue { - [$head:expr, $($tail:expr),+ $(,)*] => ({ - const QUEUE: &'static [Item<'static>] = &[$($tail),+]; - self.queue = QUEUE; - $head - }) - } - #[cfg(not(feature = "unstable-locales"))] - macro_rules! queue_from_slice { - ($slice:expr) => {{ - self.queue = &$slice[1..]; - $slice[0].clone() - }}; - } - - let item = match spec { - 'A' => fixed(Fixed::LongWeekdayName), - 'B' => fixed(Fixed::LongMonthName), - 'C' => num0(YearDiv100), - 'D' => { - queue![num0(Month), Literal("/"), num0(Day), Literal("/"), num0(YearMod100)] - } - 'F' => queue![num0(Year), Literal("-"), num0(Month), Literal("-"), num0(Day)], - 'G' => num0(IsoYear), - 'H' => num0(Hour), - 'I' => num0(Hour12), - 'M' => num0(Minute), - 'P' => fixed(Fixed::LowerAmPm), - 'R' => queue![num0(Hour), Literal(":"), num0(Minute)], - 'S' => num0(Second), - 'T' => { - queue![num0(Hour), Literal(":"), num0(Minute), Literal(":"), num0(Second)] - } - 'U' => num0(WeekFromSun), - 'V' => num0(IsoWeek), - 'W' => num0(WeekFromMon), - #[cfg(not(feature = "unstable-locales"))] - 'X' => queue_from_slice!(T_FMT), - #[cfg(feature = "unstable-locales")] - 'X' => self.switch_to_locale_str(locales::t_fmt, T_FMT), - 'Y' => num0(Year), - 'Z' => fixed(Fixed::TimezoneName), - 'a' => fixed(Fixed::ShortWeekdayName), - 'b' | 'h' => fixed(Fixed::ShortMonthName), - #[cfg(not(feature = "unstable-locales"))] - 'c' => queue_from_slice!(D_T_FMT), - #[cfg(feature = "unstable-locales")] - 'c' => self.switch_to_locale_str(locales::d_t_fmt, D_T_FMT), - 'd' => num0(Day), - 'e' => nums(Day), - 'f' => num0(Nanosecond), - 'g' => num0(IsoYearMod100), - 'j' => num0(Ordinal), - 'k' => nums(Hour), - 'l' => nums(Hour12), - 'm' => num0(Month), - 'n' => Space("\n"), - 'p' => fixed(Fixed::UpperAmPm), - 'q' => num(Quarter), - #[cfg(not(feature = "unstable-locales"))] - 'r' => queue_from_slice!(T_FMT_AMPM), - #[cfg(feature = "unstable-locales")] - 'r' => { - if self.locale.is_some() - && locales::t_fmt_ampm(self.locale.unwrap()).is_empty() - { - // 12-hour clock not supported by this locale. Switch to 24-hour format. - self.switch_to_locale_str(locales::t_fmt, T_FMT) - } else { - self.switch_to_locale_str(locales::t_fmt_ampm, T_FMT_AMPM) - } - } - 's' => num(Timestamp), - 't' => Space("\t"), - 'u' => num(WeekdayFromMon), - 'v' => { - queue![ - nums(Day), - Literal("-"), - fixed(Fixed::ShortMonthName), - Literal("-"), - num0(Year) - ] - } - 'w' => num(NumDaysFromSun), - #[cfg(not(feature = "unstable-locales"))] - 'x' => queue_from_slice!(D_FMT), - #[cfg(feature = "unstable-locales")] - 'x' => self.switch_to_locale_str(locales::d_fmt, D_FMT), - 'y' => num0(YearMod100), - 'z' => { - if is_alternate { - internal_fixed(TimezoneOffsetPermissive) - } else { - fixed(Fixed::TimezoneOffset) - } - } - '+' => fixed(Fixed::RFC3339), - ':' => { - if remainder.starts_with("::z") { - remainder = &remainder[3..]; - fixed(Fixed::TimezoneOffsetTripleColon) - } else if remainder.starts_with(":z") { - remainder = &remainder[2..]; - fixed(Fixed::TimezoneOffsetDoubleColon) - } else if remainder.starts_with('z') { - remainder = &remainder[1..]; - fixed(Fixed::TimezoneOffsetColon) - } else { - self.error(original, &mut error_len, None).1 - } - } - '.' => match next!() { - '3' => match next!() { - 'f' => fixed(Fixed::Nanosecond3), - c => { - let res = self.error(original, &mut error_len, Some(c)); - remainder = res.0; - res.1 - } - }, - '6' => match next!() { - 'f' => fixed(Fixed::Nanosecond6), - c => { - let res = self.error(original, &mut error_len, Some(c)); - remainder = res.0; - res.1 - } - }, - '9' => match next!() { - 'f' => fixed(Fixed::Nanosecond9), - c => { - let res = self.error(original, &mut error_len, Some(c)); - remainder = res.0; - res.1 - } - }, - 'f' => fixed(Fixed::Nanosecond), - c => { - let res = self.error(original, &mut error_len, Some(c)); - remainder = res.0; - res.1 - } - }, - '3' => match next!() { - 'f' => internal_fixed(Nanosecond3NoDot), - c => { - let res = self.error(original, &mut error_len, Some(c)); - remainder = res.0; - res.1 - } - }, - '6' => match next!() { - 'f' => internal_fixed(Nanosecond6NoDot), - c => { - let res = self.error(original, &mut error_len, Some(c)); - remainder = res.0; - res.1 - } - }, - '9' => match next!() { - 'f' => internal_fixed(Nanosecond9NoDot), - c => { - let res = self.error(original, &mut error_len, Some(c)); - remainder = res.0; - res.1 - } - }, - '%' => Literal("%"), - c => { - let res = self.error(original, &mut error_len, Some(c)); - remainder = res.0; - res.1 - } - }; - - // Adjust `item` if we have any padding modifier. - // Not allowed on non-numeric items or on specifiers composed out of multiple - // formatting items. - if let Some(new_pad) = pad_override { - match item { - Item::Numeric(ref kind, _pad) if self.queue.is_empty() => { - Some((remainder, Item::Numeric(kind.clone(), new_pad))) - } - _ => Some(self.error(original, &mut error_len, None)), - } - } else { - Some((remainder, item)) - } - } + '%' => (remainder, &remainder[1..]), // the next item is space - Some(c) if c.is_whitespace() => { + c if c.is_whitespace() => { // `%` is not a whitespace, so `c != '%'` is redundant let nextspec = remainder.find(|c: char| !c.is_whitespace()).unwrap_or(remainder.len()); assert!(nextspec > 0); let item = Space(&remainder[..nextspec]); remainder = &remainder[nextspec..]; - Some((remainder, item)) + return Some((remainder, item)); } // the next item is literal @@ -756,8 +453,198 @@ impl<'a> StrftimeItems<'a> { assert!(nextspec > 0); let item = Literal(&remainder[..nextspec]); remainder = &remainder[nextspec..]; - Some((remainder, item)) + return Some((remainder, item)); } + }; + + macro_rules! next { + () => { + match remainder.chars().next() { + Some(x) => { + remainder = &remainder[x.len_utf8()..]; + x + } + None => return Some((remainder, self.error(original, remainder))), // premature end of string + } + }; + } + + let spec = next!(); + let pad_override = match spec { + '-' => Some(Pad::None), + '0' => Some(Pad::Zero), + '_' => Some(Pad::Space), + _ => None, + }; + + let is_alternate = spec == '#'; + let spec = if pad_override.is_some() || is_alternate { next!() } else { spec }; + if is_alternate && !HAVE_ALTERNATES.contains(spec) { + return Some((remainder, self.error(original, remainder))); + } + + macro_rules! queue { + [$head:expr, $($tail:expr),+ $(,)*] => ({ + const QUEUE: &'static [Item<'static>] = &[$($tail),+]; + self.queue = QUEUE; + $head + }) + } + + #[cfg(not(feature = "unstable-locales"))] + macro_rules! queue_from_slice { + ($slice:expr) => {{ + self.queue = &$slice[1..]; + $slice[0].clone() + }}; + } + + let item = match spec { + 'A' => fixed(Fixed::LongWeekdayName), + 'B' => fixed(Fixed::LongMonthName), + 'C' => num0(YearDiv100), + 'D' => { + queue![num0(Month), Literal("/"), num0(Day), Literal("/"), num0(YearMod100)] + } + 'F' => queue![num0(Year), Literal("-"), num0(Month), Literal("-"), num0(Day)], + 'G' => num0(IsoYear), + 'H' => num0(Hour), + 'I' => num0(Hour12), + 'M' => num0(Minute), + 'P' => fixed(Fixed::LowerAmPm), + 'R' => queue![num0(Hour), Literal(":"), num0(Minute)], + 'S' => num0(Second), + 'T' => { + queue![num0(Hour), Literal(":"), num0(Minute), Literal(":"), num0(Second)] + } + 'U' => num0(WeekFromSun), + 'V' => num0(IsoWeek), + 'W' => num0(WeekFromMon), + #[cfg(not(feature = "unstable-locales"))] + 'X' => queue_from_slice!(T_FMT), + #[cfg(feature = "unstable-locales")] + 'X' => self.switch_to_locale_str(locales::t_fmt, T_FMT), + 'Y' => num0(Year), + 'Z' => fixed(Fixed::TimezoneName), + 'a' => fixed(Fixed::ShortWeekdayName), + 'b' | 'h' => fixed(Fixed::ShortMonthName), + #[cfg(not(feature = "unstable-locales"))] + 'c' => queue_from_slice!(D_T_FMT), + #[cfg(feature = "unstable-locales")] + 'c' => self.switch_to_locale_str(locales::d_t_fmt, D_T_FMT), + 'd' => num0(Day), + 'e' => nums(Day), + 'f' => num0(Nanosecond), + 'g' => num0(IsoYearMod100), + 'j' => num0(Ordinal), + 'k' => nums(Hour), + 'l' => nums(Hour12), + 'm' => num0(Month), + 'n' => Space("\n"), + 'p' => fixed(Fixed::UpperAmPm), + 'q' => num(Quarter), + #[cfg(not(feature = "unstable-locales"))] + 'r' => queue_from_slice!(T_FMT_AMPM), + #[cfg(feature = "unstable-locales")] + 'r' => { + if self.locale.is_some() && locales::t_fmt_ampm(self.locale.unwrap()).is_empty() { + // 12-hour clock not supported by this locale. Switch to 24-hour format. + self.switch_to_locale_str(locales::t_fmt, T_FMT) + } else { + self.switch_to_locale_str(locales::t_fmt_ampm, T_FMT_AMPM) + } + } + 's' => num(Timestamp), + 't' => Space("\t"), + 'u' => num(WeekdayFromMon), + 'v' => { + queue![ + nums(Day), + Literal("-"), + fixed(Fixed::ShortMonthName), + Literal("-"), + num0(Year) + ] + } + 'w' => num(NumDaysFromSun), + #[cfg(not(feature = "unstable-locales"))] + 'x' => queue_from_slice!(D_FMT), + #[cfg(feature = "unstable-locales")] + 'x' => self.switch_to_locale_str(locales::d_fmt, D_FMT), + 'y' => num0(YearMod100), + 'z' => { + if is_alternate { + internal_fixed(TimezoneOffsetPermissive) + } else { + fixed(Fixed::TimezoneOffset) + } + } + '+' => fixed(Fixed::RFC3339), + ':' => { + if remainder.starts_with("::z") { + remainder = &remainder[3..]; + fixed(Fixed::TimezoneOffsetTripleColon) + } else if remainder.starts_with(":z") { + remainder = &remainder[2..]; + fixed(Fixed::TimezoneOffsetDoubleColon) + } else if remainder.starts_with('z') { + remainder = &remainder[1..]; + fixed(Fixed::TimezoneOffsetColon) + } else { + self.error(original, remainder) + } + } + '.' => match next!() { + '3' => match next!() { + 'f' => fixed(Fixed::Nanosecond3), + _ => self.error(original, remainder), + }, + '6' => match next!() { + 'f' => fixed(Fixed::Nanosecond6), + _ => self.error(original, remainder), + }, + '9' => match next!() { + 'f' => fixed(Fixed::Nanosecond9), + _ => self.error(original, remainder), + }, + 'f' => fixed(Fixed::Nanosecond), + _ => self.error(original, remainder), + }, + '3' => match next!() { + 'f' => internal_fixed(Nanosecond3NoDot), + _ => self.error(original, remainder), + }, + '6' => match next!() { + 'f' => internal_fixed(Nanosecond6NoDot), + _ => self.error(original, remainder), + }, + '9' => match next!() { + 'f' => internal_fixed(Nanosecond9NoDot), + _ => self.error(original, remainder), + }, + '%' => Literal("%"), + _ => self.error(original, remainder), + }; + + // Adjust `item` if we have any padding modifier. + // Not allowed on non-numeric items or on specifiers composed out of multiple + // formatting items. + if let Some(new_pad) = pad_override { + match item { + Item::Numeric(ref kind, _pad) if self.queue.is_empty() => { + Some((remainder, Item::Numeric(kind.clone(), new_pad))) + } + _ => Some((remainder, self.error(original, remainder))), + } + } else { + Some((remainder, item)) + } + } + + fn error<'b>(&mut self, original: &'b str, remainder: &'b str) -> Item<'b> { + match self.lenient { + false => Item::Error, + true => Item::Literal(&original[..original.len() - remainder.len()]), } } @@ -779,6 +666,72 @@ impl<'a> StrftimeItems<'a> { } } +impl<'a> Iterator for StrftimeItems<'a> { + type Item = Item<'a>; + + fn next(&mut self) -> Option<Item<'a>> { + // We have items queued to return from a specifier composed of multiple formatting items. + if let Some((item, remainder)) = self.queue.split_first() { + self.queue = remainder; + return Some(item.clone()); + } + + // We are in the middle of parsing the localized formatting string of a specifier. + #[cfg(feature = "unstable-locales")] + if !self.locale_str.is_empty() { + let (remainder, item) = self.parse_next_item(self.locale_str)?; + self.locale_str = remainder; + return Some(item); + } + + // Normal: we are parsing the formatting string. + let (remainder, item) = self.parse_next_item(self.remainder)?; + self.remainder = remainder; + Some(item) + } +} + +static D_FMT: &[Item<'static>] = &[ + num0(Numeric::Month), + Item::Literal("/"), + num0(Numeric::Day), + Item::Literal("/"), + num0(Numeric::YearMod100), +]; +static D_T_FMT: &[Item<'static>] = &[ + fixed(Fixed::ShortWeekdayName), + Item::Space(" "), + fixed(Fixed::ShortMonthName), + Item::Space(" "), + nums(Numeric::Day), + Item::Space(" "), + num0(Numeric::Hour), + Item::Literal(":"), + num0(Numeric::Minute), + Item::Literal(":"), + num0(Numeric::Second), + Item::Space(" "), + num0(Numeric::Year), +]; +static T_FMT: &[Item<'static>] = &[ + num0(Numeric::Hour), + Item::Literal(":"), + num0(Numeric::Minute), + Item::Literal(":"), + num0(Numeric::Second), +]; +static T_FMT_AMPM: &[Item<'static>] = &[ + num0(Numeric::Hour12), + Item::Literal(":"), + num0(Numeric::Minute), + Item::Literal(":"), + num0(Numeric::Second), + Item::Space(" "), + fixed(Fixed::UpperAmPm), +]; + +const HAVE_ALTERNATES: &str = "z"; + #[cfg(test)] mod tests { use super::StrftimeItems; @@ -794,7 +747,7 @@ mod tests { fn test_strftime_items() { fn parse_and_collect(s: &str) -> Vec<Item<'_>> { // map any error into `[Item::Error]`. useful for easy testing. - eprintln!("test_strftime_items: parse_and_collect({:?})", s); + eprintln!("test_strftime_items: parse_and_collect({s:?})"); let items = StrftimeItems::new(s); let items = items.map(|spec| if spec == Item::Error { None } else { Some(spec) }); items.collect::<Option<Vec<_>>>().unwrap_or_else(|| vec![Item::Error]) @@ -1246,4 +1199,18 @@ mod tests { "2014-05-07T12:34:56+0000%Q%.2f%%" ); } + + /// Regression test for https://github.com/chronotope/chrono/issues/1725 + #[test] + #[cfg(any(feature = "alloc", feature = "std"))] + fn test_finite() { + let mut i = 0; + for item in StrftimeItems::new("%2f") { + println!("{:?}", item); + i += 1; + if i > 10 { + panic!("infinite loop"); + } + } + } } diff --git a/third_party/rust/chrono/src/lib.rs b/third_party/rust/chrono/src/lib.rs @@ -380,7 +380,7 @@ //! use chrono::{DateTime, Utc}; //! //! // Construct a datetime from epoch: -//! let dt: DateTime<Utc> = DateTime::from_timestamp(1_500_000_000, 0).unwrap(); +//! let dt: DateTime<Utc> = DateTime::from_timestamp_secs(1_500_000_000).unwrap(); //! assert_eq!(dt.to_rfc2822(), "Fri, 14 Jul 2017 02:40:00 +0000"); //! //! // Get epoch value from a datetime: @@ -512,8 +512,8 @@ extern crate alloc; mod time_delta; -#[cfg(feature = "std")] #[doc(no_inline)] +#[cfg(any(feature = "std", feature = "core-error"))] pub use time_delta::OutOfRangeError; pub use time_delta::TimeDelta; @@ -634,7 +634,7 @@ pub mod serde { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { SerdeError::InvalidTimestamp(ts) => { - write!(f, "value is not a legal timestamp: {}", ts) + write!(f, "value is not a legal timestamp: {ts}") } } } @@ -690,6 +690,9 @@ impl fmt::Debug for OutOfRange { #[cfg(feature = "std")] impl std::error::Error for OutOfRange {} +#[cfg(all(not(feature = "std"), feature = "core-error"))] +impl core::error::Error for OutOfRange {} + /// Workaround because `?` is not (yet) available in const context. #[macro_export] #[doc(hidden)] diff --git a/third_party/rust/chrono/src/month.rs b/third_party/rust/chrono/src/month.rs @@ -272,6 +272,9 @@ pub struct ParseMonthError { #[cfg(feature = "std")] impl std::error::Error for ParseMonthError {} +#[cfg(all(not(feature = "std"), feature = "core-error"))] +impl core::error::Error for ParseMonthError {} + impl fmt::Display for ParseMonthError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "ParseMonthError {{ .. }}") diff --git a/third_party/rust/chrono/src/naive/date/mod.rs b/third_party/rust/chrono/src/naive/date/mod.rs @@ -27,6 +27,8 @@ use rkyv::{Archive, Deserialize, Serialize}; #[cfg(all(feature = "unstable-locales", feature = "alloc"))] use pure_rust_locales::Locale; +use super::internals::{Mdf, YearFlags}; +use crate::datetime::UNIX_EPOCH_DAY; #[cfg(feature = "alloc")] use crate::format::DelayedFormat; use crate::format::{ @@ -38,8 +40,6 @@ use crate::naive::{Days, IsoWeek, NaiveDateTime, NaiveTime, NaiveWeek}; use crate::{Datelike, TimeDelta, Weekday}; use crate::{expect, try_opt}; -use super::internals::{Mdf, YearFlags}; - #[cfg(test)] mod tests; @@ -384,6 +384,35 @@ impl NaiveDate { NaiveDate::from_ordinal_and_flags(year_div_400 * 400 + year_mod_400 as i32, ordinal, flags) } + /// Makes a new `NaiveDate` from a day's number in the proleptic Gregorian calendar, with + /// January 1, 1970 being day 0. + /// + /// # Errors + /// + /// Returns `None` if the date is out of range. + /// + /// # Example + /// + /// ``` + /// use chrono::NaiveDate; + /// + /// let from_ndays_opt = NaiveDate::from_epoch_days; + /// let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); + /// + /// assert_eq!(from_ndays_opt(-719_162), Some(from_ymd(1, 1, 1))); + /// assert_eq!(from_ndays_opt(1), Some(from_ymd(1970, 1, 2))); + /// assert_eq!(from_ndays_opt(0), Some(from_ymd(1970, 1, 1))); + /// assert_eq!(from_ndays_opt(-1), Some(from_ymd(1969, 12, 31))); + /// assert_eq!(from_ndays_opt(13036), Some(from_ymd(2005, 9, 10))); + /// assert_eq!(from_ndays_opt(100_000_000), None); + /// assert_eq!(from_ndays_opt(-100_000_000), None); + /// ``` + #[must_use] + pub const fn from_epoch_days(days: i32) -> Option<NaiveDate> { + let ce_days = try_opt!(days.checked_add(UNIX_EPOCH_DAY as i32)); + NaiveDate::from_num_days_from_ce_opt(ce_days) + } + /// Makes a new `NaiveDate` by counting the number of occurrences of a particular day-of-week /// since the beginning of the given month. For instance, if you want the 2nd Friday of March /// 2017, you would use `NaiveDate::from_weekday_of_month(2017, 3, Weekday::Fri, 2)`. @@ -755,7 +784,7 @@ impl NaiveDate { /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and millisecond. /// - /// The millisecond part is allowed to exceed 1,000,000,000 in order to represent a [leap second]( + /// The millisecond part is allowed to exceed 1,000 in order to represent a [leap second]( /// ./struct.NaiveTime.html#leap-second-handling), but only when `sec == 59`. /// /// # Panics @@ -770,7 +799,7 @@ impl NaiveDate { /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and millisecond. /// - /// The millisecond part is allowed to exceed 1,000,000,000 in order to represent a [leap second]( + /// The millisecond part is allowed to exceed 1,000 in order to represent a [leap second]( /// ./struct.NaiveTime.html#leap-second-handling), but only when `sec == 59`. /// /// # Errors @@ -805,7 +834,7 @@ impl NaiveDate { /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and microsecond. /// - /// The microsecond part is allowed to exceed 1,000,000,000 in order to represent a [leap second]( + /// The microsecond part is allowed to exceed 1,000,000 in order to represent a [leap second]( /// ./struct.NaiveTime.html#leap-second-handling), but only when `sec == 59`. /// /// # Panics @@ -1407,6 +1436,23 @@ impl NaiveDate { ndays + self.ordinal() as i32 } + /// Counts the days in the proleptic Gregorian calendar, with January 1, Year 1970 as day 0. + /// + /// # Example + /// + /// ``` + /// use chrono::NaiveDate; + /// + /// let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); + /// + /// assert_eq!(from_ymd(1, 1, 1).to_epoch_days(), -719162); + /// assert_eq!(from_ymd(1970, 1, 1).to_epoch_days(), 0); + /// assert_eq!(from_ymd(2005, 9, 10).to_epoch_days(), 13036); + /// ``` + pub const fn to_epoch_days(&self) -> i32 { + self.num_days_from_ce() - UNIX_EPOCH_DAY as i32 + } + /// Create a new `NaiveDate` from a raw year-ordinal-flags `i32`. /// /// In a valid value an ordinal is never `0`, and neither are the year flags. This method @@ -2228,7 +2274,7 @@ impl fmt::Debug for NaiveDate { write_hundreds(f, (year % 100) as u8)?; } else { // ISO 8601 requires the explicit sign for out-of-range years - write!(f, "{:+05}", year)?; + write!(f, "{year:+05}")?; } f.write_char('-')?; diff --git a/third_party/rust/chrono/src/naive/date/tests.rs b/third_party/rust/chrono/src/naive/date/tests.rs @@ -26,8 +26,7 @@ fn test_date_bounds() { let maxsecs = maxsecs + 86401; // also take care of DateTime assert!( maxsecs < (1 << MAX_BITS), - "The entire `NaiveDate` range somehow exceeds 2^{} seconds", - MAX_BITS + "The entire `NaiveDate` range somehow exceeds 2^{MAX_BITS} seconds" ); const BEFORE_MIN: NaiveDate = NaiveDate::BEFORE_MIN; @@ -303,6 +302,39 @@ fn test_date_from_num_days_from_ce() { } #[test] +fn test_date_from_epoch_days() { + let from_epoch_days = NaiveDate::from_epoch_days; + assert_eq!(from_epoch_days(-719_162), Some(NaiveDate::from_ymd_opt(1, 1, 1).unwrap())); + assert_eq!(from_epoch_days(0), Some(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap())); + assert_eq!(from_epoch_days(1), Some(NaiveDate::from_ymd_opt(1970, 1, 2).unwrap())); + assert_eq!(from_epoch_days(2), Some(NaiveDate::from_ymd_opt(1970, 1, 3).unwrap())); + assert_eq!(from_epoch_days(30), Some(NaiveDate::from_ymd_opt(1970, 1, 31).unwrap())); + assert_eq!(from_epoch_days(31), Some(NaiveDate::from_ymd_opt(1970, 2, 1).unwrap())); + assert_eq!(from_epoch_days(58), Some(NaiveDate::from_ymd_opt(1970, 2, 28).unwrap())); + assert_eq!(from_epoch_days(59), Some(NaiveDate::from_ymd_opt(1970, 3, 1).unwrap())); + assert_eq!(from_epoch_days(364), Some(NaiveDate::from_ymd_opt(1970, 12, 31).unwrap())); + assert_eq!(from_epoch_days(365), Some(NaiveDate::from_ymd_opt(1971, 1, 1).unwrap())); + assert_eq!(from_epoch_days(365 * 2), Some(NaiveDate::from_ymd_opt(1972, 1, 1).unwrap())); + assert_eq!(from_epoch_days(365 * 3 + 1), Some(NaiveDate::from_ymd_opt(1973, 1, 1).unwrap())); + assert_eq!(from_epoch_days(365 * 4 + 1), Some(NaiveDate::from_ymd_opt(1974, 1, 1).unwrap())); + assert_eq!(from_epoch_days(13036), Some(NaiveDate::from_ymd_opt(2005, 9, 10).unwrap())); + assert_eq!(from_epoch_days(-365), Some(NaiveDate::from_ymd_opt(1969, 1, 1).unwrap())); + assert_eq!(from_epoch_days(-366), Some(NaiveDate::from_ymd_opt(1968, 12, 31).unwrap())); + + for days in (-9999..10001).map(|x| x * 100) { + assert_eq!(from_epoch_days(days).map(|d| d.to_epoch_days()), Some(days)); + } + + assert_eq!(from_epoch_days(NaiveDate::MIN.to_epoch_days()), Some(NaiveDate::MIN)); + assert_eq!(from_epoch_days(NaiveDate::MIN.to_epoch_days() - 1), None); + assert_eq!(from_epoch_days(NaiveDate::MAX.to_epoch_days()), Some(NaiveDate::MAX)); + assert_eq!(from_epoch_days(NaiveDate::MAX.to_epoch_days() + 1), None); + + assert_eq!(from_epoch_days(i32::MIN), None); + assert_eq!(from_epoch_days(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); @@ -425,6 +457,18 @@ fn test_date_num_days_from_ce() { } #[test] +fn test_date_to_epoch_days() { + assert_eq!(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().to_epoch_days(), 0); + + for year in -9999..10001 { + assert_eq!( + NaiveDate::from_ymd_opt(year, 1, 1).unwrap().to_epoch_days(), + NaiveDate::from_ymd_opt(year - 1, 12, 31).unwrap().to_epoch_days() + 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))); @@ -607,29 +651,26 @@ fn test_date_from_str() { "+00007-2-18", ]; for &s in &valid { - eprintln!("test_date_from_str valid {:?}", s); + eprintln!("test_date_from_str valid {s:?}"); let d = match s.parse::<NaiveDate>() { Ok(d) => d, - Err(e) => panic!("parsing `{}` has failed: {}", s, e), + Err(e) => panic!("parsing `{s}` has failed: {e}"), }; - eprintln!("d {:?} (NaiveDate)", d); - let s_ = format!("{:?}", d); - eprintln!("s_ {:?}", s_); + eprintln!("d {d:?} (NaiveDate)"); + 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) + panic!("`{s}` is parsed into `{d:?}`, but reparsing that has failed: {e}") } }; - eprintln!("d_ {:?} (NaiveDate)", d_); + eprintln!("d_ {d_:?} (NaiveDate)"); assert!( d == d_, - "`{}` is parsed into `{:?}`, but reparsed result \ - `{:?}` does not match", - s, - d, - d_ + "`{s}` is parsed into `{d:?}`, but reparsed result \ + `{d_:?}` does not match" ); } @@ -653,7 +694,7 @@ fn test_date_from_str() { "9999999-9-9", // invalid year (out of bounds) ]; for &s in &invalid { - eprintln!("test_date_from_str invalid {:?}", s); + eprintln!("test_date_from_str invalid {s:?}"); assert!(s.parse::<NaiveDate>().is_err()); } } diff --git a/third_party/rust/chrono/src/naive/datetime/serde.rs b/third_party/rust/chrono/src/naive/datetime/serde.rs @@ -955,7 +955,7 @@ pub mod ts_seconds { /// } /// /// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?; - /// let expected = DateTime::from_timestamp(1431684000, 0).unwrap().naive_utc(); + /// let expected = DateTime::from_timestamp_secs(1431684000).unwrap().naive_utc(); /// assert_eq!(my_s, S { time: expected }); /// # Ok::<(), serde_json::Error>(()) /// ``` @@ -979,7 +979,7 @@ pub mod ts_seconds { where E: de::Error, { - DateTime::from_timestamp(value, 0) + DateTime::from_timestamp_secs(value) .map(|dt| dt.naive_utc()) .ok_or_else(|| invalid_ts(value)) } @@ -991,7 +991,7 @@ pub mod ts_seconds { if value > i64::MAX as u64 { Err(invalid_ts(value)) } else { - DateTime::from_timestamp(value as i64, 0) + DateTime::from_timestamp_secs(value as i64) .map(|dt| dt.naive_utc()) .ok_or_else(|| invalid_ts(value)) } @@ -1080,7 +1080,7 @@ pub mod ts_seconds_option { /// } /// /// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?; - /// let expected = DateTime::from_timestamp(1431684000, 0).unwrap().naive_utc(); + /// let expected = DateTime::from_timestamp_secs(1431684000).unwrap().naive_utc(); /// assert_eq!(my_s, S { time: Some(expected) }); /// # Ok::<(), serde_json::Error>(()) /// ``` diff --git a/third_party/rust/chrono/src/naive/datetime/tests.rs b/third_party/rust/chrono/src/naive/datetime/tests.rs @@ -125,26 +125,23 @@ fn test_datetime_from_str() { " +82701 - 05 - 6 T 15 : 9 : 60.898989898989 ", ]; for &s in &valid { - eprintln!("test_parse_naivedatetime valid {:?}", s); + eprintln!("test_parse_naivedatetime valid {s:?}"); let d = match s.parse::<NaiveDateTime>() { Ok(d) => d, - Err(e) => panic!("parsing `{}` has failed: {}", s, e), + Err(e) => panic!("parsing `{s}` has failed: {e}"), }; - let s_ = format!("{:?}", d); + 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) + panic!("`{s}` is parsed into `{d:?}`, but reparsing that has failed: {e}") } }; assert!( d == d_, - "`{}` is parsed into `{:?}`, but reparsed result \ - `{:?}` does not match", - s, - d, - d_ + "`{s}` is parsed into `{d:?}`, but reparsed result \ + `{d_:?}` does not match" ); } @@ -175,7 +172,7 @@ fn test_datetime_from_str() { "+802701-123-12T12:12:12", // out-of-bound year, invalid month ]; for &s in &invalid { - eprintln!("test_datetime_from_str invalid {:?}", s); + eprintln!("test_datetime_from_str invalid {s:?}"); assert!(s.parse::<NaiveDateTime>().is_err()); } } diff --git a/third_party/rust/chrono/src/naive/internals.rs b/third_party/rust/chrono/src/naive/internals.rs @@ -123,7 +123,7 @@ impl fmt::Debug for YearFlags { 0o07 => "FE".fmt(f), 0o16 => "G".fmt(f), 0o06 => "GF".fmt(f), - _ => write!(f, "YearFlags({})", flags), + _ => write!(f, "YearFlags({flags})"), } } } @@ -430,7 +430,7 @@ mod tests { let mdf = match Mdf::new(month, day, flags) { Some(mdf) => mdf, None if !expected => continue, - None => panic!("Mdf::new({}, {}, {:?}) returned None", month, day, flags), + None => panic!("Mdf::new({month}, {day}, {flags:?}) returned None"), }; assert!( @@ -541,7 +541,7 @@ mod tests { let mdf = match mdf.with_month(month) { Some(mdf) => mdf, None if month > 12 => continue, - None => panic!("failed to create Mdf with month {}", month), + None => panic!("failed to create Mdf with month {month}"), }; if mdf.valid() { @@ -554,7 +554,7 @@ mod tests { let mdf = match mdf.with_day(day) { Some(mdf) => mdf, None if day > 31 => continue, - None => panic!("failed to create Mdf with month {}", month), + None => panic!("failed to create Mdf with month {month}"), }; if mdf.valid() { diff --git a/third_party/rust/chrono/src/naive/isoweek.rs b/third_party/rust/chrono/src/naive/isoweek.rs @@ -152,10 +152,10 @@ impl fmt::Debug for IsoWeek { let year = self.year(); let week = self.week(); if (0..=9999).contains(&year) { - write!(f, "{:04}-W{:02}", year, week) + write!(f, "{year:04}-W{week:02}") } else { // ISO 8601 requires the explicit sign for out-of-range years - write!(f, "{:+05}-W{:02}", year, week) + write!(f, "{year:+05}-W{week:02}") } } } @@ -176,13 +176,13 @@ mod tests { assert_eq!(minweek.week(), 1); assert_eq!(minweek.week0(), 0); #[cfg(feature = "alloc")] - assert_eq!(format!("{:?}", minweek), NaiveDate::MIN.format("%G-W%V").to_string()); + assert_eq!(format!("{minweek:?}"), NaiveDate::MIN.format("%G-W%V").to_string()); assert_eq!(maxweek.year(), date::MAX_YEAR + 1); assert_eq!(maxweek.week(), 1); assert_eq!(maxweek.week0(), 0); #[cfg(feature = "alloc")] - assert_eq!(format!("{:?}", maxweek), NaiveDate::MAX.format("%G-W%V").to_string()); + assert_eq!(format!("{maxweek:?}"), NaiveDate::MAX.format("%G-W%V").to_string()); } #[test] diff --git a/third_party/rust/chrono/src/naive/time/mod.rs b/third_party/rust/chrono/src/naive/time/mod.rs @@ -1524,7 +1524,7 @@ impl fmt::Debug for NaiveTime { } else if nano % 1_000 == 0 { write!(f, ".{:06}", nano / 1_000) } else { - write!(f, ".{:09}", nano) + write!(f, ".{nano:09}") } } } diff --git a/third_party/rust/chrono/src/naive/time/tests.rs b/third_party/rust/chrono/src/naive/time/tests.rs @@ -283,26 +283,23 @@ fn test_time_from_str() { "23:59:60.373929310237", ]; for &s in &valid { - eprintln!("test_time_parse_from_str valid {:?}", s); + 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), + Err(e) => panic!("parsing `{s}` has failed: {e}"), }; - let s_ = format!("{:?}", d); + 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) + panic!("`{s}` is parsed into `{d:?}`, but reparsing that has failed: {e}") } }; assert!( d == d_, - "`{}` is parsed into `{:?}`, but reparsed result \ - `{:?}` does not match", - s, - d, - d_ + "`{s}` is parsed into `{d:?}`, but reparsed result \ + `{d_:?}` does not match" ); } @@ -329,7 +326,7 @@ fn test_time_from_str() { "09:08:00000000007", // invalid second / invalid fraction format ]; for &s in &invalid { - eprintln!("test_time_parse_from_str invalid {:?}", s); + eprintln!("test_time_parse_from_str invalid {s:?}"); assert!(s.parse::<NaiveTime>().is_err()); } } diff --git a/third_party/rust/chrono/src/offset/fixed.rs b/third_party/rust/chrono/src/offset/fixed.rs @@ -161,9 +161,9 @@ impl fmt::Debug for FixedOffset { let min = mins.rem_euclid(60); let hour = mins.div_euclid(60); if sec == 0 { - write!(f, "{}{:02}:{:02}", sign, hour, min) + write!(f, "{sign}{hour:02}:{min:02}") } else { - write!(f, "{}{:02}:{:02}:{:02}", sign, hour, min, sec) + write!(f, "{sign}{hour:02}:{min:02}:{sec:02}") } } } diff --git a/third_party/rust/chrono/src/offset/local/mod.rs b/third_party/rust/chrono/src/offset/local/mod.rs @@ -28,6 +28,9 @@ mod inner; #[allow(unreachable_pub)] mod win_bindings; +#[cfg(all(any(target_os = "android", target_env = "ohos", test), feature = "clock"))] +mod tz_data; + #[cfg(all( not(unix), not(windows), @@ -56,7 +59,7 @@ mod inner { #[cfg(all( target_arch = "wasm32", feature = "wasmbind", - not(any(target_os = "emscripten", target_os = "wasi")) + not(any(target_os = "emscripten", target_os = "wasi", target_os = "linux")) ))] mod inner { use crate::{Datelike, FixedOffset, MappedLocalTime, NaiveDateTime, Timelike}; @@ -216,7 +219,7 @@ impl Transition { #[cfg(windows)] impl PartialOrd for Transition { fn partial_cmp(&self, other: &Self) -> Option<Ordering> { - Some(self.transition_utc.cmp(&other.transition_utc)) + Some(self.cmp(other)) } } @@ -340,8 +343,7 @@ mod tests { // but there are only two sensible options. assert!( timestr == "15:02:60" || timestr == "15:03:00", - "unexpected timestr {:?}", - timestr + "unexpected timestr {timestr:?}" ); } @@ -349,8 +351,7 @@ mod tests { let timestr = dt.time().to_string(); assert!( timestr == "15:02:03.234" || timestr == "15:02:04.234", - "unexpected timestr {:?}", - timestr + "unexpected timestr {timestr:?}" ); } } diff --git a/third_party/rust/chrono/src/offset/local/tz_data.rs b/third_party/rust/chrono/src/offset/local/tz_data.rs @@ -0,0 +1,267 @@ +//! Rust parser of ZoneInfoDb(`tzdata`) on Android and OpenHarmony +//! +//! Ported from: https://android.googlesource.com/platform/prebuilts/fullsdk/sources/+/refs/heads/androidx-appcompat-release/android-34/com/android/i18n/timezone/ZoneInfoDb.java +use std::{ + ffi::CStr, + fmt::Debug, + fs::File, + io::{Error, ErrorKind, Read, Result, Seek, SeekFrom}, +}; + +/// Get timezone data from the `tzdata` file of HarmonyOS NEXT. +#[cfg(target_env = "ohos")] +pub(crate) fn for_zone(tz_string: &str) -> Result<Option<Vec<u8>>> { + let mut file = File::open("/system/etc/zoneinfo/tzdata")?; + find_tz_data::<OHOS_ENTRY_LEN>(&mut file, tz_string.as_bytes()) +} + +/// Get timezone data from the `tzdata` file of Android. +#[cfg(target_os = "android")] +pub(crate) fn for_zone(tz_string: &str) -> Result<Option<Vec<u8>>> { + let mut file = open_android_tz_data_file()?; + find_tz_data::<ANDROID_ENTRY_LEN>(&mut file, tz_string.as_bytes()) +} + +/// Open the `tzdata` file of Android from the environment variables. +#[cfg(target_os = "android")] +fn open_android_tz_data_file() -> Result<File> { + for (env_var, path) in + [("ANDROID_DATA", "/misc/zoneinfo"), ("ANDROID_ROOT", "/usr/share/zoneinfo")] + { + if let Ok(env_value) = std::env::var(env_var) { + if let Ok(file) = File::open(format!("{}{}/tzdata", env_value, path)) { + return Ok(file); + } + } + } + Err(Error::from(ErrorKind::NotFound)) +} + +/// Get timezone data from the `tzdata` file reader +#[cfg(any(test, target_env = "ohos", target_os = "android"))] +fn find_tz_data<const ENTRY_LEN: usize>( + mut reader: impl Read + Seek, + tz_name: &[u8], +) -> Result<Option<Vec<u8>>> { + let header = TzDataHeader::new(&mut reader)?; + let index = TzDataIndexes::new::<ENTRY_LEN>(&mut reader, &header)?; + Ok(if let Some(entry) = index.find_timezone(tz_name) { + Some(index.find_tzdata(reader, &header, entry)?) + } else { + None + }) +} + +/// Header of the `tzdata` file. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct TzDataHeader { + version: [u8; 5], + index_offset: u32, + data_offset: u32, + zonetab_offset: u32, +} + +impl TzDataHeader { + /// Parse the header of the `tzdata` file. + fn new(mut data: impl Read) -> Result<Self> { + let version = { + let mut magic = [0; TZDATA_VERSION_LEN]; + data.read_exact(&mut magic)?; + if !magic.starts_with(b"tzdata") || magic[TZDATA_VERSION_LEN - 1] != 0 { + return Err(Error::new(ErrorKind::Other, "invalid tzdata header magic")); + } + let mut version = [0; 5]; + version.copy_from_slice(&magic[6..11]); + version + }; + + let mut offset = [0; 4]; + data.read_exact(&mut offset)?; + let index_offset = u32::from_be_bytes(offset); + data.read_exact(&mut offset)?; + let data_offset = u32::from_be_bytes(offset); + data.read_exact(&mut offset)?; + let zonetab_offset = u32::from_be_bytes(offset); + + Ok(Self { version, index_offset, data_offset, zonetab_offset }) + } +} + +/// Indexes of the `tzdata` file. +struct TzDataIndexes { + indexes: Vec<TzDataIndex>, +} + +impl TzDataIndexes { + /// Create a new `TzDataIndexes` from the `tzdata` file reader. + fn new<const ENTRY_LEN: usize>(mut reader: impl Read, header: &TzDataHeader) -> Result<Self> { + let mut buf = vec![0; header.data_offset.saturating_sub(header.index_offset) as usize]; + reader.read_exact(&mut buf)?; + // replace chunks with array_chunks when it's stable + Ok(TzDataIndexes { + indexes: buf + .chunks(ENTRY_LEN) + .filter_map(|chunk| { + from_bytes_until_nul(&chunk[..TZ_NAME_LEN]).map(|name| { + let name = name.to_bytes().to_vec().into_boxed_slice(); + let offset = u32::from_be_bytes( + chunk[TZ_NAME_LEN..TZ_NAME_LEN + 4].try_into().unwrap(), + ); + let length = u32::from_be_bytes( + chunk[TZ_NAME_LEN + 4..TZ_NAME_LEN + 8].try_into().unwrap(), + ); + TzDataIndex { name, offset, length } + }) + }) + .collect(), + }) + } + + /// Find a timezone by name. + fn find_timezone(&self, timezone: &[u8]) -> Option<&TzDataIndex> { + // timezones in tzdata are sorted by name. + self.indexes.binary_search_by_key(&timezone, |x| &x.name).map(|x| &self.indexes[x]).ok() + } + + /// Retrieve a chunk of timezone data by the index. + fn find_tzdata( + &self, + mut reader: impl Read + Seek, + header: &TzDataHeader, + index: &TzDataIndex, + ) -> Result<Vec<u8>> { + reader.seek(SeekFrom::Start(index.offset as u64 + header.data_offset as u64))?; + let mut buffer = vec![0; index.length as usize]; + reader.read_exact(&mut buffer)?; + Ok(buffer) + } +} + +/// Index entry of the `tzdata` file. +struct TzDataIndex { + name: Box<[u8]>, + offset: u32, + length: u32, +} + +/// TODO: Change this `CStr::from_bytes_until_nul` once MSRV was bumped above 1.72.0 +fn from_bytes_until_nul(bytes: &[u8]) -> Option<&CStr> { + let nul_pos = bytes.iter().position(|&b| b == 0)?; + // SAFETY: + // 1. nul_pos + 1 <= bytes.len() + // 2. We know there is a nul byte at nul_pos, so this slice (ending at the nul byte) is a well-formed C string. + Some(unsafe { CStr::from_bytes_with_nul_unchecked(&bytes[..=nul_pos]) }) +} + +/// Ohos tzdata index entry size: `name + offset + length` +#[cfg(any(test, target_env = "ohos"))] +const OHOS_ENTRY_LEN: usize = TZ_NAME_LEN + 2 * size_of::<u32>(); +/// Android tzdata index entry size: `name + offset + length + raw_utc_offset(legacy)`: +/// [reference](https://android.googlesource.com/platform/prebuilts/fullsdk/sources/+/refs/heads/androidx-appcompat-release/android-34/com/android/i18n/timezone/ZoneInfoDb.java#271) +#[cfg(any(test, target_os = "android"))] +const ANDROID_ENTRY_LEN: usize = TZ_NAME_LEN + 3 * size_of::<u32>(); +/// The database reserves 40 bytes for each id. +const TZ_NAME_LEN: usize = 40; +/// Size of the version string in the header of `tzdata` file. +/// e.g. `tzdata2024b\0` +const TZDATA_VERSION_LEN: usize = 12; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ohos_tzdata_header_and_index() { + let file = File::open("./tests/ohos/tzdata").unwrap(); + let header = TzDataHeader::new(&file).unwrap(); + assert_eq!(header.version, *b"2024a"); + assert_eq!(header.index_offset, 24); + assert_eq!(header.data_offset, 21240); + assert_eq!(header.zonetab_offset, 272428); + + let iter = TzDataIndexes::new::<OHOS_ENTRY_LEN>(&file, &header).unwrap(); + assert_eq!(iter.indexes.len(), 442); + assert!(iter.find_timezone(b"Asia/Shanghai").is_some()); + assert!(iter.find_timezone(b"Pacific/Noumea").is_some()); + } + + #[test] + fn test_ohos_tzdata_loading() { + let file = File::open("./tests/ohos/tzdata").unwrap(); + let header = TzDataHeader::new(&file).unwrap(); + let iter = TzDataIndexes::new::<OHOS_ENTRY_LEN>(&file, &header).unwrap(); + let timezone = iter.find_timezone(b"Asia/Shanghai").unwrap(); + let tzdata = iter.find_tzdata(&file, &header, timezone).unwrap(); + assert_eq!(tzdata.len(), 393); + } + + #[test] + fn test_invalid_tzdata_header() { + TzDataHeader::new(&b"tzdaaa2024aaaaaaaaaaaaaaa\0"[..]).unwrap_err(); + } + + #[test] + fn test_android_tzdata_header_and_index() { + let file = File::open("./tests/android/tzdata").unwrap(); + let header = TzDataHeader::new(&file).unwrap(); + assert_eq!(header.version, *b"2021a"); + assert_eq!(header.index_offset, 24); + assert_eq!(header.data_offset, 30860); + assert_eq!(header.zonetab_offset, 491837); + + let iter = TzDataIndexes::new::<ANDROID_ENTRY_LEN>(&file, &header).unwrap(); + assert_eq!(iter.indexes.len(), 593); + assert!(iter.find_timezone(b"Asia/Shanghai").is_some()); + assert!(iter.find_timezone(b"Pacific/Noumea").is_some()); + } + + #[test] + fn test_android_tzdata_loading() { + let file = File::open("./tests/android/tzdata").unwrap(); + let header = TzDataHeader::new(&file).unwrap(); + let iter = TzDataIndexes::new::<ANDROID_ENTRY_LEN>(&file, &header).unwrap(); + let timezone = iter.find_timezone(b"Asia/Shanghai").unwrap(); + let tzdata = iter.find_tzdata(&file, &header, timezone).unwrap(); + assert_eq!(tzdata.len(), 573); + } + + #[test] + fn test_ohos_tzdata_find() { + let file = File::open("./tests/ohos/tzdata").unwrap(); + let tzdata = find_tz_data::<OHOS_ENTRY_LEN>(file, b"Asia/Shanghai").unwrap().unwrap(); + assert_eq!(tzdata.len(), 393); + } + + #[test] + fn test_ohos_tzdata_find_missing() { + let file = File::open("./tests/ohos/tzdata").unwrap(); + assert!(find_tz_data::<OHOS_ENTRY_LEN>(file, b"Asia/Sjasdfai").unwrap().is_none()); + } + + #[test] + fn test_android_tzdata_find() { + let file = File::open("./tests/android/tzdata").unwrap(); + let tzdata = find_tz_data::<ANDROID_ENTRY_LEN>(file, b"Asia/Shanghai").unwrap().unwrap(); + assert_eq!(tzdata.len(), 573); + } + + #[test] + fn test_android_tzdata_find_missing() { + let file = File::open("./tests/android/tzdata").unwrap(); + assert!(find_tz_data::<ANDROID_ENTRY_LEN>(file, b"Asia/S000000i").unwrap().is_none()); + } + + #[cfg(target_env = "ohos")] + #[test] + fn test_ohos_machine_tz_data_loading() { + let tzdata = for_zone(b"Asia/Shanghai").unwrap().unwrap(); + assert!(!tzdata.is_empty()); + } + + #[cfg(target_os = "android")] + #[test] + fn test_android_machine_tz_data_loading() { + let tzdata = for_zone(b"Asia/Shanghai").unwrap().unwrap(); + assert!(!tzdata.is_empty()); + } +} diff --git a/third_party/rust/chrono/src/offset/local/tz_info/mod.rs b/third_party/rust/chrono/src/offset/local/tz_info/mod.rs @@ -54,21 +54,21 @@ 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), + DateTime(error) => write!(f, "invalid date time: {error}"), FindLocalTimeType(error) => error.fmt(f), - LocalTimeType(error) => write!(f, "invalid local time type: {}", error), + LocalTimeType(error) => write!(f, "invalid local time type: {error}"), InvalidSlice(error) => error.fmt(f), - InvalidTzString(error) => write!(f, "invalid TZ string: {}", error), + 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), + 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), + UnsupportedTzString(error) => write!(f, "unsupported TZ string: {error}"), Utf8(error) => error.fmt(f), } } diff --git a/third_party/rust/chrono/src/offset/local/tz_info/timezone.rs b/third_party/rust/chrono/src/offset/local/tz_info/timezone.rs @@ -8,8 +8,6 @@ use std::{cmp::Ordering, fmt, str}; use super::rule::{AlternateTime, TransitionRule}; use super::{DAYS_PER_WEEK, Error, SECONDS_PER_DAY, parser}; use crate::NaiveDateTime; -#[cfg(target_env = "ohos")] -use crate::offset::local::tz_info::parser::Cursor; /// Time zone #[derive(Debug, Clone, Eq, PartialEq)] @@ -47,19 +45,13 @@ impl TimeZone { } // attributes are not allowed on if blocks in Rust 1.38 - #[cfg(target_os = "android")] + #[cfg(any(target_os = "android", target_env = "ohos"))] { - if let Ok(bytes) = android_tzdata::find_tz_data(tz_string) { + if let Ok(Some(bytes)) = crate::offset::local::tz_data::for_zone(tz_string) { return Self::from_tz_data(&bytes); } } - // ohos merge all file into tzdata since ver35 - #[cfg(target_env = "ohos")] - { - return Self::from_tz_data(&find_ohos_tz_data(tz_string)?); - } - let mut chars = tz_string.chars(); if chars.next() == Some(':') { return Self::from_file(&mut find_tz_file(chars.as_str())?); @@ -142,7 +134,7 @@ impl TimeZone { } /// Returns a reference to the time zone - fn as_ref(&self) -> TimeZoneRef { + fn as_ref(&self) -> TimeZoneRef<'_> { TimeZoneRef { transitions: &self.transitions, local_time_types: &self.local_time_types, @@ -636,58 +628,6 @@ fn find_tz_file(path: impl AsRef<Path>) -> Result<File, Error> { } } -#[cfg(target_env = "ohos")] -fn from_tzdata_bytes(bytes: &mut Vec<u8>, tz_string: &str) -> Result<Vec<u8>, Error> { - const VERSION_SIZE: usize = 12; - const OFFSET_SIZE: usize = 4; - const INDEX_CHUNK_SIZE: usize = 48; - const ZONENAME_SIZE: usize = 40; - - let mut cursor = Cursor::new(&bytes); - // version head - let _ = cursor.read_exact(VERSION_SIZE)?; - let index_offset_offset = cursor.read_be_u32()?; - let data_offset_offset = cursor.read_be_u32()?; - // final offset - let _ = cursor.read_be_u32()?; - - cursor.seek_after(index_offset_offset as usize)?; - let mut idx = index_offset_offset; - while idx < data_offset_offset { - let index_buf = cursor.read_exact(ZONENAME_SIZE)?; - let offset = cursor.read_be_u32()?; - let length = cursor.read_be_u32()?; - let zone_name = str::from_utf8(index_buf)?.trim_end_matches('\0'); - if zone_name != tz_string { - idx += INDEX_CHUNK_SIZE as u32; - continue; - } - cursor.seek_after((data_offset_offset + offset) as usize)?; - return match cursor.read_exact(length as usize) { - Ok(result) => Ok(result.to_vec()), - Err(_err) => Err(Error::InvalidTzFile("invalid ohos tzdata chunk")), - }; - } - - Err(Error::InvalidTzString("cannot find tz string within tzdata")) -} - -#[cfg(target_env = "ohos")] -fn from_tzdata_file(file: &mut File, tz_string: &str) -> Result<Vec<u8>, Error> { - let mut bytes = Vec::new(); - file.read_to_end(&mut bytes)?; - from_tzdata_bytes(&mut bytes, tz_string) -} - -#[cfg(target_env = "ohos")] -fn find_ohos_tz_data(tz_string: &str) -> Result<Vec<u8>, Error> { - const TZDATA_PATH: &str = "/system/etc/zoneinfo/tzdata"; - match File::open(TZDATA_PATH) { - Ok(mut file) => from_tzdata_file(&mut file, tz_string), - Err(err) => Err(err.into()), - } -} - // Possible system timezone directories #[cfg(unix)] const ZONE_INFO_DIRECTORIES: [&str; 4] = diff --git a/third_party/rust/chrono/src/offset/local/unix.rs b/third_party/rust/chrono/src/offset/local/unix.rs @@ -74,15 +74,15 @@ struct Cache { #[cfg(target_os = "aix")] const TZDB_LOCATION: &str = "/usr/share/lib/zoneinfo"; -#[cfg(not(any(target_os = "android", target_os = "aix")))] +#[cfg(not(any(target_os = "android", target_os = "aix", target_env = "ohos")))] 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()?; + #[cfg(not(any(target_os = "android", target_env = "ohos")))] + let bytes = fs::read(format!("{TZDB_LOCATION}/{tz_name}")).ok()?; + #[cfg(any(target_os = "android", target_env = "ohos"))] + let bytes = crate::offset::local::tz_data::for_zone(&tz_name).ok()??; TimeZone::from_tz_data(&bytes).ok() } diff --git a/third_party/rust/chrono/src/offset/mod.rs b/third_party/rust/chrono/src/offset/mod.rs @@ -46,6 +46,26 @@ pub use self::utc::Utc; /// DST. /// * No result when the clock is turned forwards during a transition due to for example DST. /// +/// <div class="warning"> +/// +/// In wasm, when using [`Local`], only the [`LocalResult::Single`] variant is returned. +/// Specifically: +/// +/// * When the clock is turned backwards, where `Ambiguous(earliest, latest)` would be expected, +/// `Single(earliest)` is returned instead. +/// * When the clock is turned forwards, where `None` would be expected, `Single(t)` is returned, +/// with `t` being the requested local time represented as though there is no transition on that +/// day (i.e. still "summer time") +/// +/// This is caused because of limitations in the JavaScript +/// [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) +/// API, which always parses a local time as a single, valid time - even for an +/// input which describes a nonexistent or ambiguous time. +/// +/// See further discussion and workarounds in <https://github.com/chronotope/chrono/issues/1701>. +/// +/// </div> +/// /// 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. @@ -261,7 +281,7 @@ impl<T: fmt::Debug> MappedLocalTime<T> { MappedLocalTime::None => panic!("No such local time"), MappedLocalTime::Single(t) => t, MappedLocalTime::Ambiguous(t1, t2) => { - panic!("Ambiguous local time, ranging from {:?} to {:?}", t1, t2) + panic!("Ambiguous local time, ranging from {t1:?} to {t2:?}") } } } @@ -653,7 +673,7 @@ mod tests { MappedLocalTime::Single(dt) => { assert_eq!(dt.to_string(), *expected); } - e => panic!("Got {:?} instead of an okay answer", e), + e => panic!("Got {e:?} instead of an okay answer"), } } } diff --git a/third_party/rust/chrono/src/offset/utc.rs b/third_party/rust/chrono/src/offset/utc.rs @@ -9,7 +9,7 @@ use core::fmt; not(all( target_arch = "wasm32", feature = "wasmbind", - not(any(target_os = "emscripten", target_os = "wasi")) + not(any(target_os = "emscripten", target_os = "wasi", target_os = "linux")) )) ))] use std::time::{SystemTime, UNIX_EPOCH}; @@ -89,7 +89,7 @@ impl Utc { #[cfg(not(all( target_arch = "wasm32", feature = "wasmbind", - not(any(target_os = "emscripten", target_os = "wasi")) + not(any(target_os = "emscripten", target_os = "wasi", target_os = "linux")) )))] #[must_use] pub fn now() -> DateTime<Utc> { @@ -102,7 +102,7 @@ impl Utc { #[cfg(all( target_arch = "wasm32", feature = "wasmbind", - not(any(target_os = "emscripten", target_os = "wasi")) + not(any(target_os = "emscripten", target_os = "wasi", target_os = "linux")) ))] #[must_use] pub fn now() -> DateTime<Utc> { diff --git a/third_party/rust/chrono/src/round.rs b/third_party/rust/chrono/src/round.rs @@ -109,7 +109,11 @@ pub trait DurationRound: Sized { type Err: std::error::Error; /// Error that can occur in rounding or truncating - #[cfg(not(feature = "std"))] + #[cfg(all(not(feature = "std"), feature = "core-error"))] + type Err: core::error::Error; + + /// Error that can occur in rounding or truncating + #[cfg(all(not(feature = "std"), not(feature = "core-error")))] type Err: fmt::Debug + fmt::Display; /// Return a copy rounded by TimeDelta. @@ -362,6 +366,14 @@ impl std::error::Error for RoundingError { } } +#[cfg(all(not(feature = "std"), feature = "core-error"))] +impl core::error::Error for RoundingError { + #[allow(deprecated)] + fn description(&self) -> &str { + "error from rounding or truncating with DurationRound" + } +} + #[cfg(test)] mod tests { use super::{DurationRound, RoundingError, SubsecRound, TimeDelta}; diff --git a/third_party/rust/chrono/src/time_delta.rs b/third_party/rust/chrono/src/time_delta.rs @@ -10,6 +10,8 @@ //! Temporal quantification +#[cfg(all(not(feature = "std"), feature = "core-error"))] +use core::error::Error; use core::fmt; use core::ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign}; use core::time::Duration; @@ -587,7 +589,7 @@ impl fmt::Display for TimeDelta { // but we need to print it anyway. let (abs, sign) = if self.secs < 0 { (-*self, "-") } else { (*self, "") }; - write!(f, "{}P", sign)?; + write!(f, "{sign}P")?; // Plenty of ways to encode an empty string. `P0D` is short and not too strange. if abs.secs == 0 && abs.nanos == 0 { return f.write_str("0D"); @@ -608,7 +610,7 @@ impl fmt::Display for TimeDelta { fraction_digits = div; figures -= 1; } - f.write_fmt(format_args!(".{:01$}", fraction_digits, figures))?; + f.write_fmt(format_args!(".{fraction_digits:0figures$}"))?; } f.write_str("S")?; Ok(()) @@ -630,7 +632,7 @@ impl fmt::Display for OutOfRangeError { } } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", feature = "core-error"))] impl Error for OutOfRangeError { #[allow(deprecated)] fn description(&self) -> &str { diff --git a/third_party/rust/chrono/src/traits.rs b/third_party/rust/chrono/src/traits.rs @@ -366,7 +366,7 @@ mod tests { /// /// Panics if `div` is not positive. fn in_between(start: i32, end: i32, div: i32) -> i32 { - assert!(div > 0, "in_between: nonpositive div = {}", div); + 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. @@ -390,16 +390,10 @@ mod tests { assert_eq!( jan1_year.num_days_from_ce(), num_days_from_ce(&jan1_year), - "on {:?}", - 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 - ); + assert_eq!(mid_year.num_days_from_ce(), num_days_from_ce(&mid_year), "on {mid_year:?}"); } } diff --git a/third_party/rust/chrono/src/weekday.rs b/third_party/rust/chrono/src/weekday.rs @@ -238,12 +238,15 @@ pub struct ParseWeekdayError { pub(crate) _dummy: (), } +#[cfg(all(not(feature = "std"), feature = "core-error"))] +impl core::error::Error for ParseWeekdayError {} + #[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)) + f.write_fmt(format_args!("{self:?}")) } } diff --git a/third_party/rust/chrono/tests/dateutils.rs b/third_party/rust/chrono/tests/dateutils.rs @@ -37,11 +37,11 @@ fn verify_against_date_command_local(path: &'static str, dt: NaiveDateTime) { 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::Ambiguous(a, b) => { + assert!(format!("{a}\n") == date_command_str || format!("{b}\n") == date_command_str) + } chrono::MappedLocalTime::Single(a) => { - assert_eq!(format!("{}\n", a), date_command_str); + assert_eq!(format!("{a}\n"), date_command_str); } chrono::MappedLocalTime::None => { assert_eq!("", date_command_str); @@ -66,29 +66,26 @@ const DATE_PATH: &str = "/opt/freeware/bin/date"; fn assert_run_date_version() { // note environment variable `LANG` match std::env::var_os("LANG") { - Some(lang) => eprintln!("LANG: {:?}", 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); + eprintln!("command: {DATE_PATH:?} --version\nstdout: {stdout:?}\nstderr: {stderr:?}"); + assert!(out.status.success(), "command failed: {DATE_PATH:?} --version"); } #[test] fn try_verify_against_date_command() { if !path::Path::new(DATE_PATH).exists() { - eprintln!("date command {:?} not found, skipping", DATE_PATH); + eprintln!("date command {DATE_PATH:?} not found, skipping"); return; } assert_run_date_version(); - eprintln!( - "Run command {:?} for every hour from 1975 to 2077, skipping some years...", - DATE_PATH, - ); + eprintln!("Run command {DATE_PATH:?} for every hour from 1975 to 2077, skipping some years...",); let mut children = vec![]; for year in [1975, 1976, 1977, 2020, 2021, 2022, 2073, 2074, 2075, 2076, 2077].iter() { @@ -135,7 +132,7 @@ fn verify_against_date_command_format_local(path: &'static str, dt: NaiveDateTim dt.minute(), dt.second() )) - .arg(format!("+{}", required_format)) + .arg(format!("+{required_format}")) .output() .unwrap(); @@ -152,7 +149,7 @@ fn verify_against_date_command_format_local(path: &'static str, dt: NaiveDateTim #[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); + eprintln!("date command {DATE_PATH:?} not found, skipping"); return; } assert_run_date_version(); diff --git a/third_party/rust/chrono/tests/win_bindings.rs b/third_party/rust/chrono/tests/win_bindings.rs @@ -9,7 +9,7 @@ fn gen_bindings() { let output = "src/offset/local/win_bindings.rs"; let existing = fs::read_to_string(output).unwrap(); - bindgen(["--no-deps", "--etc", input]); + bindgen(["--no-deps", "--etc", input]).unwrap(); // 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 @@ -23,6 +23,6 @@ fn gen_bindings() { similar_asserts::assert_eq!(existing, new); if !new.lines().eq(existing.lines()) { - panic!("generated file `{}` is changed.", output); + panic!("generated file `{output}` is changed."); } } diff --git a/third_party/rust/windows-link/.cargo-checksum.json b/third_party/rust/windows-link/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.lock":"45d4fcedd34058d064b8c2cff7d288d9463d548eb9d10357f6124eab8b5aea06","Cargo.toml":"bff03cd2cb573427f5db58e0553ef8291fba08c9dda06b4a3bff8d0488fe4526","license-apache-2.0":"c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b","license-mit":"c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383","readme.md":"1d119f8a35c7c95c4cc80f0609c3fb4a897d4bd244e98bdf5054f9fd96942418","src/lib.rs":"af49fec4ea3b0f96b7fd6002b4b4365b2f4c3608ae5adde502f9cd335064906f"},"package":"76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"} -\ No newline at end of file +{"files":{"Cargo.lock":"f33a3dccb85342cd5cb58d165dc6c0421e93aeaca9ea1cd82b81f0c204d316a8","Cargo.toml":"abf0b74b168ec7d7c600f44eb90502c47e44480a199b9adc9ec74ea990605707","license-apache-2.0":"c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b","license-mit":"c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383","readme.md":"4bbe7714285567006b5b068dfc93cb3b633afae20766c9bf1fce2444874261fb","src/lib.rs":"ca9cf5a2a97cf72d855c677c936355b6d29e41682e5abfa505f28b3d216b5333"},"package":"f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"} +\ No newline at end of file diff --git a/third_party/rust/windows-link/Cargo.lock b/third_party/rust/windows-link/Cargo.lock @@ -4,4 +4,4 @@ version = 3 [[package]] name = "windows-link" -version = "0.1.1" +version = "0.2.1" diff --git a/third_party/rust/windows-link/Cargo.toml b/third_party/rust/windows-link/Cargo.toml @@ -13,8 +13,7 @@ edition = "2021" rust-version = "1.71" name = "windows-link" -version = "0.1.1" -authors = ["Microsoft"] +version = "0.2.1" build = false autolib = false autobins = false @@ -23,6 +22,7 @@ autotests = false autobenches = false description = "Linking for Windows" readme = "readme.md" +categories = ["os::windows-apis"] license = "MIT OR Apache-2.0" repository = "https://github.com/microsoft/windows-rs" @@ -31,14 +31,9 @@ name = "windows_link" path = "src/lib.rs" [lints.rust] -missing_docs = "warn" -unsafe_op_in_unsafe_fn = "warn" - -[lints.rust.rust_2018_idioms] -level = "warn" -priority = -1 +missing_unsafe_on_extern = "warn" [lints.rust.unexpected_cfgs] level = "warn" priority = 0 -check-cfg = ["cfg(windows_raw_dylib, windows_debugger_visualizer, windows_slim_errors)"] +check-cfg = ["cfg(windows_raw_dylib, windows_slim_errors)"] diff --git a/third_party/rust/windows-link/readme.md b/third_party/rust/windows-link/readme.md @@ -10,7 +10,7 @@ Start by adding the following to your Cargo.toml file: ```toml [dependencies.windows-link] -version = "0.1" +version = "0.2" ``` Use the `link` macro to define the external functions you wish to call: diff --git a/third_party/rust/windows-link/src/lib.rs b/third_party/rust/windows-link/src/lib.rs @@ -20,7 +20,7 @@ macro_rules! link { macro_rules! link { ($library:literal $abi:literal $($link_name:literal)? fn $($function:tt)*) => ( #[link(name = $library, kind = "raw-dylib", modifiers = "+verbatim")] - extern "C" { + extern $abi { $(#[link_name=$link_name])? pub fn $($function)*; }