omegafox/patches/backport-rust-crates.bootstrap

11586 lines
443 KiB
Text

# Rust crate backport by daijro
# Fixes https://bugzilla.mozilla.org/show_bug.cgi?id=1900504
# Requirement: ./mach vendor rust
diff --git a/Cargo.lock b/Cargo.lock
index 7242103ece..d52ae0cacd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -823,7 +823,7 @@ version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
dependencies = [
- "time 0.3.23",
+ "time 0.3.36",
"version_check",
]
@@ -1264,6 +1264,16 @@ dependencies = [
"uuid",
]
+[[package]]
+name = "deranged"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+dependencies = [
+ "powerfmt",
+ "serde",
+]
+
[[package]]
name = "derive_arbitrary"
version = "1.3.1"
@@ -3757,7 +3767,7 @@ dependencies = [
"serde_json",
"smallvec",
"syn",
- "time 0.3.23",
+ "time 0.3.36",
"tokio",
"tokio-util",
"tracing",
@@ -3887,7 +3897,7 @@ dependencies = [
"env_logger",
"log",
"qlog",
- "time 0.3.23",
+ "time 0.3.36",
"winapi",
]
@@ -4077,6 +4087,12 @@ dependencies = [
"nsstring",
]
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
[[package]]
name = "num-derive"
version = "0.4.0"
@@ -4450,10 +4466,16 @@ dependencies = [
"indexmap 1.9.3",
"line-wrap",
"serde",
- "time 0.3.23",
+ "time 0.3.36",
"xml-rs",
]
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
[[package]]
name = "ppv-lite86"
version = "0.2.17"
@@ -5629,11 +5651,14 @@ dependencies = [
[[package]]
name = "time"
-version = "0.3.23"
+version = "0.3.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446"
+checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [
+ "deranged",
"itoa",
+ "num-conv",
+ "powerfmt",
"serde",
"time-core",
"time-macros",
@@ -5641,16 +5666,17 @@ dependencies = [
[[package]]
name = "time-core"
-version = "0.1.1"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "time-macros"
-version = "0.2.10"
+version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4"
+checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
dependencies = [
+ "num-conv",
"time-core",
]
@@ -6327,7 +6353,7 @@ dependencies = [
"serde_derive",
"serde_json",
"thiserror",
- "time 0.3.23",
+ "time 0.3.36",
"tokio",
"tokio-stream",
"url",
diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml
index 31ca3fcf0f..03c38e4fe0 100644
--- a/supply-chain/audits.toml
+++ b/supply-chain/audits.toml
@@ -1388,6 +1388,16 @@ criteria = "safe-to-deploy"
version = "0.8.0"
notes = "This crates was written by Sentry and I've fully audited it as Firefox crash reporting machinery relies on it."
+[[audits.deranged]]
+who = "Alex Franchuk <afranchuk@mozilla.com>"
+criteria = "safe-to-deploy"
+version = "0.3.11"
+notes = """
+This crate contains a decent bit of `unsafe` code, however all internal
+unsafety is verified with copious assertions (many are compile-time), and
+otherwise the unsafety is documented and left to the caller to verify.
+"""
+
[[audits.derive_arbitrary]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-run"
@@ -2746,6 +2756,24 @@ criteria = "safe-to-deploy"
version = "0.4.2"
notes = "All code written or reviewed by Josh Stone."
+[[audits.num-conv]]
+who = "Alex Franchuk <afranchuk@mozilla.com>"
+criteria = "safe-to-deploy"
+version = "0.1.0"
+notes = """
+Very straightforward, simple crate. No dependencies, unsafe, extern,
+side-effectful std functions, etc.
+"""
+
+[[audits.powerfmt]]
+who = "Alex Franchuk <afranchuk@mozilla.com>"
+criteria = "safe-to-deploy"
+version = "0.2.0"
+notes = """
+A tiny bit of unsafe code to implement functionality that isn't in stable rust
+yet, but it's all valid. Otherwise it's a pretty simple crate.
+"""
+
[[audits.num-derive]]
who = "Josh Stone <jistone@redhat.com>"
criteria = "safe-to-deploy"
@@ -3804,6 +3832,16 @@ who = "Kershaw Chang <kershaw@mozilla.com>"
criteria = "safe-to-deploy"
delta = "0.3.17 -> 0.3.23"
+[[audits.time]]
+who = "Alex Franchuk <afranchuk@mozilla.com>"
+criteria = "safe-to-deploy"
+delta = "0.3.23 -> 0.3.36"
+notes = """
+There's a bit of new unsafe code that is self-imposed because they now assert
+that ordinals are non-zero. All unsafe code was checked to ensure that the
+invariants claimed were true.
+"""
+
[[audits.time-core]]
who = "Kershaw Chang <kershaw@mozilla.com>"
criteria = "safe-to-deploy"
@@ -3819,6 +3857,11 @@ who = "Kershaw Chang <kershaw@mozilla.com>"
criteria = "safe-to-deploy"
delta = "0.1.0 -> 0.1.1"
+[[audits.time-core]]
+who = "Alex Franchuk <afranchuk@mozilla.com>"
+criteria = "safe-to-deploy"
+delta = "0.1.1 -> 0.1.2"
+
[[audits.time-macros]]
who = "Kershaw Chang <kershaw@mozilla.com>"
criteria = "safe-to-deploy"
@@ -3834,6 +3877,11 @@ who = "Kershaw Chang <kershaw@mozilla.com>"
criteria = "safe-to-deploy"
delta = "0.2.6 -> 0.2.10"
+[[audits.time-macros]]
+who = "Alex Franchuk <afranchuk@mozilla.com>"
+criteria = "safe-to-deploy"
+delta = "0.2.10 -> 0.2.18"
+
[[audits.tinystr]]
who = "Zibi Braniecki <zibi@unicode.org>"
criteria = "safe-to-deploy"
diff --git a/third_party/rust/time-core/.cargo-checksum.json b/third_party/rust/time-core/.cargo-checksum.json
index 239ee76621..32d6e08d1f 100644
--- a/third_party/rust/time-core/.cargo-checksum.json
+++ b/third_party/rust/time-core/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"032c780eaf4ddfde703d5a6b260ad7bad35a5a1ee57a33cacf503f5e47dff6a9","LICENSE-Apache":"b8929fea28678da67251fb2daf9438f67503814211051861612441806d8edb05","LICENSE-MIT":"04620bf27e4a643dd47bf27652320c205acdb776c1f9f24bb8c3bfaba10804c5","src/convert.rs":"59566933f2977d62abbfe39b20be16a85df00db8627211471ccfe182dbbe684c","src/lib.rs":"18020c914b1cd561465e624ef3ea3eef980bd82bc93847e2543bce12da28b043","src/util.rs":"52c1fbf68b71c3582caf0d9a8255378c6c14a737e2df8d7e6d6603b0eb12ca06"},"package":"7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"}
\ No newline at end of file
+{"files":{"Cargo.toml":"d90c41d20f37fc3dbc3d88f7715cacafb5aea973030f498e9b2833decdbe63f0","LICENSE-Apache":"b8929fea28678da67251fb2daf9438f67503814211051861612441806d8edb05","LICENSE-MIT":"04620bf27e4a643dd47bf27652320c205acdb776c1f9f24bb8c3bfaba10804c5","src/convert.rs":"354a1b05e8bb1e92eda5dcdecf33dc6cf2ce72b11115ae4cb0909dcd51d2b294","src/lib.rs":"461b752a45b0f819284e8d8e6b2f49d52b3b661026ab84ee64bf04f4daa0a2d2","src/util.rs":"52c1fbf68b71c3582caf0d9a8255378c6c14a737e2df8d7e6d6603b0eb12ca06"},"package":"ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"}
\ No newline at end of file
diff --git a/third_party/rust/time-core/Cargo.toml b/third_party/rust/time-core/Cargo.toml
index bbc8a325e3..3d6555ebd5 100644
--- a/third_party/rust/time-core/Cargo.toml
+++ b/third_party/rust/time-core/Cargo.toml
@@ -11,9 +11,9 @@
[package]
edition = "2021"
-rust-version = "1.65.0"
+rust-version = "1.67.0"
name = "time-core"
-version = "0.1.1"
+version = "0.1.2"
authors = [
"Jacob Pratt <open-source@jhpratt.dev>",
"Time contributors",
@@ -29,4 +29,7 @@ categories = ["date-and-time"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/time-rs/time"
+[package.metadata.docs.rs]
+rustdoc-args = ["--generate-link-to-definition"]
+
[dependencies]
diff --git a/third_party/rust/time-core/src/convert.rs b/third_party/rust/time-core/src/convert.rs
index dae77e9afa..9c28f02638 100644
--- a/third_party/rust/time-core/src/convert.rs
+++ b/third_party/rust/time-core/src/convert.rs
@@ -1,88 +1,104 @@
-#![allow(clippy::missing_docs_in_private_items)] // TODO temporary
+//! Conversion between units of time.
-macro_rules! declare_structs {
- ($($t:ident)*) => {$(
- #[derive(Debug, Copy, Clone)]
+use self::sealed::Per;
+
+mod sealed {
+ /// A trait for defining the ratio of two units of time.
+ ///
+ /// This trait is used to implement the `per` method on the various structs.
+ pub trait Per<T> {
+ /// The smallest unsigned integer type that can represent [`VALUE`](Self::VALUE).
+ type Output;
+
+ /// The number of one unit of time in the other.
+ const VALUE: Self::Output;
+ }
+}
+
+/// Declare and implement `Per` for all relevant types. Identity implementations are automatic.
+macro_rules! impl_per {
+ ($($t:ident ($str:literal) per {$(
+ $larger:ident : $output:ty = $value:expr
+ )*})*) => {$(
+ #[doc = concat!("A unit of time representing exactly one ", $str, ".")]
+ #[derive(Debug, Clone, Copy)]
pub struct $t;
impl $t {
- pub const fn per<T>(self, _: T) -> <(Self, T) as Per>::Output
+ #[doc = concat!("Obtain the number of times `", stringify!($t), "` can fit into `T`.")]
+ #[doc = concat!("If `T` is smaller than `", stringify!($t), "`, the code will fail to")]
+ /// compile. The return type is the smallest unsigned integer type that can represent
+ /// the value.
+ ///
+ /// Valid calls:
+ ///
+ #[doc = concat!(" - `", stringify!($t), "::per(", stringify!($t), ")` (returns `u8`)")]
+ $(#[doc = concat!(" - `", stringify!($t), "::per(", stringify!($larger), ")` (returns `", stringify!($output), "`)")])*
+ pub const fn per<T>(_larger: T) -> <Self as Per<T>>::Output
where
- (Self, T): Per,
+ Self: Per<T>,
T: Copy,
{
- <(Self, T)>::VALUE
+ Self::VALUE
}
}
- )*};
-}
-
-declare_structs! {
- Nanosecond
- Microsecond
- Millisecond
- Second
- Minute
- Hour
- Day
- Week
-}
-
-mod sealed {
- pub trait Sealed {}
-}
-pub trait Per: sealed::Sealed {
- type Output;
+ impl Per<$t> for $t {
+ type Output = u8;
- const VALUE: Self::Output;
-}
-
-macro_rules! impl_per {
- ($($t:ty : $x:ident in $y:ident = $val:expr)*) => {$(
- impl sealed::Sealed for ($x, $y) {}
+ const VALUE: u8 = 1;
+ }
- impl Per for ($x, $y) {
- type Output = $t;
+ $(impl Per<$larger> for $t {
+ type Output = $output;
- const VALUE: $t = $val;
- }
+ const VALUE: $output = $value;
+ })*
)*};
}
impl_per! {
- u16: Nanosecond in Microsecond = 1_000
- u32: Nanosecond in Millisecond = 1_000_000
- u32: Nanosecond in Second = 1_000_000_000
- u64: Nanosecond in Minute = 60_000_000_000
- u64: Nanosecond in Hour = 3_600_000_000_000
- u64: Nanosecond in Day = 86_400_000_000_000
- u64: Nanosecond in Week = 604_800_000_000_000
-
- u16: Microsecond in Millisecond = 1_000
- u32: Microsecond in Second = 1_000_000
- u32: Microsecond in Minute = 60_000_000
- u32: Microsecond in Hour = 3_600_000_000
- u64: Microsecond in Day = 86_400_000_000
- u64: Microsecond in Week = 604_800_000_000
-
- u16: Millisecond in Second = 1_000
- u16: Millisecond in Minute = 60_000
- u32: Millisecond in Hour = 3_600_000
- u32: Millisecond in Day = 86_400_000
- u32: Millisecond in Week = 604_800_000
-
- u8: Second in Minute = 60
- u16: Second in Hour = 3_600
- u32: Second in Day = 86_400
- u32: Second in Week = 604_800
-
- u8: Minute in Hour = 60
- u16: Minute in Day = 1_440
- u16: Minute in Week = 10_080
-
- u8: Hour in Day = 24
- u8: Hour in Week = 168
-
- u8: Day in Week = 7
+ Nanosecond ("nanosecond") per {
+ Microsecond: u16 = 1_000
+ Millisecond: u32 = 1_000_000
+ Second: u32 = 1_000_000_000
+ Minute: u64 = 60_000_000_000
+ Hour: u64 = 3_600_000_000_000
+ Day: u64 = 86_400_000_000_000
+ Week: u64 = 604_800_000_000_000
+ }
+ Microsecond ("microsecond") per {
+ Millisecond: u16 = 1_000
+ Second: u32 = 1_000_000
+ Minute: u32 = 60_000_000
+ Hour: u32 = 3_600_000_000
+ Day: u64 = 86_400_000_000
+ Week: u64 = 604_800_000_000
+ }
+ Millisecond ("millisecond") per {
+ Second: u16 = 1_000
+ Minute: u16 = 60_000
+ Hour: u32 = 3_600_000
+ Day: u32 = 86_400_000
+ Week: u32 = 604_800_000
+ }
+ Second ("second") per {
+ Minute: u8 = 60
+ Hour: u16 = 3_600
+ Day: u32 = 86_400
+ Week: u32 = 604_800
+ }
+ Minute ("minute") per {
+ Hour: u8 = 60
+ Day: u16 = 1_440
+ Week: u16 = 10_080
+ }
+ Hour ("hour") per {
+ Day: u8 = 24
+ Week: u8 = 168
+ }
+ Day ("day") per {
+ Week: u8 = 7
+ }
+ Week ("week") per {}
}
diff --git a/third_party/rust/time-core/src/lib.rs b/third_party/rust/time-core/src/lib.rs
index 4f1c53b12e..41c3547869 100644
--- a/third_party/rust/time-core/src/lib.rs
+++ b/third_party/rust/time-core/src/lib.rs
@@ -3,47 +3,6 @@
//! This crate is an implementation detail of `time` and should not be relied upon directly.
#![no_std]
-#![deny(
- anonymous_parameters,
- clippy::all,
- clippy::alloc_instead_of_core,
- clippy::explicit_auto_deref,
- clippy::obfuscated_if_else,
- clippy::std_instead_of_core,
- clippy::undocumented_unsafe_blocks,
- illegal_floating_point_literal_pattern,
- late_bound_lifetime_arguments,
- path_statements,
- patterns_in_fns_without_body,
- rust_2018_idioms,
- trivial_casts,
- trivial_numeric_casts,
- unreachable_pub,
- unsafe_op_in_unsafe_fn,
- unused_extern_crates,
- rustdoc::broken_intra_doc_links,
- rustdoc::private_intra_doc_links
-)]
-#![warn(
- clippy::dbg_macro,
- clippy::decimal_literal_representation,
- clippy::get_unwrap,
- clippy::missing_docs_in_private_items,
- clippy::nursery,
- clippy::print_stdout,
- clippy::todo,
- clippy::unimplemented,
- clippy::unnested_or_patterns,
- clippy::unwrap_in_result,
- clippy::unwrap_used,
- clippy::use_debug,
- deprecated_in_future,
- missing_copy_implementations,
- missing_debug_implementations,
- unused_qualifications,
- variant_size_differences
-)]
-#![allow(clippy::redundant_pub_crate)]
#![doc(html_favicon_url = "https://avatars0.githubusercontent.com/u/55999857")]
#![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/55999857")]
#![doc(test(attr(deny(warnings))))]
diff --git a/third_party/rust/time-macros/.cargo-checksum.json b/third_party/rust/time-macros/.cargo-checksum.json
index 6f3847898a..ab81b00599 100644
--- a/third_party/rust/time-macros/.cargo-checksum.json
+++ b/third_party/rust/time-macros/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"97dbc36d7e8c8658e151c1cfe57397a116135a0d0efc97aacd339142da5d1c96","LICENSE-Apache":"b8929fea28678da67251fb2daf9438f67503814211051861612441806d8edb05","LICENSE-MIT":"04620bf27e4a643dd47bf27652320c205acdb776c1f9f24bb8c3bfaba10804c5","src/date.rs":"ffcd3d0998ec67abb43a3f8eccc6104172f5061b855312b89d53bb82fece2460","src/datetime.rs":"5c7f6e07dc2f0dcfcd86216664df53bc008dbc86f346df57a9ff57f52fe43bc6","src/error.rs":"b3dea92631092068dd73e57e1cbf548f7ae85762826dcdea7fd6454bf357a50a","src/format_description/ast.rs":"8ba87e3249766b89c42b040f623d3134aeec46b78208fdfee825ed0eeeb4591a","src/format_description/format_item.rs":"03ff10699383e5ad08fe690199d45288f13363337abbc811a70b03a8b1703ab1","src/format_description/lexer.rs":"e7db7b6431f00c81b8d15a162088a1622ecd65bfb58d4e642c3c93a8dd5ae4ad","src/format_description/mod.rs":"f48c0ff590bc74529f06a98f60a6af5814bc30d1456bf0b81ac334c0b3f41bba","src/format_description/public/component.rs":"e2c2c8a189e2eb9f9354ff1d9d8edeafa34303e91dc58457df373e7e61c38b78","src/format_description/public/mod.rs":"5260592b310ea9e30808d30c92ea94c7bf1bdb171250a1342279e927d2528d73","src/format_description/public/modifier.rs":"37661e1f7cd9fd11a82f5a1ce6d5971686afa91e6feebc7b9d32df297e8b667f","src/helpers/mod.rs":"a8f8ed59a72b239d7a530357d212873f2e75ea924ec19a6d5d6e24a2baa8100c","src/helpers/string.rs":"3af2d0c701ca978c705922a272e76506dbdf0f376d44ed9ae7283086c67852ba","src/lib.rs":"200678edc14d5920abc0516717b8e010667e58da8bdc65c1cb583fdde0353089","src/offset.rs":"4b9c001a954c1f121a572f5675073f7a4e46d00cc9eb77736bfea2df94ffd05b","src/quote.rs":"634a12b95236e4ab2b8ab70a1a4a2629113c3ce3cf6defefc7ffeb81544c1d89","src/serde_format_description.rs":"db5fb2dc94e01c5114cab3484e68334516d53c4642f31dae0d66f1183253a17c","src/time.rs":"d762e8f22f749d9546d5d2a78b8a0380510be27b4cd3fed375695d7982d8396e","src/to_tokens.rs":"6636ea489c7484bad9b39f72d6956a04c95ce82d8462b12079cc03db778fd263"},"package":"96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4"}
\ No newline at end of file
+{"files":{"Cargo.toml":"3330436e81a4de8b20b9a2931f9e857b7974a8423462d928b04cff55ad531cff","LICENSE-Apache":"edd65bdd88957a205c47d53fa499eed8865a70320f0f03f6391668cb304ea376","LICENSE-MIT":"231c837c45eb53f108fb48929e488965bc4fcc14e9ea21d35f50e6b99d98685b","src/date.rs":"be197c8a2ed37e8b3123a798a91697b0e61cf9b60e7b1898a0e1b458fe8e3ef1","src/datetime.rs":"5c7f6e07dc2f0dcfcd86216664df53bc008dbc86f346df57a9ff57f52fe43bc6","src/error.rs":"b3dea92631092068dd73e57e1cbf548f7ae85762826dcdea7fd6454bf357a50a","src/format_description/ast.rs":"697d5ce506b5386092d706bfe5bf4f81f50e1130796cb17c2fc61457fb165307","src/format_description/format_item.rs":"02d12976209c7af83c2aa4a3221a1a65420fae8c8ba12a28933fb738a2872ff9","src/format_description/lexer.rs":"e2c75f3dda5773a0c8301fdfc0d58a0b833923ba59ac04bcc49fd10aee20496c","src/format_description/mod.rs":"2109b77a8198769c6a6732a54233d7e0058bf4a6da724824103d107859795956","src/format_description/public/component.rs":"5d86912e247724957f7183d70745ced20a7408ed90c24bb47da73a0e26550899","src/format_description/public/mod.rs":"8030e767cb94d559dda2dddc72d42654a756362bd165e5c2cccf112f15d49610","src/format_description/public/modifier.rs":"e1d8fdababcaee2e181a7acb3a938baf309f5a0e2d3877585cf678fcc12f212a","src/helpers/mod.rs":"af47d6c053ffd1113788c5d7591d46fa7d879dc0c5cb2c6c02f9c05462499c4f","src/helpers/string.rs":"3af2d0c701ca978c705922a272e76506dbdf0f376d44ed9ae7283086c67852ba","src/lib.rs":"6ed2d4a41d15a1b5d9fef7d437a1520d967acbfbab98a88630062340f701ca54","src/offset.rs":"aed29d0da9fc65a7dc77314e0346dfdc6fdaf663f17adf9edf00933e8f8e605f","src/quote.rs":"d3003dafa3073825f188851a974846099681cc81145070affb033469cbc7bb31","src/serde_format_description.rs":"db5fb2dc94e01c5114cab3484e68334516d53c4642f31dae0d66f1183253a17c","src/time.rs":"299ddb54e44fb88e514592db5335f06352ebdd0dbf064752790657db85f4c13c","src/to_tokens.rs":"afb067f4d95d19c1b7a650cbcf60ae155b5a9619c89825867997f39ce163ac94"},"package":"3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"}
\ No newline at end of file
diff --git a/third_party/rust/time-macros/Cargo.toml b/third_party/rust/time-macros/Cargo.toml
index 1c86d657d5..b817622154 100644
--- a/third_party/rust/time-macros/Cargo.toml
+++ b/third_party/rust/time-macros/Cargo.toml
@@ -11,9 +11,9 @@
[package]
edition = "2021"
-rust-version = "1.65.0"
+rust-version = "1.67.0"
name = "time-macros"
-version = "0.2.10"
+version = "0.2.18"
authors = [
"Jacob Pratt <open-source@jhpratt.dev>",
"Time contributors",
@@ -32,14 +32,104 @@ categories = ["date-and-time"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/time-rs/time"
+[package.metadata.docs.rs]
+rustdoc-args = ["--generate-link-to-definition"]
+
[lib]
proc-macro = true
+[dependencies.num-conv]
+version = "0.1.0"
+
[dependencies.time-core]
-version = "=0.1.1"
+version = "=0.1.2"
[features]
formatting = []
large-dates = []
parsing = []
serde = []
+
+[lints.clippy]
+all = "warn"
+alloc-instead-of-core = "deny"
+dbg-macro = "warn"
+decimal-literal-representation = "warn"
+explicit-auto-deref = "warn"
+get-unwrap = "warn"
+manual-let-else = "warn"
+missing-docs-in-private-items = "warn"
+missing-enforced-import-renames = "warn"
+nursery = "warn"
+obfuscated-if-else = "warn"
+print-stdout = "warn"
+semicolon-outside-block = "warn"
+std-instead-of-core = "deny"
+todo = "warn"
+undocumented-unsafe-blocks = "deny"
+unimplemented = "warn"
+uninlined-format-args = "warn"
+unnested-or-patterns = "warn"
+unwrap-in-result = "warn"
+unwrap-used = "warn"
+use-debug = "warn"
+
+[lints.clippy.option-if-let-else]
+level = "allow"
+priority = 1
+
+[lints.clippy.redundant-pub-crate]
+level = "allow"
+priority = 1
+
+[lints.rust]
+ambiguous-glob-reexports = "deny"
+clashing-extern-declarations = "deny"
+const-item-mutation = "deny"
+deref-nullptr = "deny"
+drop-bounds = "deny"
+future-incompatible = "deny"
+hidden-glob-reexports = "deny"
+improper-ctypes = "deny"
+improper-ctypes-definitions = "deny"
+invalid-from-utf8 = "deny"
+invalid-macro-export-arguments = "deny"
+invalid-nan-comparisons = "deny"
+invalid-reference-casting = "deny"
+invalid-value = "deny"
+keyword-idents = "warn"
+let-underscore = "warn"
+macro-use-extern-crate = "warn"
+meta-variable-misuse = "warn"
+missing-abi = "warn"
+missing-copy-implementations = "warn"
+missing-debug-implementations = "warn"
+missing-docs = "warn"
+named-arguments-used-positionally = "deny"
+non-ascii-idents = "deny"
+noop-method-call = "warn"
+opaque-hidden-inferred-bound = "deny"
+overlapping-range-endpoints = "deny"
+single-use-lifetimes = "warn"
+suspicious-double-ref-op = "deny"
+temporary-cstring-as-ptr = "deny"
+trivial-casts = "warn"
+trivial-numeric-casts = "warn"
+unconditional-recursion = "deny"
+unnameable-test-items = "deny"
+unreachable-pub = "warn"
+unsafe-op-in-unsafe-fn = "deny"
+unstable-syntax-pre-expansion = "deny"
+unused = "warn"
+unused-import-braces = "warn"
+unused-lifetimes = "warn"
+unused-qualifications = "warn"
+variant-size-differences = "warn"
+
+[lints.rust.unstable-name-collisions]
+level = "warn"
+priority = 1
+
+[lints.rustdoc]
+private-doc-tests = "warn"
+unescaped-backticks = "warn"
diff --git a/third_party/rust/time-macros/LICENSE-Apache b/third_party/rust/time-macros/LICENSE-Apache
index 7646f21e37..c763a0c9de 100644
--- a/third_party/rust/time-macros/LICENSE-Apache
+++ b/third_party/rust/time-macros/LICENSE-Apache
@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright 2022 Jacob Pratt et al.
+ Copyright 2024 Jacob Pratt et al.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/third_party/rust/time-macros/LICENSE-MIT b/third_party/rust/time-macros/LICENSE-MIT
index a11a755732..5cc097f1c0 100644
--- a/third_party/rust/time-macros/LICENSE-MIT
+++ b/third_party/rust/time-macros/LICENSE-MIT
@@ -1,4 +1,4 @@
-Copyright (c) 2022 Jacob Pratt et al.
+Copyright (c) 2024 Jacob Pratt et al.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/third_party/rust/time-macros/src/date.rs b/third_party/rust/time-macros/src/date.rs
index 574ef8ce6f..03cbf11ae2 100644
--- a/third_party/rust/time-macros/src/date.rs
+++ b/third_party/rust/time-macros/src/date.rs
@@ -1,5 +1,6 @@
use std::iter::Peekable;
+use num_conv::Truncate;
use proc_macro::{token_stream, TokenTree};
use time_core::util::{days_in_year, weeks_in_year};
@@ -93,7 +94,7 @@ pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Date
span_end: Some(month_span),
});
}
- let month = month as _;
+ let month = month.truncate();
if day == 0 || day > days_in_year_month(year, month) {
return Err(Error::InvalidComponent {
name: "day",
@@ -127,10 +128,12 @@ pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Date
impl ToTokenTree for Date {
fn into_token_tree(self) -> TokenTree {
quote_group! {{
- const DATE: ::time::Date = ::time::Date::__from_ordinal_date_unchecked(
- #(self.year),
- #(self.ordinal),
- );
+ const DATE: ::time::Date = unsafe {
+ ::time::Date::__from_ordinal_date_unchecked(
+ #(self.year),
+ #(self.ordinal),
+ )
+ };
DATE
}}
}
diff --git a/third_party/rust/time-macros/src/format_description/ast.rs b/third_party/rust/time-macros/src/format_description/ast.rs
index b75056bc2f..4c3a19e5a6 100644
--- a/third_party/rust/time-macros/src/format_description/ast.rs
+++ b/third_party/rust/time-macros/src/format_description/ast.rs
@@ -1,4 +1,3 @@
-use std::boxed::Box;
use std::iter;
use super::{lexer, unused, Error, Location, Spanned, SpannedValue, Unused};
diff --git a/third_party/rust/time-macros/src/format_description/format_item.rs b/third_party/rust/time-macros/src/format_description/format_item.rs
index 6a8cf555ee..ea36caee05 100644
--- a/third_party/rust/time-macros/src/format_description/format_item.rs
+++ b/third_party/rust/time-macros/src/format_description/format_item.rs
@@ -1,4 +1,3 @@
-use std::boxed::Box;
use std::num::NonZeroU16;
use std::str::{self, FromStr};
@@ -103,14 +102,9 @@ impl From<Item<'_>> for crate::format_description::public::OwnedFormatItem {
impl<'a> From<Box<[Item<'a>]>> for crate::format_description::public::OwnedFormatItem {
fn from(items: Box<[Item<'a>]>) -> Self {
let items = items.into_vec();
- if items.len() == 1 {
- if let Ok([item]) = <[_; 1]>::try_from(items) {
- item.into()
- } else {
- bug!("the length was just checked to be 1")
- }
- } else {
- Self::Compound(items.into_iter().map(Self::from).collect())
+ match <[_; 1]>::try_from(items) {
+ Ok([item]) => item.into(),
+ Err(vec) => Self::Compound(vec.into_iter().map(Into::into).collect()),
}
}
}
@@ -143,6 +137,7 @@ macro_rules! component_definition {
_component_span: Span,
) -> Result<Self, Error>
{
+ #[allow(unused_mut)]
let mut this = Self {
$($field: None),*
};
@@ -212,6 +207,7 @@ component_definition! {
Day = "day" {
padding = "padding": Option<Padding> => padding,
},
+ End = "end" {},
Hour = "hour" {
padding = "padding": Option<Padding> => padding,
base = "repr": Option<HourBase> => is_12_hour_clock,
diff --git a/third_party/rust/time-macros/src/format_description/lexer.rs b/third_party/rust/time-macros/src/format_description/lexer.rs
index 2c927cb94d..2ea53af57a 100644
--- a/third_party/rust/time-macros/src/format_description/lexer.rs
+++ b/third_party/rust/time-macros/src/format_description/lexer.rs
@@ -3,7 +3,7 @@ use core::iter;
use super::{Error, Location, Spanned, SpannedValue};
pub(super) struct Lexed<I: Iterator> {
- iter: core::iter::Peekable<I>,
+ iter: iter::Peekable<I>,
}
impl<I: Iterator> Iterator for Lexed<I> {
diff --git a/third_party/rust/time-macros/src/format_description/mod.rs b/third_party/rust/time-macros/src/format_description/mod.rs
index fde1272f6a..676028dec4 100644
--- a/third_party/rust/time-macros/src/format_description/mod.rs
+++ b/third_party/rust/time-macros/src/format_description/mod.rs
@@ -1,7 +1,5 @@
//! Parser for format descriptions.
-use std::vec::Vec;
-
macro_rules! version {
($range:expr) => {
$range.contains(&VERSION)
@@ -17,7 +15,7 @@ pub(crate) fn parse_with_version(
version: Option<crate::FormatDescriptionVersion>,
s: &[u8],
proc_span: proc_macro::Span,
-) -> Result<Vec<crate::format_description::public::OwnedFormatItem>, crate::Error> {
+) -> Result<Vec<public::OwnedFormatItem>, crate::Error> {
match version {
Some(crate::FormatDescriptionVersion::V1) | None => parse::<1>(s, proc_span),
Some(crate::FormatDescriptionVersion::V2) => parse::<2>(s, proc_span),
@@ -27,7 +25,7 @@ pub(crate) fn parse_with_version(
fn parse<const VERSION: u8>(
s: &[u8],
proc_span: proc_macro::Span,
-) -> Result<Vec<crate::format_description::public::OwnedFormatItem>, crate::Error> {
+) -> Result<Vec<public::OwnedFormatItem>, crate::Error> {
let mut lexed = lexer::lex::<VERSION>(s, proc_span);
let ast = ast::parse::<_, VERSION>(&mut lexed);
let format_items = format_item::parse(ast);
diff --git a/third_party/rust/time-macros/src/format_description/public/component.rs b/third_party/rust/time-macros/src/format_description/public/component.rs
index 4737c6ce5c..94c73f0fb4 100644
--- a/third_party/rust/time-macros/src/format_description/public/component.rs
+++ b/third_party/rust/time-macros/src/format_description/public/component.rs
@@ -46,4 +46,5 @@ declare_component! {
OffsetSecond
Ignore
UnixTimestamp
+ End
}
diff --git a/third_party/rust/time-macros/src/format_description/public/mod.rs b/third_party/rust/time-macros/src/format_description/public/mod.rs
index ccb0b6e2a3..9fd8b5ece1 100644
--- a/third_party/rust/time-macros/src/format_description/public/mod.rs
+++ b/third_party/rust/time-macros/src/format_description/public/mod.rs
@@ -19,12 +19,12 @@ impl ToTokenStream for OwnedFormatItem {
fn append_to(self, ts: &mut TokenStream) {
match self {
Self::Literal(bytes) => quote_append! { ts
- ::time::format_description::FormatItem::Literal {
+ ::time::format_description::BorrowedFormatItem::Literal {
0: #(Literal::byte_string(bytes.as_ref()))
}
},
Self::Component(component) => quote_append! { ts
- ::time::format_description::FormatItem::Component { 0: #S(component) }
+ ::time::format_description::BorrowedFormatItem::Component { 0: #S(component) }
},
Self::Compound(items) => {
let items = items
@@ -33,11 +33,11 @@ impl ToTokenStream for OwnedFormatItem {
.map(|item| quote! { #S(item), })
.collect::<TokenStream>();
quote_append! { ts
- ::time::format_description::FormatItem::Compound { 0: &[#S(items)] }
+ ::time::format_description::BorrowedFormatItem::Compound { 0: &[#S(items)] }
}
}
Self::Optional(item) => quote_append! {ts
- ::time::format_description::FormatItem::Optional { 0: &#S(*item) }
+ ::time::format_description::BorrowedFormatItem::Optional { 0: &#S(*item) }
},
Self::First(items) => {
let items = items
@@ -46,7 +46,7 @@ impl ToTokenStream for OwnedFormatItem {
.map(|item| quote! { #S(item), })
.collect::<TokenStream>();
quote_append! { ts
- ::time::format_description::FormatItem::First { 0: &[#S(items)] }
+ ::time::format_description::BorrowedFormatItem::First { 0: &[#S(items)] }
}
}
}
diff --git a/third_party/rust/time-macros/src/format_description/public/modifier.rs b/third_party/rust/time-macros/src/format_description/public/modifier.rs
index e39c6bf552..63bfaa7065 100644
--- a/third_party/rust/time-macros/src/format_description/public/modifier.rs
+++ b/third_party/rust/time-macros/src/format_description/public/modifier.rs
@@ -10,18 +10,18 @@ macro_rules! to_tokens {
$struct_vis:vis struct $struct_name:ident {$(
$(#[$field_attr:meta])*
$field_vis:vis $field_name:ident : $field_ty:ty
- ),+ $(,)?}
+ ),* $(,)?}
) => {
$(#[$struct_attr])*
$struct_vis struct $struct_name {$(
$(#[$field_attr])*
$field_vis $field_name: $field_ty
- ),+}
+ ),*}
impl ToTokenTree for $struct_name {
fn into_token_tree(self) -> TokenTree {
let mut tokens = TokenStream::new();
- let Self {$($field_name),+} = self;
+ let Self {$($field_name),*} = self;
quote_append! { tokens
let mut value = ::time::format_description::modifier::$struct_name::default();
@@ -30,7 +30,7 @@ macro_rules! to_tokens {
quote_append!(tokens value.$field_name =);
$field_name.append_to(&mut tokens);
quote_append!(tokens ;);
- )+
+ )*
quote_append!(tokens value);
proc_macro::TokenTree::Group(proc_macro::Group::new(
@@ -245,3 +245,7 @@ to_tokens! {
pub(crate) sign_is_mandatory: bool,
}
}
+
+to_tokens! {
+ pub(crate) struct End {}
+}
diff --git a/third_party/rust/time-macros/src/helpers/mod.rs b/third_party/rust/time-macros/src/helpers/mod.rs
index 56300b3e65..0cca2002de 100644
--- a/third_party/rust/time-macros/src/helpers/mod.rs
+++ b/third_party/rust/time-macros/src/helpers/mod.rs
@@ -4,6 +4,7 @@ mod string;
use std::iter::Peekable;
use std::str::FromStr;
+use num_conv::prelude::*;
use proc_macro::{token_stream, Span, TokenTree};
use time_core::util::{days_in_year, is_leap_year};
@@ -92,15 +93,17 @@ fn jan_weekday(year: i32, ordinal: i32) -> u8 {
}
let adj_year = year - 1;
- ((ordinal + adj_year + div_floor!(adj_year, 4) - div_floor!(adj_year, 100)
+ (ordinal + adj_year + div_floor!(adj_year, 4) - div_floor!(adj_year, 100)
+ div_floor!(adj_year, 400)
+ 6)
- .rem_euclid(7)) as _
+ .rem_euclid(7)
+ .cast_unsigned()
+ .truncate()
}
pub(crate) fn days_in_year_month(year: i32, month: u8) -> u8 {
- [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month as usize - 1]
- + (month == 2 && is_leap_year(year)) as u8
+ [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month.extend::<usize>() - 1]
+ + u8::from(month == 2 && is_leap_year(year))
}
pub(crate) fn ywd_to_yo(year: i32, week: u8, iso_weekday_number: u8) -> (i32, u16) {
@@ -120,8 +123,9 @@ pub(crate) fn ywd_to_yo(year: i32, week: u8, iso_weekday_number: u8) -> (i32, u1
}
pub(crate) fn ymd_to_yo(year: i32, month: u8, day: u8) -> (i32, u16) {
- let ordinal = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334][month as usize - 1]
- + (month > 2 && is_leap_year(year)) as u16;
+ let ordinal = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
+ [month.extend::<usize>() - 1]
+ + u16::from(month > 2 && is_leap_year(year));
(year, ordinal + u16::from(day))
}
diff --git a/third_party/rust/time-macros/src/lib.rs b/third_party/rust/time-macros/src/lib.rs
index 0e8568cdad..65e24d7d3b 100644
--- a/third_party/rust/time-macros/src/lib.rs
+++ b/third_party/rust/time-macros/src/lib.rs
@@ -1,37 +1,12 @@
-#![deny(
- anonymous_parameters,
- clippy::all,
- illegal_floating_point_literal_pattern,
- late_bound_lifetime_arguments,
- path_statements,
- patterns_in_fns_without_body,
- rust_2018_idioms,
- trivial_casts,
- trivial_numeric_casts,
- unreachable_pub,
- unsafe_code,
- unused_extern_crates
-)]
-#![warn(
- clippy::dbg_macro,
- clippy::decimal_literal_representation,
- clippy::get_unwrap,
- clippy::nursery,
- clippy::print_stdout,
- clippy::todo,
- clippy::unimplemented,
- clippy::unnested_or_patterns,
- clippy::unwrap_used,
- clippy::use_debug,
- single_use_lifetimes,
- unused_qualifications,
- variant_size_differences
-)]
#![allow(
- clippy::missing_const_for_fn, // useless in proc macro
- clippy::redundant_pub_crate, // suggests bad style
- clippy::option_if_let_else, // suggests terrible code
+ clippy::missing_const_for_fn, // irrelevant for proc macros
+ clippy::missing_docs_in_private_items, // TODO remove
+ clippy::std_instead_of_core, // irrelevant for proc macros
+ clippy::std_instead_of_alloc, // irrelevant for proc macros
+ clippy::alloc_instead_of_core, // irrelevant for proc macros
+ missing_docs, // TODO remove
)]
+
#[allow(unused_macros)]
macro_rules! bug {
() => { compile_error!("provide an error message to help fix a possible bug") };
@@ -93,6 +68,7 @@ enum FormatDescriptionVersion {
#[cfg(any(feature = "formatting", feature = "parsing"))]
enum VersionOrModuleName {
Version(FormatDescriptionVersion),
+ #[cfg_attr(not(feature = "serde"), allow(dead_code))]
ModuleName(Ident),
}
@@ -175,7 +151,7 @@ pub fn format_description(input: TokenStream) -> TokenStream {
let items = format_description::parse_with_version(version, &string, span)?;
Ok(quote! {{
- const DESCRIPTION: &[::time::format_description::FormatItem<'_>] = &[#S(
+ const DESCRIPTION: &[::time::format_description::BorrowedFormatItem<'_>] = &[#S(
items
.into_iter()
.map(|item| quote! { #S(item), })
@@ -236,7 +212,8 @@ pub fn serde_format_description(input: TokenStream) -> TokenStream {
let items: TokenStream =
items.into_iter().map(|item| quote! { #S(item), }).collect();
let items = quote! {
- const ITEMS: &[::time::format_description::FormatItem<'_>] = &[#S(items)];
+ const ITEMS: &[::time::format_description::BorrowedFormatItem<'_>]
+ = &[#S(items)];
ITEMS
};
diff --git a/third_party/rust/time-macros/src/offset.rs b/third_party/rust/time-macros/src/offset.rs
index 62d7a223da..04dd37f131 100644
--- a/third_party/rust/time-macros/src/offset.rs
+++ b/third_party/rust/time-macros/src/offset.rs
@@ -1,5 +1,6 @@
use std::iter::Peekable;
+use num_conv::prelude::*;
use proc_macro::{token_stream, Span, TokenTree};
use time_core::convert::*;
@@ -52,21 +53,21 @@ pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Offs
}
}
- if hours >= 24 {
+ if hours > 25 {
Err(Error::InvalidComponent {
name: "hour",
value: hours.to_string(),
span_start: Some(hours_span),
span_end: Some(hours_span),
})
- } else if minutes >= Minute.per(Hour) as _ {
+ } else if minutes >= Minute::per(Hour).cast_signed() {
Err(Error::InvalidComponent {
name: "minute",
value: minutes.to_string(),
span_start: Some(minutes_span),
span_end: Some(minutes_span),
})
- } else if seconds >= Second.per(Minute) as _ {
+ } else if seconds >= Second::per(Minute).cast_signed() {
Err(Error::InvalidComponent {
name: "second",
value: seconds.to_string(),
@@ -85,11 +86,13 @@ pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Offs
impl ToTokenTree for Offset {
fn into_token_tree(self) -> TokenTree {
quote_group! {{
- const OFFSET: ::time::UtcOffset = ::time::UtcOffset::__from_hms_unchecked(
- #(self.hours),
- #(self.minutes),
- #(self.seconds),
- );
+ const OFFSET: ::time::UtcOffset = unsafe {
+ ::time::UtcOffset::__from_hms_unchecked(
+ #(self.hours),
+ #(self.minutes),
+ #(self.seconds),
+ )
+ };
OFFSET
}}
}
diff --git a/third_party/rust/time-macros/src/quote.rs b/third_party/rust/time-macros/src/quote.rs
index 4d3dcbca03..8603f4fa46 100644
--- a/third_party/rust/time-macros/src/quote.rs
+++ b/third_party/rust/time-macros/src/quote.rs
@@ -45,20 +45,19 @@ macro_rules! sym {
};
}
+#[allow(unused_macro_rules)] // Varies by feature flag combination.
macro_rules! quote_inner {
// Base case
($ts:ident) => {};
// Single or double symbols
($ts:ident :: $($tail:tt)*) => { sym!($ts ':' ':'); quote_inner!($ts $($tail)*); };
- ($ts:ident .. $($tail:tt)*) => { sym!($ts '.' '.'); quote_inner!($ts $($tail)*); };
($ts:ident : $($tail:tt)*) => { sym!($ts ':'); quote_inner!($ts $($tail)*); };
($ts:ident = $($tail:tt)*) => { sym!($ts '='); quote_inner!($ts $($tail)*); };
($ts:ident ; $($tail:tt)*) => { sym!($ts ';'); quote_inner!($ts $($tail)*); };
($ts:ident , $($tail:tt)*) => { sym!($ts ','); quote_inner!($ts $($tail)*); };
($ts:ident . $($tail:tt)*) => { sym!($ts '.'); quote_inner!($ts $($tail)*); };
($ts:ident & $($tail:tt)*) => { sym!($ts '&'); quote_inner!($ts $($tail)*); };
- ($ts:ident << $($tail:tt)*) => { sym!($ts '<' '<'); quote_inner!($ts $($tail)*); };
($ts:ident < $($tail:tt)*) => { sym!($ts '<'); quote_inner!($ts $($tail)*); };
($ts:ident >> $($tail:tt)*) => { sym!($ts '>' '>'); quote_inner!($ts $($tail)*); };
($ts:ident > $($tail:tt)*) => { sym!($ts '>'); quote_inner!($ts $($tail)*); };
diff --git a/third_party/rust/time-macros/src/time.rs b/third_party/rust/time-macros/src/time.rs
index 96314de1f4..4e565c1ded 100644
--- a/third_party/rust/time-macros/src/time.rs
+++ b/third_party/rust/time-macros/src/time.rs
@@ -73,21 +73,21 @@ pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Time
(hour, Period::Pm) => hour + 12,
};
- if hour >= Hour.per(Day) {
+ if hour >= Hour::per(Day) {
Err(Error::InvalidComponent {
name: "hour",
value: hour.to_string(),
span_start: Some(hour_span),
span_end: Some(period_span.unwrap_or(hour_span)),
})
- } else if minute >= Minute.per(Hour) {
+ } else if minute >= Minute::per(Hour) {
Err(Error::InvalidComponent {
name: "minute",
value: minute.to_string(),
span_start: Some(minute_span),
span_end: Some(minute_span),
})
- } else if second >= Second.per(Minute) as _ {
+ } else if second >= Second::per(Minute) as _ {
Err(Error::InvalidComponent {
name: "second",
value: second.to_string(),
@@ -99,7 +99,7 @@ pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Time
hour,
minute,
second: second.trunc() as _,
- nanosecond: (second.fract() * Nanosecond.per(Second) as f64).round() as _,
+ nanosecond: (second.fract() * Nanosecond::per(Second) as f64).round() as _,
})
}
}
@@ -107,12 +107,14 @@ pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Time
impl ToTokenTree for Time {
fn into_token_tree(self) -> TokenTree {
quote_group! {{
- const TIME: ::time::Time = ::time::Time::__from_hms_nanos_unchecked(
- #(self.hour),
- #(self.minute),
- #(self.second),
- #(self.nanosecond),
- );
+ const TIME: ::time::Time = unsafe {
+ ::time::Time::__from_hms_nanos_unchecked(
+ #(self.hour),
+ #(self.minute),
+ #(self.second),
+ #(self.nanosecond),
+ )
+ };
TIME
}}
}
diff --git a/third_party/rust/time-macros/src/to_tokens.rs b/third_party/rust/time-macros/src/to_tokens.rs
index 7e73211533..7802158cea 100644
--- a/third_party/rust/time-macros/src/to_tokens.rs
+++ b/third_party/rust/time-macros/src/to_tokens.rs
@@ -2,6 +2,7 @@ use std::num::NonZeroU16;
use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree};
+/// Turn a type into a [`TokenStream`].
pub(crate) trait ToTokenStream: Sized {
fn append_to(self, ts: &mut TokenStream);
}
diff --git a/third_party/rust/time/.cargo-checksum.json b/third_party/rust/time/.cargo-checksum.json
index 3b5423f531..9997a5d1af 100644
--- a/third_party/rust/time/.cargo-checksum.json
+++ b/third_party/rust/time/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"a011e69d5ae4484afdc3df407f6fcfdc91461b412730302a3d4fb4f4f731fe3f","LICENSE-Apache":"b8929fea28678da67251fb2daf9438f67503814211051861612441806d8edb05","LICENSE-MIT":"04620bf27e4a643dd47bf27652320c205acdb776c1f9f24bb8c3bfaba10804c5","README.md":"91fdb919a41d2c1ee240a66d548adf1e1a40db95bcfe3adaf8c55f37c2afe1e9","src/date.rs":"5c0ae4a98530dd954c19b972df517b80bd6f35c73747e422b55ea0162d4c5fcf","src/date_time.rs":"37c97a93ca15be9b9bbd64b497429c430e91b6bcf51a50f571f79646b4348707","src/duration.rs":"469e62e0b545f45b99c37a7033c269474a932b52a2dc9399d62bf7877be1a8ce","src/error/component_range.rs":"26a1aa4ea2d0f9887efcbe9584d5aa14b1e5d37525a52dc9f18e1e282599625d","src/error/conversion_range.rs":"972abb765370070de01e2fc2e1bb1e80808a069e6213577d7beaca02e1d707c3","src/error/different_variant.rs":"107bef7b3addd7108b36a2da8389f611d4482f34a5b63429841141e05c8cb30c","src/error/format.rs":"d87846c2ac62dec421402ea21e5d2a8d73add6658df4ac914067a4b43cb0ef20","src/error/indeterminate_offset.rs":"1f52f9ea107847fa781399cfcc8046451d70155fb497486c80b2138f82782941","src/error/invalid_format_description.rs":"cf617348b55d9c3273060fa2d99bd4eda215452270025f2b6caef6ef9f387af5","src/error/invalid_variant.rs":"b653a3e6e902f06cb9f2e0366c4da84b92e8bdb03164c2f8cb15fe66415706e4","src/error/mod.rs":"15fb848b1919d9cfb50fb9091abfcea6a8c7db5a2fcd6cb8f32c4af5f1ea4464","src/error/parse.rs":"3bdc8201a14469d2cc7a12a295058569098f9cfc9bd1e8fc9f526ada8298e4f8","src/error/parse_from_description.rs":"990359eb5fcb64c1ee363b044147b7330a92a4cb7373dc2f17f6fd3bcc6411a0","src/error/try_from_parsed.rs":"8c227be52653a1d33af01a8024c0fc56f1f9803f08ef01487a7eaa5833adbb57","src/ext.rs":"f31cdcf38c23a0111524ae431420f299d4d4735d99fc9a873d3472a3699de7ef","src/format_description/borrowed_format_item.rs":"afab66e65a84895751d3557fc5b8a3a5e63f9c483a6a534aa4f86fd2a5145f0b","src/format_description/component.rs":"289469371588f24de6c7afdd40e7ce65f6b08c3e05434900eafdca7dde59ab07","src/format_description/mod.rs":"955a227e9bb13e3085a43457bf8028085db92c0266b6573ddf1e12df3b937c0f","src/format_description/modifier.rs":"5c6330b3557a156d2acfd4eb454783a41a6edf62c5046e2ca60dc060caf31451","src/format_description/owned_format_item.rs":"419f5354bf504562c9225dfe90b61eee9bc959211a86a327197b4f54283da775","src/format_description/parse/ast.rs":"c20fedf0314a2ede5e3ee66024534ca8a65a2b623959386872250e19d5d43b6e","src/format_description/parse/format_item.rs":"4639e23fb86dbbef6d764e8279cc43dd5f6e09d8b14b277e6f6b9bce81f5c3ff","src/format_description/parse/lexer.rs":"c10105640a618e1e850eb6e4fd888c47d881b3f85bde691fdf204199a693e127","src/format_description/parse/mod.rs":"210cd68a37b5cbbc6a6e3b3d5161f03ad94b2902bb01899d0c02d0278f420c8c","src/format_description/well_known/iso8601.rs":"8313905039a637d4d132f8318a59c06246e7b61550b4e4bc7d129232ac022e43","src/format_description/well_known/iso8601/adt_hack.rs":"59a5182dc200a26654944a64a81488a55c7a387485f219371503a010c751e338","src/format_description/well_known/rfc2822.rs":"36c23394724ae12250d4193cab26887a6ff8f82ca441ea6b0d03c4f1c928b3dd","src/format_description/well_known/rfc3339.rs":"1a6318dffd3ebb6ac7cf96eae3d9b1eb44b1089cf4284fa6a7e935c6fcf1b43c","src/formatting/formattable.rs":"fe75a835d20f144faf8f1297d9b501e72fcce321c7dc1077805e4a2b2f9b9390","src/formatting/iso8601.rs":"3dc83bf234b60e80ab499bf3ec490b2772d69a02b452f93cbc8e843ebf340fc2","src/formatting/mod.rs":"b4f98455609968a28e6479077d01eec60e3331064dbcd453f29c6d5a768d9857","src/instant.rs":"f1724e49b173b16b08818bfd06133ce4f61da7df286ff61982113cc184efe1c0","src/lib.rs":"be86048ca1c4ab497384edd9507b41c5683d946b3149ff978277c1323cbc2889","src/macros.rs":"eb9e02a1f97bb8befab7bc27c937136817e4f65e0b3e040a81394ae938980558","src/month.rs":"91b20ea12b1a36a9ddb828279572c6098b8504a048627088137963a612b572b8","src/offset_date_time.rs":"288d7a34eecbbd9345e13804302bcb55df716fdef0fafe1d2d06e52c0d77829e","src/parsing/combinator/mod.rs":"b342fbd95dd986309d81e8910363920ba6db00958b459f6d97f57da3ae3e550d","src/parsing/combinator/rfc/iso8601.rs":"13289a0d58de273327830a3001167a8964edc5045486301efdf3ddc2e4079c32","src/parsing/combinator/rfc/mod.rs":"f30b75d248f5ae92c27646d504703f5489185afb76c998cc4375437b3d15c822","src/parsing/combinator/rfc/rfc2234.rs":"08e2813c6d40c0dae881875fe0417ae06886c73679256587e33186e46b3c3bae","src/parsing/combinator/rfc/rfc2822.rs":"2aff3a6a2778bc806031cff92ad2f43f0874620b5d484b5b39ee2d2507212f06","src/parsing/component.rs":"09008cf9d08c4b0c3cb051b986291a29e977a780bb1455f9c33e472db983e8da","src/parsing/iso8601.rs":"8dd99317a6fcde7d85f8bf5c492675efde751731b687b0cde0d5653cb129743a","src/parsing/mod.rs":"37082ac824c6c3f4900766a0a3140dc7aa46b3f85cb6098f11da7da333e421b0","src/parsing/parsable.rs":"d1b3c001f57c735af395553d35e76f9342a83da87b5a843d1eb015807a076db9","src/parsing/parsed.rs":"5cafd2220fe325817b8b65729274a0ca7c741f4579d99bc888cb1997436ef127","src/parsing/shim.rs":"46efc374bc3129e28936a850143fff8e42aafe10c69ebbb904195aaeca26adc9","src/primitive_date_time.rs":"ce557a9db6d7ed663ff78d62a60af5ee8a287f04f6fc979e81047c339d50495a","src/quickcheck.rs":"94640161a21319b75c9b31a6bbc6e34a4d573c20606b954db1bd12ddef366af8","src/rand.rs":"889c98938427a4885036673df8fcebd84c7cc20fb4b3ca82c447ff4b977c8a15","src/serde/iso8601.rs":"997bbf4fe4018f8fdc9335ac863b543fb24a58b2dee394615505a24311331516","src/serde/mod.rs":"a575be28adaf89ff5b1476b367e2019ea09d68368f227d211bc6f33f192bc1e3","src/serde/rfc2822.rs":"fe97aa1311037a362eb477fe8c6729b3b85ff2d0afab7148f10f64d109081f90","src/serde/rfc3339.rs":"9835c8b8fb24b53657769b81a71188fe4261e5869917779e1702b3a0aa854654","src/serde/timestamp.rs":"30971ad5d1fef11e396eee48d476b828ed4e99f6eac587383b864dd95c120fe4","src/serde/visitor.rs":"c5293181f337ab09ae98ce4ef41eae558ae5e3b86f961e4a0c9c93cb034647ed","src/sys/local_offset_at/imp.rs":"4b6e57f02566364270ac9b7e1540290a5658a296f7e911f988264d103e420326","src/sys/local_offset_at/mod.rs":"95b042824b414b3021eda2bcf0821afc529bfd8d4cfcad0b893edb197e48461b","src/sys/local_offset_at/unix.rs":"339ab502e121c24c6ea617f444a58fb7e23cf5afd13c5f7a52eda6d69591d580","src/sys/local_offset_at/wasm_js.rs":"e49ef256c874d6b8d15ef264a66c0b837ac42cd0683e38f3f31af2c2e8fca459","src/sys/local_offset_at/windows.rs":"0836e20249421b1f32e77f0ce4be0d3db30be00478f4c56fda9ddbff0bbb0c5d","src/sys/mod.rs":"0a43797e55e986233a71f1cc4b3a21997da42bc15db7d912373296cd535e49bc","src/tests.rs":"38d1f794892e6ab3fece55839a8e4ab6d0d2c325323310eda32144eb7240bf59","src/time.rs":"197c53ef2b49f73c363eabe2332ffd4eaba18f91f2d17070e8d568069a977c64","src/utc_offset.rs":"ce39c34ec5419a1bf51f7b8401e38a4e0daab7e827fe2fd239fae8089a212c7e","src/util.rs":"1fff6c7d712a4d2665cca55db9c142185cc13afa20f925912cb85abbcc366938","src/weekday.rs":"0a9f79b6aef6bb085204216d0be1c7666426c589c3263b63384c4b74e8b54658"},"package":"59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446"}
\ No newline at end of file
+{"files":{"Cargo.toml":"bd60027c57b6255a65c218363b8e018ccbc7339af846b30ecc14c7781e9dc701","LICENSE-Apache":"edd65bdd88957a205c47d53fa499eed8865a70320f0f03f6391668cb304ea376","LICENSE-MIT":"231c837c45eb53f108fb48929e488965bc4fcc14e9ea21d35f50e6b99d98685b","README.md":"36c735ebe90cdc962dec7e240607a052088697d0cefd555f093746039b0943cd","src/date.rs":"a20df0cc036d1c09f5554bf2c52a7dc7ceb2d08af355a5079282b3660c1bf062","src/duration.rs":"dc394786d19a1fae779ebfbd14e1502a5701756a500224fc82f116ecfeef39b2","src/error/component_range.rs":"26a1aa4ea2d0f9887efcbe9584d5aa14b1e5d37525a52dc9f18e1e282599625d","src/error/conversion_range.rs":"972abb765370070de01e2fc2e1bb1e80808a069e6213577d7beaca02e1d707c3","src/error/different_variant.rs":"107bef7b3addd7108b36a2da8389f611d4482f34a5b63429841141e05c8cb30c","src/error/format.rs":"d87846c2ac62dec421402ea21e5d2a8d73add6658df4ac914067a4b43cb0ef20","src/error/indeterminate_offset.rs":"1f52f9ea107847fa781399cfcc8046451d70155fb497486c80b2138f82782941","src/error/invalid_format_description.rs":"cf617348b55d9c3273060fa2d99bd4eda215452270025f2b6caef6ef9f387af5","src/error/invalid_variant.rs":"b653a3e6e902f06cb9f2e0366c4da84b92e8bdb03164c2f8cb15fe66415706e4","src/error/mod.rs":"a30edbd2cdc701d0327291ef1201aa1531ab8bb1a1318945085ab71f8918bb16","src/error/parse.rs":"a3f23c10cab2e4ce159c5b3d5774d54214bd0098b70a59c48a2407777cdee9e5","src/error/parse_from_description.rs":"2de1f5b5af3d9bb358cee1c66a2721a78ee99f0ee5e0c06f159e62de7a294a5f","src/error/try_from_parsed.rs":"8c227be52653a1d33af01a8024c0fc56f1f9803f08ef01487a7eaa5833adbb57","src/ext/digit_count.rs":"d32af3cac57077870f6b31c5bd6e0da3a530a7c713aaebdcd27125d9fd8857a3","src/ext/instant.rs":"7598e0fab29b6a9c2a7d1f5fa761ed7d2d8b54deac801189fec652a358ed7e08","src/ext/mod.rs":"dc70d230a170ddd1bf7798703d9ff7cac419aaadc82b5f8dbf46d5320322e2c5","src/ext/numerical_duration.rs":"d6c74898e4f6025158ef8daf35abe53268161db2c18d12e718fec17cfe442d6d","src/ext/numerical_std_duration.rs":"0dc24fc48c3eb94c22aca75423e652e13322262c63edaf5393a28bc8470d6082","src/format_description/borrowed_format_item.rs":"529cb5c91a0ff08a6f8615ccf1289c9b5f01996f691db1bd1cb5a451c25ead4c","src/format_description/component.rs":"b65c0ca896ea6ec8dbfc7570c69849c88cbba6031a2847dcfdce06d721a59397","src/format_description/mod.rs":"640958320fcda04287a84767b6fe2f941a0e5f7498b58a531f6dffbfd0ed1757","src/format_description/modifier.rs":"450e6fc64353f80304c2b616bf3e8c58f1ae02e0a2875e946cf0ea27e87a4e10","src/format_description/owned_format_item.rs":"8834a705f3f0c61a1851ffb4b897766b4216ae2dbac6ae34ad0d4aa3fd46a064","src/format_description/parse/ast.rs":"f96b423629e265d85f4068c7590a27405cb5b4275bf9f547e674f337b281a58e","src/format_description/parse/format_item.rs":"0a5b013e5dbb226a90bcda533866d9a141d1270412f44a42ba0dc053479e782c","src/format_description/parse/lexer.rs":"fae06f81ace377f716857b60bb5c0d6591123478a99b4c9823f4b19bb3f73af1","src/format_description/parse/mod.rs":"99ac90c3cdb73ce52b3e2866c4f3f7dae0557b88bce5846161c901e11fbd80c4","src/format_description/well_known/iso8601.rs":"30b2f495044ab4e1f3ff6a317a8b4ca2ffb46b7cc23428eca8b685c0f16518cc","src/format_description/well_known/iso8601/adt_hack.rs":"8f1d5f4a0959070ab96343868086adfa6fa3f5a5823f50a111c824b4a9bcd39b","src/format_description/well_known/rfc2822.rs":"36c23394724ae12250d4193cab26887a6ff8f82ca441ea6b0d03c4f1c928b3dd","src/format_description/well_known/rfc3339.rs":"1a6318dffd3ebb6ac7cf96eae3d9b1eb44b1089cf4284fa6a7e935c6fcf1b43c","src/formatting/formattable.rs":"7cd6416af947b92316b6505c2d3c45b163c87257302ae18868b1f83fbba034a4","src/formatting/iso8601.rs":"1d265461a515406c1253350b5eb870b5416f9ce76cdf96a8fbfb7c4b57dcb623","src/formatting/mod.rs":"115af930b598ed77a9b55ba8f7d89befefb1f21cec960ebd2c315188a25ba07d","src/instant.rs":"96a5aba377443945c1a75d669347711c40b33ea842f7d609eb3a7572b6ce64bf","src/internal_macros.rs":"cf2479614cc66f3b9c533f5f5c4584fe90136c7ad4d0c2137d5aa386ffa5319f","src/lib.rs":"f2fdfeaf6f85d71cbb875d54645964997a741d9c012cecdfda9c92ee82f01d15","src/macros.rs":"bea9e61c00ac2b523357300a51830d32e46f03456fc03d18fe26928042e10a2d","src/month.rs":"cee05aaad0088a38f0bec22dc913590c18d3bc74208aa72394512424677ffd25","src/offset_date_time.rs":"7d2a476c69c7f3c16c466347a9dff2c6d9687e82044b7c39034218f14adfa404","src/parsing/combinator/mod.rs":"e49d14f7d0c38a847d9f5f2e63d466464641d365f1d39c48d1a455ebe6632ec5","src/parsing/combinator/rfc/iso8601.rs":"8b819a8e3ef4b160b1bce94cad6534e3c0acbeef98cfc494606815468781c19a","src/parsing/combinator/rfc/mod.rs":"f30b75d248f5ae92c27646d504703f5489185afb76c998cc4375437b3d15c822","src/parsing/combinator/rfc/rfc2234.rs":"08e2813c6d40c0dae881875fe0417ae06886c73679256587e33186e46b3c3bae","src/parsing/combinator/rfc/rfc2822.rs":"2aff3a6a2778bc806031cff92ad2f43f0874620b5d484b5b39ee2d2507212f06","src/parsing/component.rs":"32d69c4f86e3a2e90dffbd74af8b19ea25dbeaa6f60aa89637f3988d64fd7967","src/parsing/iso8601.rs":"6cc42738e8e8ed5b2800b0fbdd85c120705ec3452483f625c75a5a1e5bf0fcc9","src/parsing/mod.rs":"7d790f512710dc33b9f6f51e4237c04812aafd7d68710df01d02d755062568d7","src/parsing/parsable.rs":"8fa8f9b2653fa5e911a6528fd3f62cd371e3b2bb2c2727447fd2a33381ffed68","src/parsing/parsed.rs":"32289f2495ad6826eab3c745c8df1c2590510c88bb4d7c21b2dbc6bfd664ed35","src/parsing/shim.rs":"46efc374bc3129e28936a850143fff8e42aafe10c69ebbb904195aaeca26adc9","src/primitive_date_time.rs":"faca737cbe28bce420dfb4468021f46968c29abdde449479af225e597441c96e","src/quickcheck.rs":"09baaccea54f77cc60d1c43a726275d15db42e1c8b4ec157d4417d3cded36762","src/rand.rs":"dcedb2473e240c46de00aa4b156d66cb755da9d5fd04adaedb3682cb6a12218f","src/serde/iso8601.rs":"997bbf4fe4018f8fdc9335ac863b543fb24a58b2dee394615505a24311331516","src/serde/mod.rs":"99a93f87c0dd35f818af72228ca74a0952ce4db4d426ddf7c21a0220caee890e","src/serde/rfc2822.rs":"fe97aa1311037a362eb477fe8c6729b3b85ff2d0afab7148f10f64d109081f90","src/serde/rfc3339.rs":"9835c8b8fb24b53657769b81a71188fe4261e5869917779e1702b3a0aa854654","src/serde/timestamp/microseconds.rs":"7b8a0f6e455ddb6c50ed4a8473ea68cf9aacb31af36ccc058b68fa3bb5f3246d","src/serde/timestamp/milliseconds.rs":"df995f05f340f0429697269aa152a39b0beca45e1b4d8e60f4a6bb27f09a4d88","src/serde/timestamp/mod.rs":"20ddd158fe5e491f9f7e74ef074d2bb10c8b02b1fcd399c0429fd796d799aa01","src/serde/timestamp/nanoseconds.rs":"a46b1e60e8b19792a26ebdab33cba53db95cb13e3c4d9675a17b1491e9fb2940","src/serde/visitor.rs":"973ba2826134d09b109ef7c09a80c48ab724bd9051706bfde85e1ba930e00134","src/sys/local_offset_at/imp.rs":"4b6e57f02566364270ac9b7e1540290a5658a296f7e911f988264d103e420326","src/sys/local_offset_at/mod.rs":"95b042824b414b3021eda2bcf0821afc529bfd8d4cfcad0b893edb197e48461b","src/sys/local_offset_at/unix.rs":"ce02c86c4b4588ef3ebfa56bc82ff09e83677fc7679e61843b0bd80c4308fcbc","src/sys/local_offset_at/wasm_js.rs":"7cdacf5548a89a00265764ef07c1abb2ea1a3211f1ec720932b066f1d18b2f20","src/sys/local_offset_at/windows.rs":"90c16515d7ac29961bd0b8af92e38c7999f260bfd3438c9935fee65e8c0cc1e9","src/sys/mod.rs":"0a43797e55e986233a71f1cc4b3a21997da42bc15db7d912373296cd535e49bc","src/tests.rs":"c858af9559784ec4fe82ddcd0f4029b1c519ef7699288995d8fac007feada859","src/time.rs":"418ae2964af95fbbfba070598b09e36475846792c1916d990e20f5481e0ea9e7","src/utc_offset.rs":"0e51f4ebb740f95b0f2b2d956b72ff49dcc993b81e4d14d1d1809fb355be5817","src/util.rs":"34b4d98803dd99d0db813d0e3cda9dcbb235a8315b6cf1438e0048d14d76663a","src/weekday.rs":"76ed201232f03077474c05e53f34b0403ead637c40a5228b9b61b342048f2917"},"package":"5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"}
\ No newline at end of file
diff --git a/third_party/rust/time/Cargo.toml b/third_party/rust/time/Cargo.toml
index ad2e8c94b3..8f1996f42e 100644
--- a/third_party/rust/time/Cargo.toml
+++ b/third_party/rust/time/Cargo.toml
@@ -11,9 +11,9 @@
[package]
edition = "2021"
-rust-version = "1.65.0"
+rust-version = "1.67.0"
name = "time"
-version = "0.3.23"
+version = "0.3.36"
authors = [
"Jacob Pratt <open-source@jhpratt.dev>",
"Time contributors",
@@ -46,6 +46,7 @@ all-features = true
rustdoc-args = [
"--cfg",
"__time_03_docs",
+ "--generate-link-to-definition",
]
targets = ["x86_64-unknown-linux-gnu"]
@@ -61,10 +62,22 @@ name = "benchmarks"
path = "../benchmarks/main.rs"
harness = false
+[dependencies.deranged]
+version = "0.3.9"
+features = ["powerfmt"]
+default-features = false
+
[dependencies.itoa]
version = "1.0.1"
optional = true
+[dependencies.num-conv]
+version = "0.1.0"
+
+[dependencies.powerfmt]
+version = "0.2.0"
+default-features = false
+
[dependencies.quickcheck]
version = "1.0.3"
optional = true
@@ -76,17 +89,20 @@ optional = true
default-features = false
[dependencies.serde]
-version = "1.0.126"
+version = "1.0.184"
optional = true
default-features = false
[dependencies.time-core]
-version = "=0.1.1"
+version = "=0.1.2"
[dependencies.time-macros]
-version = "=0.2.10"
+version = "=0.2.18"
optional = true
+[dev-dependencies.num-conv]
+version = "0.1.0"
+
[dev-dependencies.quickcheck_macros]
version = "1.0.0"
@@ -94,8 +110,15 @@ version = "1.0.0"
version = "0.8.4"
default-features = false
+[dev-dependencies.rstest]
+version = "0.18.2"
+default-features = false
+
+[dev-dependencies.rstest_reuse]
+version = "0.6.0"
+
[dev-dependencies.serde]
-version = "1.0.126"
+version = "1.0.184"
features = ["derive"]
default-features = false
@@ -106,7 +129,7 @@ version = "1.0.68"
version = "1.0.126"
[dev-dependencies.time-macros]
-version = "=0.2.10"
+version = "=0.2.18"
[features]
alloc = ["serde?/alloc"]
@@ -127,11 +150,16 @@ parsing = ["time-macros?/parsing"]
quickcheck = [
"dep:quickcheck",
"alloc",
+ "deranged/quickcheck",
+]
+rand = [
+ "dep:rand",
+ "deranged/rand",
]
-rand = ["dep:rand"]
serde = [
"dep:serde",
"time-macros?/serde",
+ "deranged/serde",
]
serde-human-readable = [
"serde",
@@ -143,7 +171,10 @@ serde-well-known = [
"formatting",
"parsing",
]
-std = ["alloc"]
+std = [
+ "alloc",
+ "deranged/std",
+]
wasm-bindgen = ["dep:js-sys"]
[target."cfg(__ui_tests)".dev-dependencies.trybuild]
@@ -154,7 +185,7 @@ version = "0.3.58"
optional = true
[target."cfg(bench)".dev-dependencies.criterion]
-version = "0.4.0"
+version = "0.5.1"
default-features = false
[target."cfg(target_family = \"unix\")".dependencies.libc]
@@ -164,3 +195,87 @@ optional = true
[target."cfg(target_family = \"unix\")".dependencies.num_threads]
version = "0.1.2"
optional = true
+
+[lints.clippy]
+all = "warn"
+alloc-instead-of-core = "deny"
+dbg-macro = "warn"
+decimal-literal-representation = "warn"
+explicit-auto-deref = "warn"
+get-unwrap = "warn"
+manual-let-else = "warn"
+missing-docs-in-private-items = "warn"
+missing-enforced-import-renames = "warn"
+nursery = "warn"
+obfuscated-if-else = "warn"
+print-stdout = "warn"
+semicolon-outside-block = "warn"
+std-instead-of-core = "deny"
+todo = "warn"
+undocumented-unsafe-blocks = "deny"
+unimplemented = "warn"
+uninlined-format-args = "warn"
+unnested-or-patterns = "warn"
+unwrap-in-result = "warn"
+unwrap-used = "warn"
+use-debug = "warn"
+
+[lints.clippy.option-if-let-else]
+level = "allow"
+priority = 1
+
+[lints.clippy.redundant-pub-crate]
+level = "allow"
+priority = 1
+
+[lints.rust]
+ambiguous-glob-reexports = "deny"
+clashing-extern-declarations = "deny"
+const-item-mutation = "deny"
+deref-nullptr = "deny"
+drop-bounds = "deny"
+future-incompatible = "deny"
+hidden-glob-reexports = "deny"
+improper-ctypes = "deny"
+improper-ctypes-definitions = "deny"
+invalid-from-utf8 = "deny"
+invalid-macro-export-arguments = "deny"
+invalid-nan-comparisons = "deny"
+invalid-reference-casting = "deny"
+invalid-value = "deny"
+keyword-idents = "warn"
+let-underscore = "warn"
+macro-use-extern-crate = "warn"
+meta-variable-misuse = "warn"
+missing-abi = "warn"
+missing-copy-implementations = "warn"
+missing-debug-implementations = "warn"
+missing-docs = "warn"
+named-arguments-used-positionally = "deny"
+non-ascii-idents = "deny"
+noop-method-call = "warn"
+opaque-hidden-inferred-bound = "deny"
+overlapping-range-endpoints = "deny"
+single-use-lifetimes = "warn"
+suspicious-double-ref-op = "deny"
+temporary-cstring-as-ptr = "deny"
+trivial-casts = "warn"
+trivial-numeric-casts = "warn"
+unconditional-recursion = "deny"
+unnameable-test-items = "deny"
+unreachable-pub = "warn"
+unsafe-op-in-unsafe-fn = "deny"
+unstable-syntax-pre-expansion = "deny"
+unused = "warn"
+unused-import-braces = "warn"
+unused-lifetimes = "warn"
+unused-qualifications = "warn"
+variant-size-differences = "warn"
+
+[lints.rust.unstable-name-collisions]
+level = "warn"
+priority = 1
+
+[lints.rustdoc]
+private-doc-tests = "warn"
+unescaped-backticks = "warn"
diff --git a/third_party/rust/time/LICENSE-Apache b/third_party/rust/time/LICENSE-Apache
index 7646f21e37..c763a0c9de 100644
--- a/third_party/rust/time/LICENSE-Apache
+++ b/third_party/rust/time/LICENSE-Apache
@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright 2022 Jacob Pratt et al.
+ Copyright 2024 Jacob Pratt et al.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/third_party/rust/time/LICENSE-MIT b/third_party/rust/time/LICENSE-MIT
index a11a755732..5cc097f1c0 100644
--- a/third_party/rust/time/LICENSE-MIT
+++ b/third_party/rust/time/LICENSE-MIT
@@ -1,4 +1,4 @@
-Copyright (c) 2022 Jacob Pratt et al.
+Copyright (c) 2024 Jacob Pratt et al.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/third_party/rust/time/README.md b/third_party/rust/time/README.md
index 173b3c4a2b..b2991f93d8 100644
--- a/third_party/rust/time/README.md
+++ b/third_party/rust/time/README.md
@@ -1,6 +1,6 @@
# time
-[![minimum rustc: 1.65](https://img.shields.io/badge/minimum%20rustc-1.65-yellowgreen?logo=rust&style=flat-square)](https://www.whatrustisit.com)
+[![minimum rustc: 1.67](https://img.shields.io/badge/minimum%20rustc-1.67-yellowgreen?logo=rust&style=flat-square)](https://www.whatrustisit.com)
[![version](https://img.shields.io/crates/v/time?color=blue&logo=rust&style=flat-square)](https://crates.io/crates/time)
[![build status](https://img.shields.io/github/actions/workflow/status/time-rs/time/build.yaml?branch=main&style=flat-square)](https://github.com/time-rs/time/actions)
[![codecov](https://codecov.io/gh/time-rs/time/branch/main/graph/badge.svg?token=yt4XSmQNKQ)](https://codecov.io/gh/time-rs/time)
diff --git a/third_party/rust/time/src/date.rs b/third_party/rust/time/src/date.rs
index ad8565a8e1..ed443c5fd1 100644
--- a/third_party/rust/time/src/date.rs
+++ b/third_party/rust/time/src/date.rs
@@ -1,19 +1,34 @@
//! The [`Date`] struct and its associated `impl`s.
-use core::fmt;
+#[cfg(feature = "formatting")]
+use alloc::string::String;
+use core::num::NonZeroI32;
use core::ops::{Add, Sub};
use core::time::Duration as StdDuration;
+use core::{cmp, fmt};
#[cfg(feature = "formatting")]
use std::io;
+use deranged::RangedI32;
+use num_conv::prelude::*;
+use powerfmt::ext::FormatterExt;
+use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay};
+
use crate::convert::*;
+use crate::ext::DigitCount;
#[cfg(feature = "formatting")]
use crate::formatting::Formattable;
+use crate::internal_macros::{
+ cascade, const_try, const_try_opt, div_floor, ensure_ranged, expect_opt, impl_add_assign,
+ impl_sub_assign,
+};
#[cfg(feature = "parsing")]
use crate::parsing::Parsable;
use crate::util::{days_in_year, days_in_year_month, is_leap_year, weeks_in_year};
use crate::{error, Duration, Month, PrimitiveDateTime, Time, Weekday};
+type Year = RangedI32<MIN_YEAR, MAX_YEAR>;
+
/// The minimum valid year.
pub(crate) const MIN_YEAR: i32 = if cfg!(feature = "large-dates") {
-999_999
@@ -39,32 +54,43 @@ pub struct Date {
// | 2 bits | 21 bits | 9 bits |
// | unassigned | year | ordinal |
// The year is 15 bits when `large-dates` is not enabled.
- value: i32,
+ value: NonZeroI32,
}
impl Date {
/// The minimum valid `Date`.
///
/// The value of this may vary depending on the feature flags enabled.
- pub const MIN: Self = Self::__from_ordinal_date_unchecked(MIN_YEAR, 1);
+ // Safety: `ordinal` is not zero.
+ #[allow(clippy::undocumented_unsafe_blocks)]
+ pub const MIN: Self = unsafe { Self::__from_ordinal_date_unchecked(MIN_YEAR, 1) };
/// The maximum valid `Date`.
///
/// The value of this may vary depending on the feature flags enabled.
- pub const MAX: Self = Self::__from_ordinal_date_unchecked(MAX_YEAR, days_in_year(MAX_YEAR));
+ // Safety: `ordinal` is not zero.
+ #[allow(clippy::undocumented_unsafe_blocks)]
+ pub const MAX: Self =
+ unsafe { Self::__from_ordinal_date_unchecked(MAX_YEAR, days_in_year(MAX_YEAR)) };
// region: constructors
/// Construct a `Date` from the year and ordinal values, the validity of which must be
/// guaranteed by the caller.
+ ///
+ /// # Safety
+ ///
+ /// `ordinal` must not be zero. `year` should be in the range `MIN_YEAR..=MAX_YEAR`, but this
+ /// is not a safety invariant.
#[doc(hidden)]
- pub const fn __from_ordinal_date_unchecked(year: i32, ordinal: u16) -> Self {
+ pub const unsafe fn __from_ordinal_date_unchecked(year: i32, ordinal: u16) -> Self {
debug_assert!(year >= MIN_YEAR);
debug_assert!(year <= MAX_YEAR);
debug_assert!(ordinal != 0);
debug_assert!(ordinal <= days_in_year(year));
Self {
- value: (year << 9) | ordinal as i32,
+ // Safety: The caller must guarantee that `ordinal` is not zero.
+ value: unsafe { NonZeroI32::new_unchecked((year << 9) | ordinal as i32) },
}
}
@@ -91,14 +117,29 @@ impl Date {
[0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335],
];
- ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR);
- ensure_value_in_range!(day conditionally in 1 => days_in_year_month(year, month));
+ ensure_ranged!(Year: year);
+ match day {
+ 1..=28 => {}
+ 29..=31 if day <= days_in_year_month(year, month) => {}
+ _ => {
+ return Err(error::ComponentRange {
+ name: "day",
+ minimum: 1,
+ maximum: days_in_year_month(year, month) as _,
+ value: day as _,
+ conditional_range: true,
+ });
+ }
+ }
- Ok(Self::__from_ordinal_date_unchecked(
- year,
- DAYS_CUMULATIVE_COMMON_LEAP[is_leap_year(year) as usize][month as usize - 1]
- + day as u16,
- ))
+ // Safety: `ordinal` is not zero.
+ Ok(unsafe {
+ Self::__from_ordinal_date_unchecked(
+ year,
+ DAYS_CUMULATIVE_COMMON_LEAP[is_leap_year(year) as usize][month as usize - 1]
+ + day as u16,
+ )
+ })
}
/// Attempt to create a `Date` from the year and ordinal day number.
@@ -114,9 +155,23 @@ impl Date {
/// assert!(Date::from_ordinal_date(2019, 366).is_err()); // 2019 isn't a leap year.
/// ```
pub const fn from_ordinal_date(year: i32, ordinal: u16) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR);
- ensure_value_in_range!(ordinal conditionally in 1 => days_in_year(year));
- Ok(Self::__from_ordinal_date_unchecked(year, ordinal))
+ ensure_ranged!(Year: year);
+ match ordinal {
+ 1..=365 => {}
+ 366 if is_leap_year(year) => {}
+ _ => {
+ return Err(error::ComponentRange {
+ name: "ordinal",
+ minimum: 1,
+ maximum: days_in_year(year) as _,
+ value: ordinal as _,
+ conditional_range: true,
+ });
+ }
+ }
+
+ // Safety: `ordinal` is not zero.
+ Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) })
}
/// Attempt to create a `Date` from the ISO year, week, and weekday.
@@ -137,8 +192,20 @@ impl Date {
week: u8,
weekday: Weekday,
) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR);
- ensure_value_in_range!(week conditionally in 1 => weeks_in_year(year));
+ ensure_ranged!(Year: year);
+ match week {
+ 1..=52 => {}
+ 53 if week <= weeks_in_year(year) => {}
+ _ => {
+ return Err(error::ComponentRange {
+ name: "week",
+ minimum: 1,
+ maximum: weeks_in_year(year) as _,
+ value: week as _,
+ conditional_range: true,
+ });
+ }
+ }
let adj_year = year - 1;
let raw = 365 * adj_year + div_floor!(adj_year, 4) - div_floor!(adj_year, 100)
@@ -155,14 +222,21 @@ impl Date {
let ordinal = week as i16 * 7 + weekday.number_from_monday() as i16 - jan_4;
Ok(if ordinal <= 0 {
- Self::__from_ordinal_date_unchecked(
- year - 1,
- (ordinal as u16).wrapping_add(days_in_year(year - 1)),
- )
+ // Safety: `ordinal` is not zero.
+ unsafe {
+ Self::__from_ordinal_date_unchecked(
+ year - 1,
+ (ordinal as u16).wrapping_add(days_in_year(year - 1)),
+ )
+ }
} else if ordinal > days_in_year(year) as i16 {
- Self::__from_ordinal_date_unchecked(year + 1, ordinal as u16 - days_in_year(year))
+ // Safety: `ordinal` is not zero.
+ unsafe {
+ Self::__from_ordinal_date_unchecked(year + 1, ordinal as u16 - days_in_year(year))
+ }
} else {
- Self::__from_ordinal_date_unchecked(year, ordinal as _)
+ // Safety: `ordinal` is not zero.
+ unsafe { Self::__from_ordinal_date_unchecked(year, ordinal as _) }
})
}
@@ -181,9 +255,8 @@ impl Date {
/// ```
#[doc(alias = "from_julian_date")]
pub const fn from_julian_day(julian_day: i32) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(
- julian_day in Self::MIN.to_julian_day() => Self::MAX.to_julian_day()
- );
+ type JulianDay = RangedI32<{ Date::MIN.to_julian_day() }, { Date::MAX.to_julian_day() }>;
+ ensure_ranged!(JulianDay: julian_day);
Ok(Self::from_julian_day_unchecked(julian_day))
}
@@ -223,7 +296,8 @@ impl Date {
cascade!(ordinal in 1..366 => year);
}
- Self::__from_ordinal_date_unchecked(year, ordinal)
+ // Safety: `ordinal` is not zero.
+ unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) }
}
// endregion constructors
@@ -237,7 +311,7 @@ impl Date {
/// assert_eq!(date!(2020 - 01 - 01).year(), 2020);
/// ```
pub const fn year(self) -> i32 {
- self.value >> 9
+ self.value.get() >> 9
}
/// Get the month.
@@ -316,7 +390,7 @@ impl Date {
/// assert_eq!(date!(2019 - 12 - 31).ordinal(), 365);
/// ```
pub const fn ordinal(self) -> u16 {
- (self.value & 0x1FF) as _
+ (self.value.get() & 0x1FF) as _
}
/// Get the ISO 8601 year and week number.
@@ -483,14 +557,16 @@ impl Date {
/// ```
pub const fn next_day(self) -> Option<Self> {
if self.ordinal() == 366 || (self.ordinal() == 365 && !is_leap_year(self.year())) {
- if self.value == Self::MAX.value {
+ if self.value.get() == Self::MAX.value.get() {
None
} else {
- Some(Self::__from_ordinal_date_unchecked(self.year() + 1, 1))
+ // Safety: `ordinal` is not zero.
+ unsafe { Some(Self::__from_ordinal_date_unchecked(self.year() + 1, 1)) }
}
} else {
Some(Self {
- value: self.value + 1,
+ // Safety: `ordinal` is not zero.
+ value: unsafe { NonZeroI32::new_unchecked(self.value.get() + 1) },
})
}
}
@@ -517,15 +593,16 @@ impl Date {
pub const fn previous_day(self) -> Option<Self> {
if self.ordinal() != 1 {
Some(Self {
- value: self.value - 1,
+ // Safety: `ordinal` is not zero.
+ value: unsafe { NonZeroI32::new_unchecked(self.value.get() - 1) },
})
- } else if self.value == Self::MIN.value {
+ } else if self.value.get() == Self::MIN.value.get() {
None
} else {
- Some(Self::__from_ordinal_date_unchecked(
- self.year() - 1,
- days_in_year(self.year() - 1),
- ))
+ // Safety: `ordinal` is not zero.
+ Some(unsafe {
+ Self::__from_ordinal_date_unchecked(self.year() - 1, days_in_year(self.year() - 1))
+ })
}
}
@@ -697,6 +774,49 @@ impl Date {
}
}
+ /// Computes `self + duration`, returning `None` if an overflow occurred.
+ ///
+ /// ```rust
+ /// # use time::{Date, ext::NumericalStdDuration};
+ /// # use time_macros::date;
+ /// assert_eq!(Date::MAX.checked_add_std(1.std_days()), None);
+ /// assert_eq!(
+ /// date!(2020 - 12 - 31).checked_add_std(2.std_days()),
+ /// Some(date!(2021 - 01 - 02))
+ /// );
+ /// ```
+ ///
+ /// # Note
+ ///
+ /// This function only takes whole days into account.
+ ///
+ /// ```rust
+ /// # use time::{Date, ext::NumericalStdDuration};
+ /// # use time_macros::date;
+ /// assert_eq!(Date::MAX.checked_add_std(23.std_hours()), Some(Date::MAX));
+ /// assert_eq!(
+ /// date!(2020 - 12 - 31).checked_add_std(23.std_hours()),
+ /// Some(date!(2020 - 12 - 31))
+ /// );
+ /// assert_eq!(
+ /// date!(2020 - 12 - 31).checked_add_std(47.std_hours()),
+ /// Some(date!(2021 - 01 - 01))
+ /// );
+ /// ```
+ pub const fn checked_add_std(self, duration: StdDuration) -> Option<Self> {
+ let whole_days = duration.as_secs() / Second::per(Day) as u64;
+ if whole_days > i32::MAX as u64 {
+ return None;
+ }
+
+ let julian_day = const_try_opt!(self.to_julian_day().checked_add(whole_days as _));
+ if let Ok(date) = Self::from_julian_day(julian_day) {
+ Some(date)
+ } else {
+ None
+ }
+ }
+
/// Computes `self - duration`, returning `None` if an overflow occurred.
///
/// ```
@@ -742,6 +862,49 @@ impl Date {
}
}
+ /// Computes `self - duration`, returning `None` if an overflow occurred.
+ ///
+ /// ```
+ /// # use time::{Date, ext::NumericalStdDuration};
+ /// # use time_macros::date;
+ /// assert_eq!(Date::MIN.checked_sub_std(1.std_days()), None);
+ /// assert_eq!(
+ /// date!(2020 - 12 - 31).checked_sub_std(2.std_days()),
+ /// Some(date!(2020 - 12 - 29))
+ /// );
+ /// ```
+ ///
+ /// # Note
+ ///
+ /// This function only takes whole days into account.
+ ///
+ /// ```
+ /// # use time::{Date, ext::NumericalStdDuration};
+ /// # use time_macros::date;
+ /// assert_eq!(Date::MIN.checked_sub_std(23.std_hours()), Some(Date::MIN));
+ /// assert_eq!(
+ /// date!(2020 - 12 - 31).checked_sub_std(23.std_hours()),
+ /// Some(date!(2020 - 12 - 31))
+ /// );
+ /// assert_eq!(
+ /// date!(2020 - 12 - 31).checked_sub_std(47.std_hours()),
+ /// Some(date!(2020 - 12 - 30))
+ /// );
+ /// ```
+ pub const fn checked_sub_std(self, duration: StdDuration) -> Option<Self> {
+ let whole_days = duration.as_secs() / Second::per(Day) as u64;
+ if whole_days > i32::MAX as u64 {
+ return None;
+ }
+
+ let julian_day = const_try_opt!(self.to_julian_day().checked_sub(whole_days as _));
+ if let Ok(date) = Self::from_julian_day(julian_day) {
+ Some(date)
+ } else {
+ None
+ }
+ }
+
/// Calculates the first occurrence of a weekday that is strictly later than a given `Date`.
/// Returns `None` if an overflow occurred.
pub(crate) const fn checked_next_occurrence(self, weekday: Weekday) -> Option<Self> {
@@ -787,12 +950,8 @@ impl Date {
return None;
}
- let next_occ = self.checked_next_occurrence(weekday);
- if let Some(val) = next_occ {
- val.checked_add(Duration::weeks(n as i64 - 1))
- } else {
- None
- }
+ const_try_opt!(self.checked_next_occurrence(weekday))
+ .checked_add(Duration::weeks(n as i64 - 1))
}
/// Calculates the `n`th occurrence of a weekday that is strictly earlier than a given `Date`.
@@ -802,12 +961,8 @@ impl Date {
return None;
}
- let next_occ = self.checked_prev_occurrence(weekday);
- if let Some(val) = next_occ {
- val.checked_sub(Duration::weeks(n as i64 - 1))
- } else {
- None
- }
+ const_try_opt!(self.checked_prev_occurrence(weekday))
+ .checked_sub(Duration::weeks(n as i64 - 1))
}
// endregion: checked arithmetic
@@ -907,17 +1062,21 @@ impl Date {
/// ```
#[must_use = "This method does not mutate the original `Date`."]
pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR);
+ ensure_ranged!(Year: year);
let ordinal = self.ordinal();
// Dates in January and February are unaffected by leap years.
if ordinal <= 59 {
- return Ok(Self::__from_ordinal_date_unchecked(year, ordinal));
+ // Safety: `ordinal` is not zero.
+ return Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) });
}
match (is_leap_year(self.year()), is_leap_year(year)) {
- (false, false) | (true, true) => Ok(Self::__from_ordinal_date_unchecked(year, ordinal)),
+ (false, false) | (true, true) => {
+ // Safety: `ordinal` is not zero.
+ Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) })
+ }
// February 29 does not exist in common years.
(true, false) if ordinal == 60 => Err(error::ComponentRange {
name: "day",
@@ -928,10 +1087,12 @@ impl Date {
}),
// We're going from a common year to a leap year. Shift dates in March and later by
// one day.
- (false, true) => Ok(Self::__from_ordinal_date_unchecked(year, ordinal + 1)),
+ // Safety: `ordinal` is not zero.
+ (false, true) => Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal + 1) }),
// We're going from a leap year to a common year. Shift dates in January and
// February by one day.
- (true, false) => Ok(Self::__from_ordinal_date_unchecked(year, ordinal - 1)),
+ // Safety: `ordinal` is not zero.
+ (true, false) => Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal - 1) }),
}
}
@@ -969,17 +1130,55 @@ impl Date {
/// ```
#[must_use = "This method does not mutate the original `Date`."]
pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> {
- // Days 1-28 are present in every month, so we can skip checking.
- if day == 0 || day >= 29 {
- ensure_value_in_range!(
- day conditionally in 1 => days_in_year_month(self.year(), self.month())
- );
+ match day {
+ 1..=28 => {}
+ 29..=31 if day <= days_in_year_month(self.year(), self.month()) => {}
+ _ => {
+ return Err(error::ComponentRange {
+ name: "day",
+ minimum: 1,
+ maximum: days_in_year_month(self.year(), self.month()) as _,
+ value: day as _,
+ conditional_range: true,
+ });
+ }
}
- Ok(Self::__from_ordinal_date_unchecked(
- self.year(),
- (self.ordinal() as i16 - self.day() as i16 + day as i16) as _,
- ))
+ // Safety: `ordinal` is not zero.
+ Ok(unsafe {
+ Self::__from_ordinal_date_unchecked(
+ self.year(),
+ (self.ordinal() as i16 - self.day() as i16 + day as i16) as _,
+ )
+ })
+ }
+
+ /// Replace the day of the year.
+ ///
+ /// ```rust
+ /// # use time_macros::date;
+ /// assert_eq!(date!(2022 - 049).replace_ordinal(1), Ok(date!(2022 - 001)));
+ /// assert!(date!(2022 - 049).replace_ordinal(0).is_err()); // 0 isn't a valid ordinal
+ /// assert!(date!(2022 - 049).replace_ordinal(366).is_err()); // 2022 isn't a leap year
+ /// ````
+ #[must_use = "This method does not mutate the original `Date`."]
+ pub const fn replace_ordinal(self, ordinal: u16) -> Result<Self, error::ComponentRange> {
+ match ordinal {
+ 1..=365 => {}
+ 366 if is_leap_year(self.year()) => {}
+ _ => {
+ return Err(error::ComponentRange {
+ name: "ordinal",
+ minimum: 1,
+ maximum: days_in_year(self.year()) as _,
+ value: ordinal as _,
+ conditional_range: true,
+ });
+ }
+ }
+
+ // Safety: `ordinal` is in range.
+ Ok(unsafe { Self::__from_ordinal_date_unchecked(self.year(), ordinal) })
}
// endregion replacement
}
@@ -1138,29 +1337,92 @@ impl Date {
}
}
-impl fmt::Display for Date {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- if cfg!(feature = "large-dates") && self.year().abs() >= 10_000 {
- write!(
- f,
- "{:+}-{:02}-{:02}",
- self.year(),
- self.month() as u8,
- self.day()
+mod private {
+ #[non_exhaustive]
+ #[derive(Debug, Clone, Copy)]
+ pub struct DateMetadata {
+ /// The width of the year component, including the sign.
+ pub(super) year_width: u8,
+ /// Whether the sign should be displayed.
+ pub(super) display_sign: bool,
+ pub(super) year: i32,
+ pub(super) month: u8,
+ pub(super) day: u8,
+ }
+}
+use private::DateMetadata;
+
+impl SmartDisplay for Date {
+ type Metadata = DateMetadata;
+
+ fn metadata(&self, _: FormatterOptions) -> Metadata<Self> {
+ let (year, month, day) = self.to_calendar_date();
+
+ // There is a minimum of four digits for any year.
+ let mut year_width = cmp::max(year.unsigned_abs().num_digits(), 4);
+ let display_sign = if !(0..10_000).contains(&year) {
+ // An extra character is required for the sign.
+ year_width += 1;
+ true
+ } else {
+ false
+ };
+
+ let formatted_width = year_width.extend::<usize>()
+ + smart_display::padded_width_of!(
+ "-",
+ u8::from(month) => width(2),
+ "-",
+ day => width(2),
+ );
+
+ Metadata::new(
+ formatted_width,
+ self,
+ DateMetadata {
+ year_width,
+ display_sign,
+ year,
+ month: u8::from(month),
+ day,
+ },
+ )
+ }
+
+ fn fmt_with_metadata(
+ &self,
+ f: &mut fmt::Formatter<'_>,
+ metadata: Metadata<Self>,
+ ) -> fmt::Result {
+ let DateMetadata {
+ year_width,
+ display_sign,
+ year,
+ month,
+ day,
+ } = *metadata;
+ let year_width = year_width.extend();
+
+ if display_sign {
+ f.pad_with_width(
+ metadata.unpadded_width(),
+ format_args!("{year:+0year_width$}-{month:02}-{day:02}"),
)
} else {
- write!(
- f,
- "{:0width$}-{:02}-{:02}",
- self.year(),
- self.month() as u8,
- self.day(),
- width = 4 + (self.year() < 0) as usize
+ f.pad_with_width(
+ metadata.unpadded_width(),
+ format_args!("{year:0year_width$}-{month:02}-{day:02}"),
)
}
}
}
+impl fmt::Display for Date {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ SmartDisplay::fmt(self, f)
+ }
+}
+
impl fmt::Debug for Date {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt::Display::fmt(self, f)
@@ -1172,6 +1434,9 @@ impl fmt::Debug for Date {
impl Add<Duration> for Date {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn add(self, duration: Duration) -> Self::Output {
self.checked_add(duration)
.expect("overflow adding duration to date")
@@ -1181,11 +1446,12 @@ impl Add<Duration> for Date {
impl Add<StdDuration> for Date {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn add(self, duration: StdDuration) -> Self::Output {
- Self::from_julian_day(
- self.to_julian_day() + (duration.as_secs() / Second.per(Day) as u64) as i32,
- )
- .expect("overflow adding duration to date")
+ self.checked_add_std(duration)
+ .expect("overflow adding duration to date")
}
}
@@ -1194,6 +1460,9 @@ impl_add_assign!(Date: Duration, StdDuration);
impl Sub<Duration> for Date {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, duration: Duration) -> Self::Output {
self.checked_sub(duration)
.expect("overflow subtracting duration from date")
@@ -1203,11 +1472,12 @@ impl Sub<Duration> for Date {
impl Sub<StdDuration> for Date {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, duration: StdDuration) -> Self::Output {
- Self::from_julian_day(
- self.to_julian_day() - (duration.as_secs() / Second.per(Day) as u64) as i32,
- )
- .expect("overflow subtracting duration from date")
+ self.checked_sub_std(duration)
+ .expect("overflow subtracting duration from date")
}
}
@@ -1217,7 +1487,7 @@ impl Sub for Date {
type Output = Duration;
fn sub(self, other: Self) -> Self::Output {
- Duration::days((self.to_julian_day() - other.to_julian_day()) as _)
+ Duration::days((self.to_julian_day() - other.to_julian_day()).extend())
}
}
// endregion trait impls
diff --git a/third_party/rust/time/src/date_time.rs b/third_party/rust/time/src/date_time.rs
deleted file mode 100644
index f4730f502d..0000000000
--- a/third_party/rust/time/src/date_time.rs
+++ /dev/null
@@ -1,1180 +0,0 @@
-//! The [`DateTime`] struct and its associated `impl`s.
-
-// TODO(jhpratt) Document everything before making public.
-#![allow(clippy::missing_docs_in_private_items)]
-// This is intentional, as the struct will likely be exposed at some point.
-#![allow(unreachable_pub)]
-
-use core::cmp::Ordering;
-use core::fmt;
-use core::hash::{Hash, Hasher};
-use core::mem::size_of;
-use core::ops::{Add, AddAssign, Sub, SubAssign};
-use core::time::Duration as StdDuration;
-#[cfg(feature = "formatting")]
-use std::io;
-#[cfg(feature = "std")]
-use std::time::SystemTime;
-
-use crate::convert::*;
-use crate::date::{MAX_YEAR, MIN_YEAR};
-#[cfg(feature = "formatting")]
-use crate::formatting::Formattable;
-#[cfg(feature = "parsing")]
-use crate::parsing::{Parsable, Parsed};
-use crate::{error, util, Date, Duration, Month, Time, UtcOffset, Weekday};
-
-#[allow(missing_debug_implementations, missing_copy_implementations)]
-pub(crate) mod offset_kind {
- pub enum None {}
- pub enum Fixed {}
-}
-
-pub(crate) use sealed::MaybeOffset;
-use sealed::*;
-mod sealed {
- use super::*;
-
- /// A type that is guaranteed to be either `()` or [`UtcOffset`].
- ///
- /// **Do not** add any additional implementations of this trait.
- #[allow(unreachable_pub)] // intentional
- pub trait MaybeOffsetType {}
- impl MaybeOffsetType for () {}
- impl MaybeOffsetType for UtcOffset {}
-
- pub trait MaybeOffset: Sized {
- /// The offset type as it is stored in memory.
- #[cfg(feature = "quickcheck")]
- type MemoryOffsetType: Copy + MaybeOffsetType + quickcheck::Arbitrary;
- #[cfg(not(feature = "quickcheck"))]
- type MemoryOffsetType: Copy + MaybeOffsetType;
-
- /// The offset type as it should be thought about.
- ///
- /// For example, a `DateTime<Utc>` has a logical offset type of [`UtcOffset`], but does not
- /// actually store an offset in memory.
- type LogicalOffsetType: Copy + MaybeOffsetType;
-
- /// Required to be `Self`. Used for bound equality.
- type Self_;
-
- /// True if and only if `Self::LogicalOffsetType` is `UtcOffset`.
- const HAS_LOGICAL_OFFSET: bool =
- size_of::<Self::LogicalOffsetType>() == size_of::<UtcOffset>();
- /// True if and only if `Self::MemoryOffsetType` is `UtcOffset`.
- const HAS_MEMORY_OFFSET: bool =
- size_of::<Self::MemoryOffsetType>() == size_of::<UtcOffset>();
-
- /// `Some` if and only if the logical UTC offset is statically known.
- // TODO(jhpratt) When const trait impls are stable, this can be removed in favor of
- // `.as_offset_opt()`.
- const STATIC_OFFSET: Option<UtcOffset>;
-
- #[cfg(feature = "parsing")]
- fn try_from_parsed(parsed: Parsed) -> Result<Self::MemoryOffsetType, error::TryFromParsed>;
- }
-
- // Traits to indicate whether a `MaybeOffset` has a logical offset type of `UtcOffset` or not.
-
- pub trait HasLogicalOffset: MaybeOffset<LogicalOffsetType = UtcOffset> {}
- impl<T: MaybeOffset<LogicalOffsetType = UtcOffset>> HasLogicalOffset for T {}
-
- pub trait NoLogicalOffset: MaybeOffset<LogicalOffsetType = ()> {}
- impl<T: MaybeOffset<LogicalOffsetType = ()>> NoLogicalOffset for T {}
-
- // Traits to indicate whether a `MaybeOffset` has a memory offset type of `UtcOffset` or not.
-
- pub trait HasMemoryOffset: MaybeOffset<MemoryOffsetType = UtcOffset> {}
- impl<T: MaybeOffset<MemoryOffsetType = UtcOffset>> HasMemoryOffset for T {}
-
- pub trait NoMemoryOffset: MaybeOffset<MemoryOffsetType = ()> {}
- impl<T: MaybeOffset<MemoryOffsetType = ()>> NoMemoryOffset for T {}
-
- // Traits to indicate backing type being implemented.
-
- pub trait IsOffsetKindNone:
- MaybeOffset<Self_ = offset_kind::None, MemoryOffsetType = (), LogicalOffsetType = ()>
- {
- }
- impl IsOffsetKindNone for offset_kind::None {}
-
- pub trait IsOffsetKindFixed:
- MaybeOffset<
- Self_ = offset_kind::Fixed,
- MemoryOffsetType = UtcOffset,
- LogicalOffsetType = UtcOffset,
- >
- {
- }
- impl IsOffsetKindFixed for offset_kind::Fixed {}
-}
-
-impl MaybeOffset for offset_kind::None {
- type MemoryOffsetType = ();
- type LogicalOffsetType = ();
-
- type Self_ = Self;
-
- const STATIC_OFFSET: Option<UtcOffset> = None;
-
- #[cfg(feature = "parsing")]
- fn try_from_parsed(_: Parsed) -> Result<(), error::TryFromParsed> {
- Ok(())
- }
-}
-
-impl MaybeOffset for offset_kind::Fixed {
- type MemoryOffsetType = UtcOffset;
- type LogicalOffsetType = UtcOffset;
-
- type Self_ = Self;
-
- const STATIC_OFFSET: Option<UtcOffset> = None;
-
- #[cfg(feature = "parsing")]
- fn try_from_parsed(parsed: Parsed) -> Result<UtcOffset, error::TryFromParsed> {
- parsed.try_into()
- }
-}
-
-// region: const trait method hacks
-// TODO(jhpratt) When const trait impls are stable, these methods can be removed in favor of methods
-// in `MaybeOffset`, which would then be made `const`.
-const fn maybe_offset_as_offset_opt<O: MaybeOffset>(
- offset: O::MemoryOffsetType,
-) -> Option<UtcOffset> {
- if O::STATIC_OFFSET.is_some() {
- O::STATIC_OFFSET
- } else if O::HAS_MEMORY_OFFSET {
- #[repr(C)] // needed to guarantee they align at the start
- union Convert<O: MaybeOffset> {
- input: O::MemoryOffsetType,
- output: UtcOffset,
- }
-
- // Safety: `O::HAS_OFFSET` indicates that `O::Offset` is `UtcOffset`. This code effectively
- // performs a transmute from `O::Offset` to `UtcOffset`, which we know is the same type.
- Some(unsafe { Convert::<O> { input: offset }.output })
- } else {
- None
- }
-}
-
-const fn maybe_offset_as_offset<O: MaybeOffset + HasLogicalOffset>(
- offset: O::MemoryOffsetType,
-) -> UtcOffset {
- match maybe_offset_as_offset_opt::<O>(offset) {
- Some(offset) => offset,
- None => bug!("`MaybeOffset::as_offset` called on a type without an offset in memory"),
- }
-}
-
-pub(crate) const fn maybe_offset_from_offset<O: MaybeOffset>(
- offset: UtcOffset,
-) -> O::MemoryOffsetType {
- #[repr(C)] // needed to guarantee the types align at the start
- union Convert<O: MaybeOffset> {
- input: UtcOffset,
- output: O::MemoryOffsetType,
- }
-
- // Safety: It is statically known that there are only two possibilities due to the trait bound
- // of `O::MemoryOffsetType`, which ultimately relies on `MaybeOffsetType`. The two possibilities
- // are:
- // 1. UtcOffset -> UtcOffset
- // 2. UtcOffset -> ()
- // (1) is valid because it is an identity conversion, which is always valid. (2) is valid
- // because `()` is a 1-ZST, so converting to it is always valid.
- unsafe { Convert::<O> { input: offset }.output }
-}
-// endregion const trait methods hacks
-
-/// The Julian day of the Unix epoch.
-const UNIX_EPOCH_JULIAN_DAY: i32 = Date::__from_ordinal_date_unchecked(1970, 1).to_julian_day();
-
-pub struct DateTime<O: MaybeOffset> {
- pub(crate) date: Date,
- pub(crate) time: Time,
- pub(crate) offset: O::MemoryOffsetType,
-}
-
-// Manual impl to remove extraneous bounds.
-impl<O: MaybeOffset> Clone for DateTime<O> {
- fn clone(&self) -> Self {
- *self
- }
-}
-
-// Manual impl to remove extraneous bounds.
-impl<O: MaybeOffset> Copy for DateTime<O> {}
-
-// region: constructors
-impl DateTime<offset_kind::None> {
- pub const MIN: Self = Self {
- date: Date::MIN,
- time: Time::MIN,
- offset: (),
- };
-
- pub const MAX: Self = Self {
- date: Date::MAX,
- time: Time::MAX,
- offset: (),
- };
-}
-
-impl DateTime<offset_kind::Fixed> {
- pub const UNIX_EPOCH: Self = Self {
- date: Date::__from_ordinal_date_unchecked(1970, 1),
- time: Time::MIDNIGHT,
- offset: UtcOffset::UTC,
- };
-}
-
-impl<O: MaybeOffset> DateTime<O> {
- pub const fn new(date: Date, time: Time) -> Self
- where
- O: IsOffsetKindNone,
- {
- Self {
- date,
- time,
- offset: (),
- }
- }
-
- pub const fn from_unix_timestamp(timestamp: i64) -> Result<Self, error::ComponentRange>
- where
- O: HasLogicalOffset,
- {
- #[allow(clippy::missing_docs_in_private_items)]
- const MIN_TIMESTAMP: i64 = Date::MIN.midnight().assume_utc().unix_timestamp();
- #[allow(clippy::missing_docs_in_private_items)]
- const MAX_TIMESTAMP: i64 = Date::MAX
- .with_time(Time::__from_hms_nanos_unchecked(23, 59, 59, 999_999_999))
- .assume_utc()
- .unix_timestamp();
-
- ensure_value_in_range!(timestamp in MIN_TIMESTAMP => MAX_TIMESTAMP);
-
- // Use the unchecked method here, as the input validity has already been verified.
- let date = Date::from_julian_day_unchecked(
- UNIX_EPOCH_JULIAN_DAY + div_floor!(timestamp, Second.per(Day) as i64) as i32,
- );
-
- let seconds_within_day = timestamp.rem_euclid(Second.per(Day) as _);
- let time = Time::__from_hms_nanos_unchecked(
- (seconds_within_day / Second.per(Hour) as i64) as _,
- ((seconds_within_day % Second.per(Hour) as i64) / Minute.per(Hour) as i64) as _,
- (seconds_within_day % Second.per(Minute) as i64) as _,
- 0,
- );
-
- Ok(Self {
- date,
- time,
- offset: maybe_offset_from_offset::<O>(UtcOffset::UTC),
- })
- }
-
- pub const fn from_unix_timestamp_nanos(timestamp: i128) -> Result<Self, error::ComponentRange>
- where
- O: HasLogicalOffset,
- {
- let datetime = const_try!(Self::from_unix_timestamp(div_floor!(
- timestamp,
- Nanosecond.per(Second) as i128
- ) as i64));
-
- Ok(Self {
- date: datetime.date,
- time: Time::__from_hms_nanos_unchecked(
- datetime.hour(),
- datetime.minute(),
- datetime.second(),
- timestamp.rem_euclid(Nanosecond.per(Second) as _) as u32,
- ),
- offset: maybe_offset_from_offset::<O>(UtcOffset::UTC),
- })
- }
- // endregion constructors
-
- // region: now
- // The return type will likely be loosened once `ZonedDateTime` is implemented. This is not a
- // breaking change calls are currently limited to only `OffsetDateTime`.
- #[cfg(feature = "std")]
- pub fn now_utc() -> DateTime<offset_kind::Fixed>
- where
- O: IsOffsetKindFixed,
- {
- #[cfg(all(
- target_family = "wasm",
- not(any(target_os = "emscripten", target_os = "wasi")),
- feature = "wasm-bindgen"
- ))]
- {
- js_sys::Date::new_0().into()
- }
-
- #[cfg(not(all(
- target_family = "wasm",
- not(any(target_os = "emscripten", target_os = "wasi")),
- feature = "wasm-bindgen"
- )))]
- SystemTime::now().into()
- }
-
- // The return type will likely be loosened once `ZonedDateTime` is implemented. This is not a
- // breaking change calls are currently limited to only `OffsetDateTime`.
- #[cfg(feature = "local-offset")]
- pub fn now_local() -> Result<DateTime<offset_kind::Fixed>, error::IndeterminateOffset>
- where
- O: IsOffsetKindFixed,
- {
- let t = DateTime::<offset_kind::Fixed>::now_utc();
- Ok(t.to_offset(UtcOffset::local_offset_at(crate::OffsetDateTime(t))?))
- }
- // endregion now
-
- // region: getters
- // region: component getters
- pub const fn date(self) -> Date {
- self.date
- }
-
- pub const fn time(self) -> Time {
- self.time
- }
-
- pub const fn offset(self) -> UtcOffset
- where
- O: HasLogicalOffset,
- {
- maybe_offset_as_offset::<O>(self.offset)
- }
- // endregion component getters
-
- // region: date getters
- pub const fn year(self) -> i32 {
- self.date.year()
- }
-
- pub const fn month(self) -> Month {
- self.date.month()
- }
-
- pub const fn day(self) -> u8 {
- self.date.day()
- }
-
- pub const fn ordinal(self) -> u16 {
- self.date.ordinal()
- }
-
- pub const fn iso_week(self) -> u8 {
- self.date.iso_week()
- }
-
- pub const fn sunday_based_week(self) -> u8 {
- self.date.sunday_based_week()
- }
-
- pub const fn monday_based_week(self) -> u8 {
- self.date.monday_based_week()
- }
-
- pub const fn to_calendar_date(self) -> (i32, Month, u8) {
- self.date.to_calendar_date()
- }
-
- pub const fn to_ordinal_date(self) -> (i32, u16) {
- self.date.to_ordinal_date()
- }
-
- pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) {
- self.date.to_iso_week_date()
- }
-
- pub const fn weekday(self) -> Weekday {
- self.date.weekday()
- }
-
- pub const fn to_julian_day(self) -> i32 {
- self.date.to_julian_day()
- }
- // endregion date getters
-
- // region: time getters
- pub const fn as_hms(self) -> (u8, u8, u8) {
- self.time.as_hms()
- }
-
- pub const fn as_hms_milli(self) -> (u8, u8, u8, u16) {
- self.time.as_hms_milli()
- }
-
- pub const fn as_hms_micro(self) -> (u8, u8, u8, u32) {
- self.time.as_hms_micro()
- }
-
- pub const fn as_hms_nano(self) -> (u8, u8, u8, u32) {
- self.time.as_hms_nano()
- }
-
- pub const fn hour(self) -> u8 {
- self.time.hour()
- }
-
- pub const fn minute(self) -> u8 {
- self.time.minute()
- }
-
- pub const fn second(self) -> u8 {
- self.time.second()
- }
-
- pub const fn millisecond(self) -> u16 {
- self.time.millisecond()
- }
-
- pub const fn microsecond(self) -> u32 {
- self.time.microsecond()
- }
-
- pub const fn nanosecond(self) -> u32 {
- self.time.nanosecond()
- }
- // endregion time getters
-
- // region: unix timestamp getters
- pub const fn unix_timestamp(self) -> i64
- where
- O: HasLogicalOffset,
- {
- let offset = maybe_offset_as_offset::<O>(self.offset).whole_seconds() as i64;
-
- let days =
- (self.to_julian_day() as i64 - UNIX_EPOCH_JULIAN_DAY as i64) * Second.per(Day) as i64;
- let hours = self.hour() as i64 * Second.per(Hour) as i64;
- let minutes = self.minute() as i64 * Second.per(Minute) as i64;
- let seconds = self.second() as i64;
- days + hours + minutes + seconds - offset
- }
-
- pub const fn unix_timestamp_nanos(self) -> i128
- where
- O: HasLogicalOffset,
- {
- self.unix_timestamp() as i128 * Nanosecond.per(Second) as i128 + self.nanosecond() as i128
- }
- // endregion unix timestamp getters
- // endregion: getters
-
- // region: attach offset
- pub const fn assume_offset(self, offset: UtcOffset) -> DateTime<offset_kind::Fixed>
- where
- O: NoLogicalOffset,
- {
- DateTime {
- date: self.date,
- time: self.time,
- offset,
- }
- }
-
- pub const fn assume_utc(self) -> DateTime<offset_kind::Fixed>
- where
- O: NoLogicalOffset,
- {
- self.assume_offset(UtcOffset::UTC)
- }
- // endregion attach offset
-
- // region: to offset
- pub const fn to_offset(self, offset: UtcOffset) -> DateTime<offset_kind::Fixed>
- where
- O: HasLogicalOffset,
- {
- expect_opt!(
- self.checked_to_offset(offset),
- "local datetime out of valid range"
- )
- }
-
- pub const fn checked_to_offset(self, offset: UtcOffset) -> Option<DateTime<offset_kind::Fixed>>
- where
- O: HasLogicalOffset,
- {
- let self_offset = maybe_offset_as_offset::<O>(self.offset);
-
- if self_offset.whole_hours() == offset.whole_hours()
- && self_offset.minutes_past_hour() == offset.minutes_past_hour()
- && self_offset.seconds_past_minute() == offset.seconds_past_minute()
- {
- return Some(DateTime {
- date: self.date,
- time: self.time,
- offset,
- });
- }
-
- let (year, ordinal, time) = self.to_offset_raw(offset);
-
- if year > MAX_YEAR || year < MIN_YEAR {
- return None;
- }
-
- Some(DateTime {
- date: Date::__from_ordinal_date_unchecked(year, ordinal),
- time,
- offset,
- })
- }
-
- /// Equivalent to `.to_offset(UtcOffset::UTC)`, but returning the year, ordinal, and time. This
- /// avoids constructing an invalid [`Date`] if the new value is out of range.
- pub(crate) const fn to_offset_raw(self, offset: UtcOffset) -> (i32, u16, Time) {
- let Some(from) = maybe_offset_as_offset_opt::<O>(self.offset) else {
- // No adjustment is needed because there is no offset.
- return (self.year(), self.ordinal(), self.time);
- };
- let to = offset;
-
- // Fast path for when no conversion is necessary.
- if from.whole_hours() == to.whole_hours()
- && from.minutes_past_hour() == to.minutes_past_hour()
- && from.seconds_past_minute() == to.seconds_past_minute()
- {
- return (self.year(), self.ordinal(), self.time());
- }
-
- let mut second = self.second() as i16 - from.seconds_past_minute() as i16
- + to.seconds_past_minute() as i16;
- let mut minute =
- self.minute() as i16 - from.minutes_past_hour() as i16 + to.minutes_past_hour() as i16;
- let mut hour = self.hour() as i8 - from.whole_hours() + to.whole_hours();
- let (mut year, ordinal) = self.to_ordinal_date();
- let mut ordinal = ordinal as i16;
-
- // Cascade the values twice. This is needed because the values are adjusted twice above.
- cascade!(second in 0..Second.per(Minute) as i16 => minute);
- cascade!(second in 0..Second.per(Minute) as i16 => minute);
- cascade!(minute in 0..Minute.per(Hour) as i16 => hour);
- cascade!(minute in 0..Minute.per(Hour) as i16 => hour);
- cascade!(hour in 0..Hour.per(Day) as i8 => ordinal);
- cascade!(hour in 0..Hour.per(Day) as i8 => ordinal);
- cascade!(ordinal => year);
-
- debug_assert!(ordinal > 0);
- debug_assert!(ordinal <= crate::util::days_in_year(year) as i16);
-
- (
- year,
- ordinal as _,
- Time::__from_hms_nanos_unchecked(
- hour as _,
- minute as _,
- second as _,
- self.nanosecond(),
- ),
- )
- }
- // endregion to offset
-
- // region: checked arithmetic
- pub const fn checked_add(self, duration: Duration) -> Option<Self> {
- let (date_adjustment, time) = self.time.adjusting_add(duration);
- let date = const_try_opt!(self.date.checked_add(duration));
-
- Some(Self {
- date: match date_adjustment {
- util::DateAdjustment::Previous => const_try_opt!(date.previous_day()),
- util::DateAdjustment::Next => const_try_opt!(date.next_day()),
- util::DateAdjustment::None => date,
- },
- time,
- offset: self.offset,
- })
- }
-
- pub const fn checked_sub(self, duration: Duration) -> Option<Self> {
- let (date_adjustment, time) = self.time.adjusting_sub(duration);
- let date = const_try_opt!(self.date.checked_sub(duration));
-
- Some(Self {
- date: match date_adjustment {
- util::DateAdjustment::Previous => const_try_opt!(date.previous_day()),
- util::DateAdjustment::Next => const_try_opt!(date.next_day()),
- util::DateAdjustment::None => date,
- },
- time,
- offset: self.offset,
- })
- }
- // endregion checked arithmetic
-
- // region: saturating arithmetic
- pub const fn saturating_add(self, duration: Duration) -> Self {
- if let Some(datetime) = self.checked_add(duration) {
- datetime
- } else if duration.is_negative() {
- Self {
- date: Date::MIN,
- time: Time::MIN,
- offset: self.offset,
- }
- } else {
- Self {
- date: Date::MAX,
- time: Time::MAX,
- offset: self.offset,
- }
- }
- }
-
- pub const fn saturating_sub(self, duration: Duration) -> Self {
- if let Some(datetime) = self.checked_sub(duration) {
- datetime
- } else if duration.is_negative() {
- Self {
- date: Date::MAX,
- time: Time::MAX,
- offset: self.offset,
- }
- } else {
- Self {
- date: Date::MIN,
- time: Time::MIN,
- offset: self.offset,
- }
- }
- }
- // endregion saturating arithmetic
-
- // region: replacement
- pub const fn replace_time(self, time: Time) -> Self {
- Self {
- date: self.date,
- time,
- offset: self.offset,
- }
- }
-
- pub const fn replace_date(self, date: Date) -> Self {
- Self {
- date,
- time: self.time,
- offset: self.offset,
- }
- }
-
- pub const fn replace_date_time(self, date_time: DateTime<offset_kind::None>) -> Self
- where
- O: HasLogicalOffset,
- {
- Self {
- date: date_time.date,
- time: date_time.time,
- offset: self.offset,
- }
- }
-
- pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> {
- Ok(Self {
- date: const_try!(self.date.replace_year(year)),
- time: self.time,
- offset: self.offset,
- })
- }
-
- pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange> {
- Ok(Self {
- date: const_try!(self.date.replace_month(month)),
- time: self.time,
- offset: self.offset,
- })
- }
-
- pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self {
- date: const_try!(self.date.replace_day(day)),
- time: self.time,
- offset: self.offset,
- })
- }
-
- pub const fn replace_hour(self, hour: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self {
- date: self.date,
- time: const_try!(self.time.replace_hour(hour)),
- offset: self.offset,
- })
- }
-
- pub const fn replace_minute(self, minute: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self {
- date: self.date,
- time: const_try!(self.time.replace_minute(minute)),
- offset: self.offset,
- })
- }
-
- pub const fn replace_second(self, second: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self {
- date: self.date,
- time: const_try!(self.time.replace_second(second)),
- offset: self.offset,
- })
- }
-
- pub const fn replace_millisecond(
- self,
- millisecond: u16,
- ) -> Result<Self, error::ComponentRange> {
- Ok(Self {
- date: self.date,
- time: const_try!(self.time.replace_millisecond(millisecond)),
- offset: self.offset,
- })
- }
-
- pub const fn replace_microsecond(
- self,
- microsecond: u32,
- ) -> Result<Self, error::ComponentRange> {
- Ok(Self {
- date: self.date,
- time: const_try!(self.time.replace_microsecond(microsecond)),
- offset: self.offset,
- })
- }
-
- pub const fn replace_nanosecond(self, nanosecond: u32) -> Result<Self, error::ComponentRange> {
- Ok(Self {
- date: self.date,
- time: const_try!(self.time.replace_nanosecond(nanosecond)),
- offset: self.offset,
- })
- }
-
- // Don't gate this on just having an offset, as `ZonedDateTime` cannot be set to an arbitrary
- // offset.
- pub const fn replace_offset(self, offset: UtcOffset) -> DateTime<offset_kind::Fixed>
- where
- O: IsOffsetKindFixed,
- {
- DateTime {
- date: self.date,
- time: self.time,
- offset,
- }
- }
-
- // endregion replacement
-
- // region: formatting & parsing
- #[cfg(feature = "formatting")]
- pub fn format_into(
- self,
- output: &mut impl io::Write,
- format: &(impl Formattable + ?Sized),
- ) -> Result<usize, error::Format> {
- format.format_into(
- output,
- Some(self.date),
- Some(self.time),
- maybe_offset_as_offset_opt::<O>(self.offset),
- )
- }
-
- #[cfg(feature = "formatting")]
- pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
- format.format(
- Some(self.date),
- Some(self.time),
- maybe_offset_as_offset_opt::<O>(self.offset),
- )
- }
-
- #[cfg(feature = "parsing")]
- pub fn parse(
- input: &str,
- description: &(impl Parsable + ?Sized),
- ) -> Result<Self, error::Parse> {
- description.parse_date_time(input.as_bytes())
- }
-
- /// A helper method to check if the `OffsetDateTime` is a valid representation of a leap second.
- /// Leap seconds, when parsed, are represented as the preceding nanosecond. However, leap
- /// seconds can only occur as the last second of a month UTC.
- #[cfg(feature = "parsing")]
- pub(crate) const fn is_valid_leap_second_stand_in(self) -> bool {
- // Leap seconds aren't allowed if there is no offset.
- if !O::HAS_LOGICAL_OFFSET {
- return false;
- }
-
- // This comparison doesn't need to be adjusted for the stored offset, so check it first for
- // speed.
- if self.nanosecond() != 999_999_999 {
- return false;
- }
-
- let (year, ordinal, time) = self.to_offset_raw(UtcOffset::UTC);
- let Ok(date) = Date::from_ordinal_date(year, ordinal) else {
- return false;
- };
-
- time.hour() == 23
- && time.minute() == 59
- && time.second() == 59
- && date.day() == util::days_in_year_month(year, date.month())
- }
-
- // endregion formatting & parsing
-
- // region: deprecated time getters
-
- // All the way at the bottom as it's low priority. These methods only exist for when
- // `OffsetDateTime` is made an alias of `DateTime<Fixed>`. Consider hiding these methods from
- // documentation in the future.
-
- #[doc(hidden)]
- #[allow(dead_code)] // while functionally private
- #[deprecated(since = "0.3.18", note = "use `as_hms` instead")]
- pub const fn to_hms(self) -> (u8, u8, u8)
- where
- O: IsOffsetKindFixed,
- {
- self.time.as_hms()
- }
-
- #[doc(hidden)]
- #[allow(dead_code)] // while functionally private
- #[deprecated(since = "0.3.18", note = "use `as_hms_milli` instead")]
- pub const fn to_hms_milli(self) -> (u8, u8, u8, u16)
- where
- O: IsOffsetKindFixed,
- {
- self.time.as_hms_milli()
- }
-
- #[doc(hidden)]
- #[allow(dead_code)] // while functionally private
- #[deprecated(since = "0.3.18", note = "use `as_hms_micro` instead")]
- pub const fn to_hms_micro(self) -> (u8, u8, u8, u32)
- where
- O: IsOffsetKindFixed,
- {
- self.time.as_hms_micro()
- }
-
- #[doc(hidden)]
- #[allow(dead_code)] // while functionally private
- #[deprecated(since = "0.3.18", note = "use `as_hms_nano` instead")]
- pub const fn to_hms_nano(self) -> (u8, u8, u8, u32)
- where
- O: IsOffsetKindFixed,
- {
- self.time.as_hms_nano()
- }
- // endregion deprecated time getters
-}
-
-impl<O: MaybeOffset> fmt::Debug for DateTime<O> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- <Self as fmt::Display>::fmt(self, f)
- }
-}
-
-impl<O: MaybeOffset> fmt::Display for DateTime<O> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{} {}", self.date, self.time)?;
- if let Some(offset) = maybe_offset_as_offset_opt::<O>(self.offset) {
- write!(f, " {offset}")?;
- }
- Ok(())
- }
-}
-
-// region: trait impls
-impl<O: MaybeOffset> PartialEq for DateTime<O> {
- fn eq(&self, rhs: &Self) -> bool {
- if O::HAS_LOGICAL_OFFSET {
- self.to_offset_raw(UtcOffset::UTC) == rhs.to_offset_raw(UtcOffset::UTC)
- } else {
- (self.date, self.time) == (rhs.date, rhs.time)
- }
- }
-}
-
-impl<O: MaybeOffset> Eq for DateTime<O> {}
-
-impl<O: MaybeOffset> PartialOrd for DateTime<O> {
- fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
- Some(self.cmp(rhs))
- }
-}
-
-impl<O: MaybeOffset> Ord for DateTime<O> {
- fn cmp(&self, rhs: &Self) -> Ordering {
- if O::HAS_LOGICAL_OFFSET {
- self.to_offset_raw(UtcOffset::UTC)
- .cmp(&rhs.to_offset_raw(UtcOffset::UTC))
- } else {
- (self.date, self.time).cmp(&(rhs.date, rhs.time))
- }
- }
-}
-
-impl<O: MaybeOffset> Hash for DateTime<O> {
- fn hash<H: Hasher>(&self, hasher: &mut H) {
- if O::HAS_LOGICAL_OFFSET {
- self.to_offset_raw(UtcOffset::UTC).hash(hasher);
- } else {
- (self.date, self.time).hash(hasher);
- }
- }
-}
-
-impl<O: MaybeOffset> Add<Duration> for DateTime<O> {
- type Output = Self;
-
- fn add(self, duration: Duration) -> Self {
- self.checked_add(duration)
- .expect("resulting value is out of range")
- }
-}
-
-impl<O: MaybeOffset> Add<StdDuration> for DateTime<O> {
- type Output = Self;
-
- fn add(self, duration: StdDuration) -> Self::Output {
- let (is_next_day, time) = self.time.adjusting_add_std(duration);
-
- Self {
- date: if is_next_day {
- (self.date + duration)
- .next_day()
- .expect("resulting value is out of range")
- } else {
- self.date + duration
- },
- time,
- offset: self.offset,
- }
- }
-}
-
-impl<O: MaybeOffset> AddAssign<Duration> for DateTime<O> {
- fn add_assign(&mut self, rhs: Duration) {
- *self = *self + rhs;
- }
-}
-
-impl<O: MaybeOffset> AddAssign<StdDuration> for DateTime<O> {
- fn add_assign(&mut self, rhs: StdDuration) {
- *self = *self + rhs;
- }
-}
-
-impl<O: MaybeOffset> Sub<Duration> for DateTime<O> {
- type Output = Self;
-
- fn sub(self, duration: Duration) -> Self {
- self.checked_sub(duration)
- .expect("resulting value is out of range")
- }
-}
-
-impl<O: MaybeOffset> Sub<StdDuration> for DateTime<O> {
- type Output = Self;
-
- fn sub(self, duration: StdDuration) -> Self::Output {
- let (is_previous_day, time) = self.time.adjusting_sub_std(duration);
-
- Self {
- date: if is_previous_day {
- (self.date - duration)
- .previous_day()
- .expect("resulting value is out of range")
- } else {
- self.date - duration
- },
- time,
- offset: self.offset,
- }
- }
-}
-
-impl<O: MaybeOffset> SubAssign<Duration> for DateTime<O> {
- fn sub_assign(&mut self, rhs: Duration) {
- *self = *self - rhs;
- }
-}
-
-impl<O: MaybeOffset> SubAssign<StdDuration> for DateTime<O> {
- fn sub_assign(&mut self, rhs: StdDuration) {
- *self = *self - rhs;
- }
-}
-
-impl<O: MaybeOffset> Sub<Self> for DateTime<O> {
- type Output = Duration;
-
- fn sub(self, rhs: Self) -> Self::Output {
- let base = (self.date - rhs.date) + (self.time - rhs.time);
-
- match (
- maybe_offset_as_offset_opt::<O>(self.offset),
- maybe_offset_as_offset_opt::<O>(rhs.offset),
- ) {
- (Some(self_offset), Some(rhs_offset)) => {
- let adjustment = Duration::seconds(
- (self_offset.whole_seconds() - rhs_offset.whole_seconds()) as i64,
- );
- base - adjustment
- }
- (left, right) => {
- debug_assert!(
- left.is_none() && right.is_none(),
- "offset type should not be different for the same type"
- );
- base
- }
- }
- }
-}
-
-#[cfg(feature = "std")]
-impl Add<Duration> for SystemTime {
- type Output = Self;
-
- fn add(self, duration: Duration) -> Self::Output {
- if duration.is_zero() {
- self
- } else if duration.is_positive() {
- self + duration.unsigned_abs()
- } else {
- debug_assert!(duration.is_negative());
- self - duration.unsigned_abs()
- }
- }
-}
-
-impl_add_assign!(SystemTime: #[cfg(feature = "std")] Duration);
-
-#[cfg(feature = "std")]
-impl Sub<Duration> for SystemTime {
- type Output = Self;
-
- fn sub(self, duration: Duration) -> Self::Output {
- (DateTime::from(self) - duration).into()
- }
-}
-
-impl_sub_assign!(SystemTime: #[cfg(feature = "std")] Duration);
-
-#[cfg(feature = "std")]
-impl Sub<SystemTime> for DateTime<offset_kind::Fixed> {
- type Output = Duration;
-
- fn sub(self, rhs: SystemTime) -> Self::Output {
- self - Self::from(rhs)
- }
-}
-
-#[cfg(feature = "std")]
-impl Sub<DateTime<offset_kind::Fixed>> for SystemTime {
- type Output = Duration;
-
- fn sub(self, rhs: DateTime<offset_kind::Fixed>) -> Self::Output {
- DateTime::<offset_kind::Fixed>::from(self) - rhs
- }
-}
-
-#[cfg(feature = "std")]
-impl PartialEq<SystemTime> for DateTime<offset_kind::Fixed> {
- fn eq(&self, rhs: &SystemTime) -> bool {
- self == &Self::from(*rhs)
- }
-}
-
-#[cfg(feature = "std")]
-impl PartialEq<DateTime<offset_kind::Fixed>> for SystemTime {
- fn eq(&self, rhs: &DateTime<offset_kind::Fixed>) -> bool {
- &DateTime::<offset_kind::Fixed>::from(*self) == rhs
- }
-}
-
-#[cfg(feature = "std")]
-impl PartialOrd<SystemTime> for DateTime<offset_kind::Fixed> {
- fn partial_cmp(&self, other: &SystemTime) -> Option<Ordering> {
- self.partial_cmp(&Self::from(*other))
- }
-}
-
-#[cfg(feature = "std")]
-impl PartialOrd<DateTime<offset_kind::Fixed>> for SystemTime {
- fn partial_cmp(&self, other: &DateTime<offset_kind::Fixed>) -> Option<Ordering> {
- DateTime::<offset_kind::Fixed>::from(*self).partial_cmp(other)
- }
-}
-
-#[cfg(feature = "std")]
-impl From<SystemTime> for DateTime<offset_kind::Fixed> {
- fn from(system_time: SystemTime) -> Self {
- match system_time.duration_since(SystemTime::UNIX_EPOCH) {
- Ok(duration) => Self::UNIX_EPOCH + duration,
- Err(err) => Self::UNIX_EPOCH - err.duration(),
- }
- }
-}
-
-#[allow(clippy::fallible_impl_from)] // caused by `debug_assert!`
-#[cfg(feature = "std")]
-impl From<DateTime<offset_kind::Fixed>> for SystemTime {
- fn from(datetime: DateTime<offset_kind::Fixed>) -> Self {
- let duration = datetime - DateTime::<offset_kind::Fixed>::UNIX_EPOCH;
-
- if duration.is_zero() {
- Self::UNIX_EPOCH
- } else if duration.is_positive() {
- Self::UNIX_EPOCH + duration.unsigned_abs()
- } else {
- debug_assert!(duration.is_negative());
- Self::UNIX_EPOCH - duration.unsigned_abs()
- }
- }
-}
-
-#[allow(clippy::fallible_impl_from)]
-#[cfg(all(
- target_family = "wasm",
- not(any(target_os = "emscripten", target_os = "wasi")),
- feature = "wasm-bindgen"
-))]
-impl From<js_sys::Date> for DateTime<offset_kind::Fixed> {
- fn from(js_date: js_sys::Date) -> Self {
- // get_time() returns milliseconds
- let timestamp_nanos = (js_date.get_time() * Nanosecond.per(Millisecond) as f64) as i128;
- Self::from_unix_timestamp_nanos(timestamp_nanos)
- .expect("invalid timestamp: Timestamp cannot fit in range")
- }
-}
-
-#[cfg(all(
- target_family = "wasm",
- not(any(target_os = "emscripten", target_os = "wasi")),
- feature = "wasm-bindgen"
-))]
-impl From<DateTime<offset_kind::Fixed>> for js_sys::Date {
- fn from(datetime: DateTime<offset_kind::Fixed>) -> Self {
- // new Date() takes milliseconds
- let timestamp =
- (datetime.unix_timestamp_nanos() / Nanosecond.per(Millisecond) as i128) as f64;
- js_sys::Date::new(&timestamp.into())
- }
-}
-// endregion trait impls
diff --git a/third_party/rust/time/src/duration.rs b/third_party/rust/time/src/duration.rs
index 68a38eec78..0da8385b3c 100644
--- a/third_party/rust/time/src/duration.rs
+++ b/third_party/rust/time/src/duration.rs
@@ -6,9 +6,16 @@ use core::iter::Sum;
use core::ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign};
use core::time::Duration as StdDuration;
+use deranged::RangedI32;
+use num_conv::prelude::*;
+
use crate::convert::*;
use crate::error;
+use crate::internal_macros::{
+ const_try_opt, expect_opt, impl_add_assign, impl_div_assign, impl_mul_assign, impl_sub_assign,
+};
#[cfg(feature = "std")]
+#[allow(deprecated)]
use crate::Instant;
/// By explicitly inserting this enum where padding is expected, the compiler is able to better
@@ -20,11 +27,9 @@ pub(crate) enum Padding {
Optimize,
}
-impl Default for Padding {
- fn default() -> Self {
- Self::Optimize
- }
-}
+/// The type of the `nanosecond` field of `Duration`.
+type Nanoseconds =
+ RangedI32<{ -(Nanosecond::per(Second) as i32 - 1) }, { Nanosecond::per(Second) as i32 - 1 }>;
/// A span of time with nanosecond precision.
///
@@ -32,12 +37,13 @@ impl Default for Padding {
/// nanoseconds.
///
/// This implementation allows for negative durations, unlike [`core::time::Duration`].
-#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Duration {
/// Number of whole seconds.
seconds: i64,
/// Number of nanoseconds within the second. The sign always matches the `seconds` field.
- nanoseconds: i32, // always -10^9 < nanoseconds < 10^9
+ // Sign must match that of `seconds` (though this is not a safety requirement).
+ nanoseconds: Nanoseconds,
#[allow(clippy::missing_docs_in_private_items)]
padding: Padding,
}
@@ -51,10 +57,22 @@ impl fmt::Debug for Duration {
}
}
-/// This is adapted from the `std` implementation, which uses mostly bit
+impl Default for Duration {
+ fn default() -> Self {
+ Self {
+ seconds: 0,
+ nanoseconds: Nanoseconds::new_static::<0>(),
+ padding: Padding::Optimize,
+ }
+ }
+}
+
+/// This is adapted from the [`std` implementation][std], which uses mostly bit
/// operations to ensure the highest precision:
-/// https://github.com/rust-lang/rust/blob/3a37c2f0523c87147b64f1b8099fc9df22e8c53e/library/core/src/time.rs#L1262-L1340
+///
/// Changes from `std` are marked and explained below.
+///
+/// [std]: https://github.com/rust-lang/rust/blob/3a37c2f0523c87147b64f1b8099fc9df22e8c53e/library/core/src/time.rs#L1262-L1340
#[rustfmt::skip] // Skip `rustfmt` because it reformats the arguments of the macro weirdly.
macro_rules! try_from_secs {
(
@@ -87,7 +105,7 @@ macro_rules! try_from_secs {
// the input is less than 1 second
let t = <$double_ty>::from(mant) << ($offset + exp);
let nanos_offset = $mant_bits + $offset;
- let nanos_tmp = u128::from(Nanosecond.per(Second)) * u128::from(t);
+ let nanos_tmp = u128::from(Nanosecond::per(Second)) * u128::from(t);
let nanos = (nanos_tmp >> nanos_offset) as u32;
let rem_mask = (1 << nanos_offset) - 1;
@@ -101,7 +119,7 @@ macro_rules! try_from_secs {
// f32 does not have enough precision to trigger the second branch
// since it can not represent numbers between 0.999_999_940_395 and 1.0.
let nanos = nanos + add_ns as u32;
- if ($mant_bits == 23) || (nanos != Nanosecond.per(Second)) {
+ if ($mant_bits == 23) || (nanos != Nanosecond::per(Second)) {
(0, nanos)
} else {
(1, 0)
@@ -110,7 +128,7 @@ macro_rules! try_from_secs {
let secs = u64::from(mant >> ($mant_bits - exp));
let t = <$double_ty>::from((mant << exp) & MANT_MASK);
let nanos_offset = $mant_bits;
- let nanos_tmp = <$double_ty>::from(Nanosecond.per(Second)) * t;
+ let nanos_tmp = <$double_ty>::from(Nanosecond::per(Second)) * t;
let nanos = (nanos_tmp >> nanos_offset) as u32;
let rem_mask = (1 << nanos_offset) - 1;
@@ -126,7 +144,7 @@ macro_rules! try_from_secs {
// and 2.0. Bigger values result in even smaller precision of the
// fractional part.
let nanos = nanos + add_ns as u32;
- if ($mant_bits == 23) || (nanos != Nanosecond.per(Second)) {
+ if ($mant_bits == 23) || (nanos != Nanosecond::per(Second)) {
(secs, nanos)
} else {
(secs + 1, 0)
@@ -144,7 +162,7 @@ macro_rules! try_from_secs {
// following numbers -128..=127. The check above (exp < 63)
// doesn't cover i64::MIN as that is -2^63, so we have this
// additional case to handle the asymmetry of iN::MIN.
- break 'value Self::new_unchecked(i64::MIN, 0);
+ break 'value Self::new_ranged_unchecked(i64::MIN, Nanoseconds::new_static::<0>());
} else if $secs.is_nan() {
// Change from std: std doesn't differentiate between the error
// cases.
@@ -164,7 +182,8 @@ macro_rules! try_from_secs {
let secs_signed = ((secs as i64) ^ (mask as i64)) - (mask as i64);
#[allow(trivial_numeric_casts)]
let nanos_signed = ((nanos as i32) ^ (mask as i32)) - (mask as i32);
- Self::new_unchecked(secs_signed, nanos_signed)
+ // Safety: `nanos_signed` is in range.
+ unsafe { Self::new_unchecked(secs_signed, nanos_signed) }
}
}};
}
@@ -244,10 +263,10 @@ impl Duration {
pub const WEEK: Self = Self::weeks(1);
/// The minimum possible duration. Adding any negative duration to this will cause an overflow.
- pub const MIN: Self = Self::new_unchecked(i64::MIN, -((Nanosecond.per(Second) - 1) as i32));
+ pub const MIN: Self = Self::new_ranged(i64::MIN, Nanoseconds::MIN);
/// The maximum possible duration. Adding any positive duration to this will cause an overflow.
- pub const MAX: Self = Self::new_unchecked(i64::MAX, (Nanosecond.per(Second) - 1) as _);
+ pub const MAX: Self = Self::new_ranged(i64::MAX, Nanoseconds::MAX);
// endregion constants
// region: is_{sign}
@@ -259,7 +278,7 @@ impl Duration {
/// assert!(!1.nanoseconds().is_zero());
/// ```
pub const fn is_zero(self) -> bool {
- self.seconds == 0 && self.nanoseconds == 0
+ self.seconds == 0 && self.nanoseconds.get() == 0
}
/// Check if a duration is negative.
@@ -271,7 +290,7 @@ impl Duration {
/// assert!(!1.seconds().is_negative());
/// ```
pub const fn is_negative(self) -> bool {
- self.seconds < 0 || self.nanoseconds < 0
+ self.seconds < 0 || self.nanoseconds.get() < 0
}
/// Check if a duration is positive.
@@ -283,7 +302,7 @@ impl Duration {
/// assert!(!(-1).seconds().is_positive());
/// ```
pub const fn is_positive(self) -> bool {
- self.seconds > 0 || self.nanoseconds > 0
+ self.seconds > 0 || self.nanoseconds.get() > 0
}
// endregion is_{sign}
@@ -300,7 +319,7 @@ impl Duration {
/// ```
pub const fn abs(self) -> Self {
match self.seconds.checked_abs() {
- Some(seconds) => Self::new_unchecked(seconds, self.nanoseconds.abs()),
+ Some(seconds) => Self::new_ranged_unchecked(seconds, self.nanoseconds.abs()),
None => Self::MAX,
}
}
@@ -315,21 +334,36 @@ impl Duration {
/// assert_eq!((-1).seconds().unsigned_abs(), 1.std_seconds());
/// ```
pub const fn unsigned_abs(self) -> StdDuration {
- StdDuration::new(self.seconds.unsigned_abs(), self.nanoseconds.unsigned_abs())
+ StdDuration::new(
+ self.seconds.unsigned_abs(),
+ self.nanoseconds.get().unsigned_abs(),
+ )
}
// endregion abs
// region: constructors
/// Create a new `Duration` without checking the validity of the components.
- pub(crate) const fn new_unchecked(seconds: i64, nanoseconds: i32) -> Self {
+ ///
+ /// # Safety
+ ///
+ /// - `nanoseconds` must be in the range `-999_999_999..=999_999_999`.
+ ///
+ /// While the sign of `nanoseconds` is required to be the same as the sign of `seconds`, this is
+ /// not a safety invariant.
+ pub(crate) const unsafe fn new_unchecked(seconds: i64, nanoseconds: i32) -> Self {
+ Self::new_ranged_unchecked(
+ seconds,
+ // Safety: The caller must uphold the safety invariants.
+ unsafe { Nanoseconds::new_unchecked(nanoseconds) },
+ )
+ }
+
+ /// Create a new `Duration` without checking the validity of the components.
+ pub(crate) const fn new_ranged_unchecked(seconds: i64, nanoseconds: Nanoseconds) -> Self {
if seconds < 0 {
- debug_assert!(nanoseconds <= 0);
- debug_assert!(nanoseconds > -(Nanosecond.per(Second) as i32));
+ debug_assert!(nanoseconds.get() <= 0);
} else if seconds > 0 {
- debug_assert!(nanoseconds >= 0);
- debug_assert!(nanoseconds < Nanosecond.per(Second) as _);
- } else {
- debug_assert!(nanoseconds.unsigned_abs() < Nanosecond.per(Second));
+ debug_assert!(nanoseconds.get() >= 0);
}
Self {
@@ -348,24 +382,52 @@ impl Duration {
/// assert_eq!(Duration::new(-1, 0), (-1).seconds());
/// assert_eq!(Duration::new(1, 2_000_000_000), 3.seconds());
/// ```
+ ///
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
pub const fn new(mut seconds: i64, mut nanoseconds: i32) -> Self {
seconds = expect_opt!(
- seconds.checked_add(nanoseconds as i64 / Nanosecond.per(Second) as i64),
+ seconds.checked_add(nanoseconds as i64 / Nanosecond::per(Second) as i64),
"overflow constructing `time::Duration`"
);
- nanoseconds %= Nanosecond.per(Second) as i32;
+ nanoseconds %= Nanosecond::per(Second) as i32;
if seconds > 0 && nanoseconds < 0 {
// `seconds` cannot overflow here because it is positive.
seconds -= 1;
- nanoseconds += Nanosecond.per(Second) as i32;
+ nanoseconds += Nanosecond::per(Second) as i32;
} else if seconds < 0 && nanoseconds > 0 {
// `seconds` cannot overflow here because it is negative.
seconds += 1;
- nanoseconds -= Nanosecond.per(Second) as i32;
+ nanoseconds -= Nanosecond::per(Second) as i32;
+ }
+
+ // Safety: `nanoseconds` is in range due to the modulus above.
+ unsafe { Self::new_unchecked(seconds, nanoseconds) }
+ }
+
+ /// Create a new `Duration` with the provided seconds and nanoseconds.
+ pub(crate) const fn new_ranged(mut seconds: i64, mut nanoseconds: Nanoseconds) -> Self {
+ if seconds > 0 && nanoseconds.get() < 0 {
+ // `seconds` cannot overflow here because it is positive.
+ seconds -= 1;
+ // Safety: `nanoseconds` is negative with a maximum of 999,999,999, so adding a billion
+ // to it is guaranteed to result in an in-range value.
+ nanoseconds = unsafe {
+ Nanoseconds::new_unchecked(nanoseconds.get() + Nanosecond::per(Second) as i32)
+ };
+ } else if seconds < 0 && nanoseconds.get() > 0 {
+ // `seconds` cannot overflow here because it is negative.
+ seconds += 1;
+ // Safety: `nanoseconds` is positive with a minimum of -999,999,999, so subtracting a
+ // billion from it is guaranteed to result in an in-range value.
+ nanoseconds = unsafe {
+ Nanoseconds::new_unchecked(nanoseconds.get() - Nanosecond::per(Second) as i32)
+ };
}
- Self::new_unchecked(seconds, nanoseconds)
+ Self::new_ranged_unchecked(seconds, nanoseconds)
}
/// Create a new `Duration` with the given number of weeks. Equivalent to
@@ -375,9 +437,13 @@ impl Duration {
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(Duration::weeks(1), 604_800.seconds());
/// ```
+ ///
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
pub const fn weeks(weeks: i64) -> Self {
Self::seconds(expect_opt!(
- weeks.checked_mul(Second.per(Week) as _),
+ weeks.checked_mul(Second::per(Week) as _),
"overflow constructing `time::Duration`"
))
}
@@ -389,9 +455,13 @@ impl Duration {
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(Duration::days(1), 86_400.seconds());
/// ```
+ ///
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
pub const fn days(days: i64) -> Self {
Self::seconds(expect_opt!(
- days.checked_mul(Second.per(Day) as _),
+ days.checked_mul(Second::per(Day) as _),
"overflow constructing `time::Duration`"
))
}
@@ -403,9 +473,13 @@ impl Duration {
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(Duration::hours(1), 3_600.seconds());
/// ```
+ ///
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
pub const fn hours(hours: i64) -> Self {
Self::seconds(expect_opt!(
- hours.checked_mul(Second.per(Hour) as _),
+ hours.checked_mul(Second::per(Hour) as _),
"overflow constructing `time::Duration`"
))
}
@@ -417,9 +491,13 @@ impl Duration {
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(Duration::minutes(1), 60.seconds());
/// ```
+ ///
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
pub const fn minutes(minutes: i64) -> Self {
Self::seconds(expect_opt!(
- minutes.checked_mul(Second.per(Minute) as _),
+ minutes.checked_mul(Second::per(Minute) as _),
"overflow constructing `time::Duration`"
))
}
@@ -431,7 +509,7 @@ impl Duration {
/// assert_eq!(Duration::seconds(1), 1_000.milliseconds());
/// ```
pub const fn seconds(seconds: i64) -> Self {
- Self::new_unchecked(seconds, 0)
+ Self::new_ranged_unchecked(seconds, Nanoseconds::new_static::<0>())
}
/// Creates a new `Duration` from the specified number of seconds represented as `f64`.
@@ -614,11 +692,14 @@ impl Duration {
/// assert_eq!(Duration::milliseconds(-1), (-1_000).microseconds());
/// ```
pub const fn milliseconds(milliseconds: i64) -> Self {
- Self::new_unchecked(
- milliseconds / Millisecond.per(Second) as i64,
- (milliseconds % Millisecond.per(Second) as i64 * Nanosecond.per(Millisecond) as i64)
- as _,
- )
+ // Safety: `nanoseconds` is guaranteed to be in range because of the modulus.
+ unsafe {
+ Self::new_unchecked(
+ milliseconds / Millisecond::per(Second) as i64,
+ (milliseconds % Millisecond::per(Second) as i64
+ * Nanosecond::per(Millisecond) as i64) as _,
+ )
+ }
}
/// Create a new `Duration` with the given number of microseconds.
@@ -629,11 +710,14 @@ impl Duration {
/// assert_eq!(Duration::microseconds(-1), (-1_000).nanoseconds());
/// ```
pub const fn microseconds(microseconds: i64) -> Self {
- Self::new_unchecked(
- microseconds / Microsecond.per(Second) as i64,
- (microseconds % Microsecond.per(Second) as i64 * Nanosecond.per(Microsecond) as i64)
- as _,
- )
+ // Safety: `nanoseconds` is guaranteed to be in range because of the modulus.
+ unsafe {
+ Self::new_unchecked(
+ microseconds / Microsecond::per(Second) as i64,
+ (microseconds % Microsecond::per(Second) as i64
+ * Nanosecond::per(Microsecond) as i64) as _,
+ )
+ }
}
/// Create a new `Duration` with the given number of nanoseconds.
@@ -644,10 +728,13 @@ impl Duration {
/// assert_eq!(Duration::nanoseconds(-1), (-1).microseconds() / 1_000);
/// ```
pub const fn nanoseconds(nanoseconds: i64) -> Self {
- Self::new_unchecked(
- nanoseconds / Nanosecond.per(Second) as i64,
- (nanoseconds % Nanosecond.per(Second) as i64) as _,
- )
+ // Safety: `nanoseconds` is guaranteed to be in range because of the modulus.
+ unsafe {
+ Self::new_unchecked(
+ nanoseconds / Nanosecond::per(Second) as i64,
+ (nanoseconds % Nanosecond::per(Second) as i64) as _,
+ )
+ }
}
/// Create a new `Duration` with the given number of nanoseconds.
@@ -655,14 +742,15 @@ impl Duration {
/// As the input range cannot be fully mapped to the output, this should only be used where it's
/// known to result in a valid value.
pub(crate) const fn nanoseconds_i128(nanoseconds: i128) -> Self {
- let seconds = nanoseconds / Nanosecond.per(Second) as i128;
- let nanoseconds = nanoseconds % Nanosecond.per(Second) as i128;
+ let seconds = nanoseconds / Nanosecond::per(Second) as i128;
+ let nanoseconds = nanoseconds % Nanosecond::per(Second) as i128;
if seconds > i64::MAX as i128 || seconds < i64::MIN as i128 {
crate::expect_failed("overflow constructing `time::Duration`");
}
- Self::new_unchecked(seconds as _, nanoseconds as _)
+ // Safety: `nanoseconds` is guaranteed to be in range because of the modulus above.
+ unsafe { Self::new_unchecked(seconds as _, nanoseconds as _) }
}
// endregion constructors
@@ -677,7 +765,7 @@ impl Duration {
/// assert_eq!((-6).days().whole_weeks(), 0);
/// ```
pub const fn whole_weeks(self) -> i64 {
- self.whole_seconds() / Second.per(Week) as i64
+ self.whole_seconds() / Second::per(Week) as i64
}
/// Get the number of whole days in the duration.
@@ -690,7 +778,7 @@ impl Duration {
/// assert_eq!((-23).hours().whole_days(), 0);
/// ```
pub const fn whole_days(self) -> i64 {
- self.whole_seconds() / Second.per(Day) as i64
+ self.whole_seconds() / Second::per(Day) as i64
}
/// Get the number of whole hours in the duration.
@@ -703,7 +791,7 @@ impl Duration {
/// assert_eq!((-59).minutes().whole_hours(), 0);
/// ```
pub const fn whole_hours(self) -> i64 {
- self.whole_seconds() / Second.per(Hour) as i64
+ self.whole_seconds() / Second::per(Hour) as i64
}
/// Get the number of whole minutes in the duration.
@@ -716,7 +804,7 @@ impl Duration {
/// assert_eq!((-59).seconds().whole_minutes(), 0);
/// ```
pub const fn whole_minutes(self) -> i64 {
- self.whole_seconds() / Second.per(Minute) as i64
+ self.whole_seconds() / Second::per(Minute) as i64
}
/// Get the number of whole seconds in the duration.
@@ -740,7 +828,7 @@ impl Duration {
/// assert_eq!((-1.5).seconds().as_seconds_f64(), -1.5);
/// ```
pub fn as_seconds_f64(self) -> f64 {
- self.seconds as f64 + self.nanoseconds as f64 / Nanosecond.per(Second) as f64
+ self.seconds as f64 + self.nanoseconds.get() as f64 / Nanosecond::per(Second) as f64
}
/// Get the number of fractional seconds in the duration.
@@ -751,7 +839,7 @@ impl Duration {
/// assert_eq!((-1.5).seconds().as_seconds_f32(), -1.5);
/// ```
pub fn as_seconds_f32(self) -> f32 {
- self.seconds as f32 + self.nanoseconds as f32 / Nanosecond.per(Second) as f32
+ self.seconds as f32 + self.nanoseconds.get() as f32 / Nanosecond::per(Second) as f32
}
/// Get the number of whole milliseconds in the duration.
@@ -764,13 +852,13 @@ impl Duration {
/// assert_eq!((-1).milliseconds().whole_milliseconds(), -1);
/// ```
pub const fn whole_milliseconds(self) -> i128 {
- self.seconds as i128 * Millisecond.per(Second) as i128
- + self.nanoseconds as i128 / Nanosecond.per(Millisecond) as i128
+ self.seconds as i128 * Millisecond::per(Second) as i128
+ + self.nanoseconds.get() as i128 / Nanosecond::per(Millisecond) as i128
}
/// Get the number of milliseconds past the number of whole seconds.
///
- /// Always in the range `-1_000..1_000`.
+ /// Always in the range `-999..=999`.
///
/// ```rust
/// # use time::ext::NumericalDuration;
@@ -779,7 +867,7 @@ impl Duration {
/// ```
// Allow the lint, as the value is guaranteed to be less than 1000.
pub const fn subsec_milliseconds(self) -> i16 {
- (self.nanoseconds / Nanosecond.per(Millisecond) as i32) as _
+ (self.nanoseconds.get() / Nanosecond::per(Millisecond) as i32) as _
}
/// Get the number of whole microseconds in the duration.
@@ -792,13 +880,13 @@ impl Duration {
/// assert_eq!((-1).microseconds().whole_microseconds(), -1);
/// ```
pub const fn whole_microseconds(self) -> i128 {
- self.seconds as i128 * Microsecond.per(Second) as i128
- + self.nanoseconds as i128 / Nanosecond.per(Microsecond) as i128
+ self.seconds as i128 * Microsecond::per(Second) as i128
+ + self.nanoseconds.get() as i128 / Nanosecond::per(Microsecond) as i128
}
/// Get the number of microseconds past the number of whole seconds.
///
- /// Always in the range `-1_000_000..1_000_000`.
+ /// Always in the range `-999_999..=999_999`.
///
/// ```rust
/// # use time::ext::NumericalDuration;
@@ -806,7 +894,7 @@ impl Duration {
/// assert_eq!((-1.0004).seconds().subsec_microseconds(), -400);
/// ```
pub const fn subsec_microseconds(self) -> i32 {
- self.nanoseconds / Nanosecond.per(Microsecond) as i32
+ self.nanoseconds.get() / Nanosecond::per(Microsecond) as i32
}
/// Get the number of nanoseconds in the duration.
@@ -819,12 +907,12 @@ impl Duration {
/// assert_eq!((-1).nanoseconds().whole_nanoseconds(), -1);
/// ```
pub const fn whole_nanoseconds(self) -> i128 {
- self.seconds as i128 * Nanosecond.per(Second) as i128 + self.nanoseconds as i128
+ self.seconds as i128 * Nanosecond::per(Second) as i128 + self.nanoseconds.get() as i128
}
/// Get the number of nanoseconds past the number of whole seconds.
///
- /// The returned value will always be in the range `-1_000_000_000..1_000_000_000`.
+ /// The returned value will always be in the range `-999_999_999..=999_999_999`.
///
/// ```rust
/// # use time::ext::NumericalDuration;
@@ -832,6 +920,12 @@ impl Duration {
/// assert_eq!((-1.000_000_400).seconds().subsec_nanoseconds(), -400);
/// ```
pub const fn subsec_nanoseconds(self) -> i32 {
+ self.nanoseconds.get()
+ }
+
+ /// Get the number of nanoseconds past the number of whole seconds.
+ #[cfg(feature = "quickcheck")]
+ pub(crate) const fn subsec_nanoseconds_ranged(self) -> Nanoseconds {
self.nanoseconds
}
// endregion getters
@@ -847,18 +941,19 @@ impl Duration {
/// ```
pub const fn checked_add(self, rhs: Self) -> Option<Self> {
let mut seconds = const_try_opt!(self.seconds.checked_add(rhs.seconds));
- let mut nanoseconds = self.nanoseconds + rhs.nanoseconds;
+ let mut nanoseconds = self.nanoseconds.get() + rhs.nanoseconds.get();
- if nanoseconds >= Nanosecond.per(Second) as _ || seconds < 0 && nanoseconds > 0 {
- nanoseconds -= Nanosecond.per(Second) as i32;
+ if nanoseconds >= Nanosecond::per(Second) as _ || seconds < 0 && nanoseconds > 0 {
+ nanoseconds -= Nanosecond::per(Second) as i32;
seconds = const_try_opt!(seconds.checked_add(1));
- } else if nanoseconds <= -(Nanosecond.per(Second) as i32) || seconds > 0 && nanoseconds < 0
+ } else if nanoseconds <= -(Nanosecond::per(Second) as i32) || seconds > 0 && nanoseconds < 0
{
- nanoseconds += Nanosecond.per(Second) as i32;
+ nanoseconds += Nanosecond::per(Second) as i32;
seconds = const_try_opt!(seconds.checked_sub(1));
}
- Some(Self::new_unchecked(seconds, nanoseconds))
+ // Safety: `nanoseconds` is guaranteed to be in range because of the overflow handling.
+ unsafe { Some(Self::new_unchecked(seconds, nanoseconds)) }
}
/// Computes `self - rhs`, returning `None` if an overflow occurred.
@@ -871,18 +966,19 @@ impl Duration {
/// ```
pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
let mut seconds = const_try_opt!(self.seconds.checked_sub(rhs.seconds));
- let mut nanoseconds = self.nanoseconds - rhs.nanoseconds;
+ let mut nanoseconds = self.nanoseconds.get() - rhs.nanoseconds.get();
- if nanoseconds >= Nanosecond.per(Second) as _ || seconds < 0 && nanoseconds > 0 {
- nanoseconds -= Nanosecond.per(Second) as i32;
+ if nanoseconds >= Nanosecond::per(Second) as _ || seconds < 0 && nanoseconds > 0 {
+ nanoseconds -= Nanosecond::per(Second) as i32;
seconds = const_try_opt!(seconds.checked_add(1));
- } else if nanoseconds <= -(Nanosecond.per(Second) as i32) || seconds > 0 && nanoseconds < 0
+ } else if nanoseconds <= -(Nanosecond::per(Second) as i32) || seconds > 0 && nanoseconds < 0
{
- nanoseconds += Nanosecond.per(Second) as i32;
+ nanoseconds += Nanosecond::per(Second) as i32;
seconds = const_try_opt!(seconds.checked_sub(1));
}
- Some(Self::new_unchecked(seconds, nanoseconds))
+ // Safety: `nanoseconds` is guaranteed to be in range because of the overflow handling.
+ unsafe { Some(Self::new_unchecked(seconds, nanoseconds)) }
}
/// Computes `self * rhs`, returning `None` if an overflow occurred.
@@ -897,14 +993,15 @@ impl Duration {
/// ```
pub const fn checked_mul(self, rhs: i32) -> Option<Self> {
// Multiply nanoseconds as i64, because it cannot overflow that way.
- let total_nanos = self.nanoseconds as i64 * rhs as i64;
- let extra_secs = total_nanos / Nanosecond.per(Second) as i64;
- let nanoseconds = (total_nanos % Nanosecond.per(Second) as i64) as _;
+ let total_nanos = self.nanoseconds.get() as i64 * rhs as i64;
+ let extra_secs = total_nanos / Nanosecond::per(Second) as i64;
+ let nanoseconds = (total_nanos % Nanosecond::per(Second) as i64) as _;
let seconds = const_try_opt!(
const_try_opt!(self.seconds.checked_mul(rhs as _)).checked_add(extra_secs)
);
- Some(Self::new_unchecked(seconds, nanoseconds))
+ // Safety: `nanoseconds` is guaranteed to be in range because of the modulus above.
+ unsafe { Some(Self::new_unchecked(seconds, nanoseconds)) }
}
/// Computes `self / rhs`, returning `None` if `rhs == 0` or if the result would overflow.
@@ -916,13 +1013,35 @@ impl Duration {
/// assert_eq!(1.seconds().checked_div(0), None);
/// ```
pub const fn checked_div(self, rhs: i32) -> Option<Self> {
- let seconds = const_try_opt!(self.seconds.checked_div(rhs as i64));
- let carry = self.seconds - seconds * (rhs as i64);
- let extra_nanos =
- const_try_opt!((carry * Nanosecond.per(Second) as i64).checked_div(rhs as i64));
- let nanoseconds = const_try_opt!(self.nanoseconds.checked_div(rhs)) + (extra_nanos as i32);
+ let (secs, extra_secs) = (
+ const_try_opt!(self.seconds.checked_div(rhs as i64)),
+ self.seconds % (rhs as i64),
+ );
+ let (mut nanos, extra_nanos) = (self.nanoseconds.get() / rhs, self.nanoseconds.get() % rhs);
+ nanos += ((extra_secs * (Nanosecond::per(Second) as i64) + extra_nanos as i64)
+ / (rhs as i64)) as i32;
+
+ // Safety: `nanoseconds` is in range.
+ unsafe { Some(Self::new_unchecked(secs, nanos)) }
+ }
- Some(Self::new_unchecked(seconds, nanoseconds))
+ /// Computes `-self`, returning `None` if the result would overflow.
+ ///
+ /// ```rust
+ /// # use time::ext::NumericalDuration;
+ /// # use time::Duration;
+ /// assert_eq!(5.seconds().checked_neg(), Some((-5).seconds()));
+ /// assert_eq!(Duration::MIN.checked_neg(), None);
+ /// ```
+ pub const fn checked_neg(self) -> Option<Self> {
+ if self.seconds == i64::MIN {
+ None
+ } else {
+ Some(Self::new_ranged_unchecked(
+ -self.seconds,
+ self.nanoseconds.neg(),
+ ))
+ }
}
// endregion checked arithmetic
@@ -947,24 +1066,25 @@ impl Duration {
}
return Self::MIN;
}
- let mut nanoseconds = self.nanoseconds + rhs.nanoseconds;
+ let mut nanoseconds = self.nanoseconds.get() + rhs.nanoseconds.get();
- if nanoseconds >= Nanosecond.per(Second) as _ || seconds < 0 && nanoseconds > 0 {
- nanoseconds -= Nanosecond.per(Second) as i32;
+ if nanoseconds >= Nanosecond::per(Second) as _ || seconds < 0 && nanoseconds > 0 {
+ nanoseconds -= Nanosecond::per(Second) as i32;
seconds = match seconds.checked_add(1) {
Some(seconds) => seconds,
None => return Self::MAX,
};
- } else if nanoseconds <= -(Nanosecond.per(Second) as i32) || seconds > 0 && nanoseconds < 0
+ } else if nanoseconds <= -(Nanosecond::per(Second) as i32) || seconds > 0 && nanoseconds < 0
{
- nanoseconds += Nanosecond.per(Second) as i32;
+ nanoseconds += Nanosecond::per(Second) as i32;
seconds = match seconds.checked_sub(1) {
Some(seconds) => seconds,
None => return Self::MIN,
};
}
- Self::new_unchecked(seconds, nanoseconds)
+ // Safety: `nanoseconds` is guaranteed to be in range because of the overflow handling.
+ unsafe { Self::new_unchecked(seconds, nanoseconds) }
}
/// Computes `self - rhs`, saturating if an overflow occurred.
@@ -987,24 +1107,25 @@ impl Duration {
}
return Self::MIN;
}
- let mut nanoseconds = self.nanoseconds - rhs.nanoseconds;
+ let mut nanoseconds = self.nanoseconds.get() - rhs.nanoseconds.get();
- if nanoseconds >= Nanosecond.per(Second) as _ || seconds < 0 && nanoseconds > 0 {
- nanoseconds -= Nanosecond.per(Second) as i32;
+ if nanoseconds >= Nanosecond::per(Second) as _ || seconds < 0 && nanoseconds > 0 {
+ nanoseconds -= Nanosecond::per(Second) as i32;
seconds = match seconds.checked_add(1) {
Some(seconds) => seconds,
None => return Self::MAX,
};
- } else if nanoseconds <= -(Nanosecond.per(Second) as i32) || seconds > 0 && nanoseconds < 0
+ } else if nanoseconds <= -(Nanosecond::per(Second) as i32) || seconds > 0 && nanoseconds < 0
{
- nanoseconds += Nanosecond.per(Second) as i32;
+ nanoseconds += Nanosecond::per(Second) as i32;
seconds = match seconds.checked_sub(1) {
Some(seconds) => seconds,
None => return Self::MIN,
};
}
- Self::new_unchecked(seconds, nanoseconds)
+ // Safety: `nanoseconds` is guaranteed to be in range because of the overflow handling.
+ unsafe { Self::new_unchecked(seconds, nanoseconds) }
}
/// Computes `self * rhs`, saturating if an overflow occurred.
@@ -1021,9 +1142,9 @@ impl Duration {
/// ```
pub const fn saturating_mul(self, rhs: i32) -> Self {
// Multiply nanoseconds as i64, because it cannot overflow that way.
- let total_nanos = self.nanoseconds as i64 * rhs as i64;
- let extra_secs = total_nanos / Nanosecond.per(Second) as i64;
- let nanoseconds = (total_nanos % Nanosecond.per(Second) as i64) as _;
+ let total_nanos = self.nanoseconds.get() as i64 * rhs as i64;
+ let extra_secs = total_nanos / Nanosecond::per(Second) as i64;
+ let nanoseconds = (total_nanos % Nanosecond::per(Second) as i64) as _;
let (seconds, overflow1) = self.seconds.overflowing_mul(rhs as _);
if overflow1 {
if self.seconds > 0 && rhs > 0 || self.seconds < 0 && rhs < 0 {
@@ -1039,13 +1160,19 @@ impl Duration {
return Self::MIN;
}
- Self::new_unchecked(seconds, nanoseconds)
+ // Safety: `nanoseconds` is guaranteed to be in range because of to the modulus above.
+ unsafe { Self::new_unchecked(seconds, nanoseconds) }
}
// endregion saturating arithmetic
/// Runs a closure, returning the duration of time it took to run. The return value of the
/// closure is provided in the second part of the tuple.
#[cfg(feature = "std")]
+ #[deprecated(
+ since = "0.3.32",
+ note = "extremely limited use case, not intended for benchmarking"
+ )]
+ #[allow(deprecated)]
pub fn time_fn<T>(f: impl FnOnce() -> T) -> (Self, T) {
let start = Instant::now();
let return_value = f();
@@ -1097,13 +1224,13 @@ impl fmt::Display for Duration {
// Even if this produces a de-normal float, because we're rounding we don't really care.
let seconds = self.unsigned_abs().as_secs_f64();
- item!("d", seconds / Second.per(Day) as f64);
- item!("h", seconds / Second.per(Hour) as f64);
- item!("m", seconds / Second.per(Minute) as f64);
+ item!("d", seconds / Second::per(Day) as f64);
+ item!("h", seconds / Second::per(Hour) as f64);
+ item!("m", seconds / Second::per(Minute) as f64);
item!("s", seconds);
- item!("ms", seconds * Millisecond.per(Second) as f64);
- item!("µs", seconds * Microsecond.per(Second) as f64);
- item!("ns", seconds * Nanosecond.per(Second) as f64);
+ item!("ms", seconds * Millisecond::per(Second) as f64);
+ item!("µs", seconds * Microsecond::per(Second) as f64);
+ item!("ns", seconds * Nanosecond::per(Second) as f64);
} else {
// Precise, but verbose representation.
@@ -1122,25 +1249,28 @@ impl fmt::Display for Duration {
}
let seconds = self.seconds.unsigned_abs();
- let nanoseconds = self.nanoseconds.unsigned_abs();
+ let nanoseconds = self.nanoseconds.get().unsigned_abs();
- item!("d", seconds / Second.per(Day) as u64)?;
+ item!("d", seconds / Second::per(Day).extend::<u64>())?;
item!(
"h",
- seconds / Second.per(Hour) as u64 % Hour.per(Day) as u64
+ seconds / Second::per(Hour).extend::<u64>() % Hour::per(Day).extend::<u64>()
)?;
item!(
"m",
- seconds / Second.per(Minute) as u64 % Minute.per(Hour) as u64
+ seconds / Second::per(Minute).extend::<u64>() % Minute::per(Hour).extend::<u64>()
)?;
- item!("s", seconds % Second.per(Minute) as u64)?;
- item!("ms", nanoseconds / Nanosecond.per(Millisecond))?;
+ item!("s", seconds % Second::per(Minute).extend::<u64>())?;
+ item!("ms", nanoseconds / Nanosecond::per(Millisecond))?;
item!(
"µs",
- nanoseconds / Nanosecond.per(Microsecond) as u32
- % Microsecond.per(Millisecond) as u32
+ nanoseconds / Nanosecond::per(Microsecond).extend::<u32>()
+ % Microsecond::per(Millisecond).extend::<u32>()
+ )?;
+ item!(
+ "ns",
+ nanoseconds % Nanosecond::per(Microsecond).extend::<u32>()
)?;
- item!("ns", nanoseconds % Nanosecond.per(Microsecond) as u32)?;
}
Ok(())
@@ -1156,7 +1286,7 @@ impl TryFrom<StdDuration> for Duration {
.as_secs()
.try_into()
.map_err(|_| error::ConversionRange)?,
- original.subsec_nanos() as _,
+ original.subsec_nanos().cast_signed(),
))
}
}
@@ -1172,6 +1302,7 @@ impl TryFrom<Duration> for StdDuration {
.map_err(|_| error::ConversionRange)?,
duration
.nanoseconds
+ .get()
.try_into()
.map_err(|_| error::ConversionRange)?,
))
@@ -1181,6 +1312,9 @@ impl TryFrom<Duration> for StdDuration {
impl Add for Duration {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn add(self, rhs: Self) -> Self::Output {
self.checked_add(rhs)
.expect("overflow when adding durations")
@@ -1190,6 +1324,9 @@ impl Add for Duration {
impl Add<StdDuration> for Duration {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn add(self, std_duration: StdDuration) -> Self::Output {
self + Self::try_from(std_duration)
.expect("overflow converting `std::time::Duration` to `time::Duration`")
@@ -1207,6 +1344,9 @@ impl Add<Duration> for StdDuration {
impl_add_assign!(Duration: Self, StdDuration);
impl AddAssign<Duration> for StdDuration {
+ /// # Panics
+ ///
+ /// This may panic if the resulting addition cannot be represented.
fn add_assign(&mut self, rhs: Duration) {
*self = (*self + rhs).try_into().expect(
"Cannot represent a resulting duration in std. Try `let x = x + rhs;`, which will \
@@ -1219,13 +1359,16 @@ impl Neg for Duration {
type Output = Self;
fn neg(self) -> Self::Output {
- Self::new_unchecked(-self.seconds, -self.nanoseconds)
+ self.checked_neg().expect("overflow when negating duration")
}
}
impl Sub for Duration {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, rhs: Self) -> Self::Output {
self.checked_sub(rhs)
.expect("overflow when subtracting durations")
@@ -1235,6 +1378,9 @@ impl Sub for Duration {
impl Sub<StdDuration> for Duration {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, rhs: StdDuration) -> Self::Output {
self - Self::try_from(rhs)
.expect("overflow converting `std::time::Duration` to `time::Duration`")
@@ -1244,6 +1390,9 @@ impl Sub<StdDuration> for Duration {
impl Sub<Duration> for StdDuration {
type Output = Duration;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, rhs: Duration) -> Self::Output {
Duration::try_from(self)
.expect("overflow converting `std::time::Duration` to `time::Duration`")
@@ -1254,6 +1403,9 @@ impl Sub<Duration> for StdDuration {
impl_sub_assign!(Duration: Self, StdDuration);
impl SubAssign<Duration> for StdDuration {
+ /// # Panics
+ ///
+ /// This may panic if the resulting subtraction can not be represented.
fn sub_assign(&mut self, rhs: Duration) {
*self = (*self - rhs).try_into().expect(
"Cannot represent a resulting duration in std. Try `let x = x - rhs;`, which will \
@@ -1271,7 +1423,7 @@ macro_rules! duration_mul_div_int {
fn mul(self, rhs: $type) -> Self::Output {
Self::nanoseconds_i128(
self.whole_nanoseconds()
- .checked_mul(rhs as _)
+ .checked_mul(rhs.cast_signed().extend::<i128>())
.expect("overflow when multiplying duration")
)
}
@@ -1289,7 +1441,9 @@ macro_rules! duration_mul_div_int {
type Output = Self;
fn div(self, rhs: $type) -> Self::Output {
- Self::nanoseconds_i128(self.whole_nanoseconds() / rhs as i128)
+ Self::nanoseconds_i128(
+ self.whole_nanoseconds() / rhs.cast_signed().extend::<i128>()
+ )
}
}
)+};
@@ -1386,14 +1540,18 @@ impl PartialEq<Duration> for StdDuration {
impl PartialOrd<StdDuration> for Duration {
fn partial_cmp(&self, rhs: &StdDuration) -> Option<Ordering> {
- if rhs.as_secs() > i64::MAX as _ {
+ if rhs.as_secs() > i64::MAX.cast_unsigned() {
return Some(Ordering::Less);
}
Some(
self.seconds
- .cmp(&(rhs.as_secs() as _))
- .then_with(|| self.nanoseconds.cmp(&(rhs.subsec_nanos() as _))),
+ .cmp(&rhs.as_secs().cast_signed())
+ .then_with(|| {
+ self.nanoseconds
+ .get()
+ .cmp(&rhs.subsec_nanos().cast_signed())
+ }),
)
}
}
diff --git a/third_party/rust/time/src/error/mod.rs b/third_party/rust/time/src/error/mod.rs
index 346b89f748..d2730c462d 100644
--- a/third_party/rust/time/src/error/mod.rs
+++ b/third_party/rust/time/src/error/mod.rs
@@ -36,31 +36,47 @@ pub use parse_from_description::ParseFromDescription;
#[cfg(feature = "parsing")]
pub use try_from_parsed::TryFromParsed;
+#[cfg(feature = "parsing")]
+use crate::internal_macros::bug;
+
/// A unified error type for anything returned by a method in the time crate.
///
/// This can be used when you either don't know or don't care about the exact error returned.
/// `Result<_, time::Error>` (or its alias `time::Result<_>`) will work in these situations.
#[allow(missing_copy_implementations, variant_size_differences)]
-#[allow(clippy::missing_docs_in_private_items)] // variants only
#[non_exhaustive]
#[derive(Debug)]
pub enum Error {
+ #[allow(missing_docs)]
ConversionRange(ConversionRange),
+ #[allow(missing_docs)]
ComponentRange(ComponentRange),
#[cfg(feature = "local-offset")]
+ #[allow(missing_docs)]
IndeterminateOffset(IndeterminateOffset),
#[cfg(feature = "formatting")]
+ #[allow(missing_docs)]
Format(Format),
#[cfg(feature = "parsing")]
+ #[allow(missing_docs)]
ParseFromDescription(ParseFromDescription),
#[cfg(feature = "parsing")]
+ #[allow(missing_docs)]
#[non_exhaustive]
+ #[deprecated(
+ since = "0.3.28",
+ note = "no longer output. moved to the `ParseFromDescription` variant"
+ )]
UnexpectedTrailingCharacters,
#[cfg(feature = "parsing")]
+ #[allow(missing_docs)]
TryFromParsed(TryFromParsed),
#[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))]
+ #[allow(missing_docs)]
InvalidFormatDescription(InvalidFormatDescription),
+ #[allow(missing_docs)]
DifferentVariant(DifferentVariant),
+ #[allow(missing_docs)]
InvalidVariant(InvalidVariant),
}
@@ -76,7 +92,8 @@ impl fmt::Display for Error {
#[cfg(feature = "parsing")]
Self::ParseFromDescription(e) => e.fmt(f),
#[cfg(feature = "parsing")]
- Self::UnexpectedTrailingCharacters => f.write_str("unexpected trailing characters"),
+ #[allow(deprecated)]
+ Self::UnexpectedTrailingCharacters => bug!("variant should not be used"),
#[cfg(feature = "parsing")]
Self::TryFromParsed(e) => e.fmt(f),
#[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))]
@@ -100,7 +117,8 @@ impl std::error::Error for Error {
#[cfg(feature = "parsing")]
Self::ParseFromDescription(err) => Some(err),
#[cfg(feature = "parsing")]
- Self::UnexpectedTrailingCharacters => None,
+ #[allow(deprecated)]
+ Self::UnexpectedTrailingCharacters => bug!("variant should not be used"),
#[cfg(feature = "parsing")]
Self::TryFromParsed(err) => Some(err),
#[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))]
diff --git a/third_party/rust/time/src/error/parse.rs b/third_party/rust/time/src/error/parse.rs
index b90ac74e73..09935b1c9b 100644
--- a/third_party/rust/time/src/error/parse.rs
+++ b/third_party/rust/time/src/error/parse.rs
@@ -3,18 +3,23 @@
use core::fmt;
use crate::error::{self, ParseFromDescription, TryFromParsed};
+use crate::internal_macros::bug;
/// An error that occurred at some stage of parsing.
#[allow(variant_size_differences)]
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Parse {
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
TryFromParsed(TryFromParsed),
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
ParseFromDescription(ParseFromDescription),
/// The input should have ended, but there were characters remaining.
#[non_exhaustive]
+ #[deprecated(
+ since = "0.3.28",
+ note = "no longer output. moved to the `ParseFromDescription` variant"
+ )]
UnexpectedTrailingCharacters,
}
@@ -23,7 +28,8 @@ impl fmt::Display for Parse {
match self {
Self::TryFromParsed(err) => err.fmt(f),
Self::ParseFromDescription(err) => err.fmt(f),
- Self::UnexpectedTrailingCharacters => f.write_str("unexpected trailing characters"),
+ #[allow(deprecated)]
+ Self::UnexpectedTrailingCharacters => bug!("variant should not be used"),
}
}
}
@@ -34,7 +40,8 @@ impl std::error::Error for Parse {
match self {
Self::TryFromParsed(err) => Some(err),
Self::ParseFromDescription(err) => Some(err),
- Self::UnexpectedTrailingCharacters => None,
+ #[allow(deprecated)]
+ Self::UnexpectedTrailingCharacters => bug!("variant should not be used"),
}
}
}
@@ -78,7 +85,8 @@ impl From<Parse> for crate::Error {
match err {
Parse::TryFromParsed(err) => Self::TryFromParsed(err),
Parse::ParseFromDescription(err) => Self::ParseFromDescription(err),
- Parse::UnexpectedTrailingCharacters => Self::UnexpectedTrailingCharacters,
+ #[allow(deprecated)]
+ Parse::UnexpectedTrailingCharacters => bug!("variant should not be used"),
}
}
}
@@ -89,7 +97,8 @@ impl TryFrom<crate::Error> for Parse {
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::ParseFromDescription(err) => Ok(Self::ParseFromDescription(err)),
- crate::Error::UnexpectedTrailingCharacters => Ok(Self::UnexpectedTrailingCharacters),
+ #[allow(deprecated)]
+ crate::Error::UnexpectedTrailingCharacters => bug!("variant should not be used"),
crate::Error::TryFromParsed(err) => Ok(Self::TryFromParsed(err)),
_ => Err(error::DifferentVariant),
}
diff --git a/third_party/rust/time/src/error/parse_from_description.rs b/third_party/rust/time/src/error/parse_from_description.rs
index 772f10d173..40124dce37 100644
--- a/third_party/rust/time/src/error/parse_from_description.rs
+++ b/third_party/rust/time/src/error/parse_from_description.rs
@@ -13,6 +13,9 @@ pub enum ParseFromDescription {
InvalidLiteral,
/// A dynamic component was not valid.
InvalidComponent(&'static str),
+ /// The input was expected to have ended, but there are characters that remain.
+ #[non_exhaustive]
+ UnexpectedTrailingCharacters,
}
impl fmt::Display for ParseFromDescription {
@@ -22,6 +25,9 @@ impl fmt::Display for ParseFromDescription {
Self::InvalidComponent(name) => {
write!(f, "the '{name}' component could not be parsed")
}
+ Self::UnexpectedTrailingCharacters => {
+ f.write_str("unexpected trailing characters; the end of input was expected")
+ }
}
}
}
diff --git a/third_party/rust/time/src/ext.rs b/third_party/rust/time/src/ext.rs
deleted file mode 100644
index 8b7759d8e9..0000000000
--- a/third_party/rust/time/src/ext.rs
+++ /dev/null
@@ -1,280 +0,0 @@
-//! Extension traits.
-
-use core::time::Duration as StdDuration;
-
-use crate::convert::*;
-use crate::Duration;
-
-/// Sealed trait to prevent downstream implementations.
-mod sealed {
- /// A trait that cannot be implemented by downstream users.
- pub trait Sealed {}
- impl Sealed for i64 {}
- impl Sealed for u64 {}
- impl Sealed for f64 {}
-}
-
-// region: NumericalDuration
-/// Create [`Duration`]s from numeric literals.
-///
-/// # Examples
-///
-/// Basic construction of [`Duration`]s.
-///
-/// ```rust
-/// # use time::{Duration, ext::NumericalDuration};
-/// assert_eq!(5.nanoseconds(), Duration::nanoseconds(5));
-/// assert_eq!(5.microseconds(), Duration::microseconds(5));
-/// assert_eq!(5.milliseconds(), Duration::milliseconds(5));
-/// assert_eq!(5.seconds(), Duration::seconds(5));
-/// assert_eq!(5.minutes(), Duration::minutes(5));
-/// assert_eq!(5.hours(), Duration::hours(5));
-/// assert_eq!(5.days(), Duration::days(5));
-/// assert_eq!(5.weeks(), Duration::weeks(5));
-/// ```
-///
-/// Signed integers work as well!
-///
-/// ```rust
-/// # use time::{Duration, ext::NumericalDuration};
-/// assert_eq!((-5).nanoseconds(), Duration::nanoseconds(-5));
-/// assert_eq!((-5).microseconds(), Duration::microseconds(-5));
-/// assert_eq!((-5).milliseconds(), Duration::milliseconds(-5));
-/// assert_eq!((-5).seconds(), Duration::seconds(-5));
-/// assert_eq!((-5).minutes(), Duration::minutes(-5));
-/// assert_eq!((-5).hours(), Duration::hours(-5));
-/// assert_eq!((-5).days(), Duration::days(-5));
-/// assert_eq!((-5).weeks(), Duration::weeks(-5));
-/// ```
-///
-/// Just like any other [`Duration`], they can be added, subtracted, etc.
-///
-/// ```rust
-/// # use time::ext::NumericalDuration;
-/// assert_eq!(2.seconds() + 500.milliseconds(), 2_500.milliseconds());
-/// assert_eq!(2.seconds() - 500.milliseconds(), 1_500.milliseconds());
-/// ```
-///
-/// When called on floating point values, any remainder of the floating point value will be
-/// truncated. Keep in mind that floating point numbers are inherently imprecise and have limited
-/// capacity.
-pub trait NumericalDuration: sealed::Sealed {
- /// Create a [`Duration`] from the number of nanoseconds.
- fn nanoseconds(self) -> Duration;
- /// Create a [`Duration`] from the number of microseconds.
- fn microseconds(self) -> Duration;
- /// Create a [`Duration`] from the number of milliseconds.
- fn milliseconds(self) -> Duration;
- /// Create a [`Duration`] from the number of seconds.
- fn seconds(self) -> Duration;
- /// Create a [`Duration`] from the number of minutes.
- fn minutes(self) -> Duration;
- /// Create a [`Duration`] from the number of hours.
- fn hours(self) -> Duration;
- /// Create a [`Duration`] from the number of days.
- fn days(self) -> Duration;
- /// Create a [`Duration`] from the number of weeks.
- fn weeks(self) -> Duration;
-}
-
-impl NumericalDuration for i64 {
- fn nanoseconds(self) -> Duration {
- Duration::nanoseconds(self)
- }
-
- fn microseconds(self) -> Duration {
- Duration::microseconds(self)
- }
-
- fn milliseconds(self) -> Duration {
- Duration::milliseconds(self)
- }
-
- fn seconds(self) -> Duration {
- Duration::seconds(self)
- }
-
- fn minutes(self) -> Duration {
- Duration::minutes(self)
- }
-
- fn hours(self) -> Duration {
- Duration::hours(self)
- }
-
- fn days(self) -> Duration {
- Duration::days(self)
- }
-
- fn weeks(self) -> Duration {
- Duration::weeks(self)
- }
-}
-
-impl NumericalDuration for f64 {
- fn nanoseconds(self) -> Duration {
- Duration::nanoseconds(self as _)
- }
-
- fn microseconds(self) -> Duration {
- Duration::nanoseconds((self * Nanosecond.per(Microsecond) as Self) as _)
- }
-
- fn milliseconds(self) -> Duration {
- Duration::nanoseconds((self * Nanosecond.per(Millisecond) as Self) as _)
- }
-
- fn seconds(self) -> Duration {
- Duration::nanoseconds((self * Nanosecond.per(Second) as Self) as _)
- }
-
- fn minutes(self) -> Duration {
- Duration::nanoseconds((self * Nanosecond.per(Minute) as Self) as _)
- }
-
- fn hours(self) -> Duration {
- Duration::nanoseconds((self * Nanosecond.per(Hour) as Self) as _)
- }
-
- fn days(self) -> Duration {
- Duration::nanoseconds((self * Nanosecond.per(Day) as Self) as _)
- }
-
- fn weeks(self) -> Duration {
- Duration::nanoseconds((self * Nanosecond.per(Week) as Self) as _)
- }
-}
-// endregion NumericalDuration
-
-// region: NumericalStdDuration
-/// Create [`std::time::Duration`]s from numeric literals.
-///
-/// # Examples
-///
-/// Basic construction of [`std::time::Duration`]s.
-///
-/// ```rust
-/// # use time::ext::NumericalStdDuration;
-/// # use core::time::Duration;
-/// assert_eq!(5.std_nanoseconds(), Duration::from_nanos(5));
-/// assert_eq!(5.std_microseconds(), Duration::from_micros(5));
-/// assert_eq!(5.std_milliseconds(), Duration::from_millis(5));
-/// assert_eq!(5.std_seconds(), Duration::from_secs(5));
-/// assert_eq!(5.std_minutes(), Duration::from_secs(5 * 60));
-/// assert_eq!(5.std_hours(), Duration::from_secs(5 * 3_600));
-/// assert_eq!(5.std_days(), Duration::from_secs(5 * 86_400));
-/// assert_eq!(5.std_weeks(), Duration::from_secs(5 * 604_800));
-/// ```
-///
-/// Just like any other [`std::time::Duration`], they can be added, subtracted, etc.
-///
-/// ```rust
-/// # use time::ext::NumericalStdDuration;
-/// assert_eq!(
-/// 2.std_seconds() + 500.std_milliseconds(),
-/// 2_500.std_milliseconds()
-/// );
-/// assert_eq!(
-/// 2.std_seconds() - 500.std_milliseconds(),
-/// 1_500.std_milliseconds()
-/// );
-/// ```
-///
-/// When called on floating point values, any remainder of the floating point value will be
-/// truncated. Keep in mind that floating point numbers are inherently imprecise and have limited
-/// capacity.
-pub trait NumericalStdDuration: sealed::Sealed {
- /// Create a [`std::time::Duration`] from the number of nanoseconds.
- fn std_nanoseconds(self) -> StdDuration;
- /// Create a [`std::time::Duration`] from the number of microseconds.
- fn std_microseconds(self) -> StdDuration;
- /// Create a [`std::time::Duration`] from the number of milliseconds.
- fn std_milliseconds(self) -> StdDuration;
- /// Create a [`std::time::Duration`] from the number of seconds.
- fn std_seconds(self) -> StdDuration;
- /// Create a [`std::time::Duration`] from the number of minutes.
- fn std_minutes(self) -> StdDuration;
- /// Create a [`std::time::Duration`] from the number of hours.
- fn std_hours(self) -> StdDuration;
- /// Create a [`std::time::Duration`] from the number of days.
- fn std_days(self) -> StdDuration;
- /// Create a [`std::time::Duration`] from the number of weeks.
- fn std_weeks(self) -> StdDuration;
-}
-
-impl NumericalStdDuration for u64 {
- fn std_nanoseconds(self) -> StdDuration {
- StdDuration::from_nanos(self)
- }
-
- fn std_microseconds(self) -> StdDuration {
- StdDuration::from_micros(self)
- }
-
- fn std_milliseconds(self) -> StdDuration {
- StdDuration::from_millis(self)
- }
-
- fn std_seconds(self) -> StdDuration {
- StdDuration::from_secs(self)
- }
-
- fn std_minutes(self) -> StdDuration {
- StdDuration::from_secs(self * Second.per(Minute) as Self)
- }
-
- fn std_hours(self) -> StdDuration {
- StdDuration::from_secs(self * Second.per(Hour) as Self)
- }
-
- fn std_days(self) -> StdDuration {
- StdDuration::from_secs(self * Second.per(Day) as Self)
- }
-
- fn std_weeks(self) -> StdDuration {
- StdDuration::from_secs(self * Second.per(Week) as Self)
- }
-}
-
-impl NumericalStdDuration for f64 {
- fn std_nanoseconds(self) -> StdDuration {
- assert!(self >= 0.);
- StdDuration::from_nanos(self as _)
- }
-
- fn std_microseconds(self) -> StdDuration {
- assert!(self >= 0.);
- StdDuration::from_nanos((self * Nanosecond.per(Microsecond) as Self) as _)
- }
-
- fn std_milliseconds(self) -> StdDuration {
- assert!(self >= 0.);
- StdDuration::from_nanos((self * Nanosecond.per(Millisecond) as Self) as _)
- }
-
- fn std_seconds(self) -> StdDuration {
- assert!(self >= 0.);
- StdDuration::from_nanos((self * Nanosecond.per(Second) as Self) as _)
- }
-
- fn std_minutes(self) -> StdDuration {
- assert!(self >= 0.);
- StdDuration::from_nanos((self * Nanosecond.per(Minute) as Self) as _)
- }
-
- fn std_hours(self) -> StdDuration {
- assert!(self >= 0.);
- StdDuration::from_nanos((self * Nanosecond.per(Hour) as Self) as _)
- }
-
- fn std_days(self) -> StdDuration {
- assert!(self >= 0.);
- StdDuration::from_nanos((self * Nanosecond.per(Day) as Self) as _)
- }
-
- fn std_weeks(self) -> StdDuration {
- assert!(self >= 0.);
- StdDuration::from_nanos((self * Nanosecond.per(Week) as Self) as _)
- }
-}
-// endregion NumericalStdDuration
diff --git a/third_party/rust/time/src/format_description/borrowed_format_item.rs b/third_party/rust/time/src/format_description/borrowed_format_item.rs
index 425b902878..4f3e6386c7 100644
--- a/third_party/rust/time/src/format_description/borrowed_format_item.rs
+++ b/third_party/rust/time/src/format_description/borrowed_format_item.rs
@@ -5,6 +5,19 @@ use alloc::string::String;
#[cfg(feature = "alloc")]
use core::fmt;
+/// A complete description of how to format and parse a type.
+///
+/// This alias exists for backwards-compatibility. It is recommended to use `BorrowedFormatItem`
+/// for clarity, as it is more explicit that the data is borrowed rather than owned.
+#[cfg(doc)]
+#[deprecated(
+ since = "0.3.35",
+ note = "use `BorrowedFormatItem` instead for clarity"
+)]
+pub type FormatItem<'a> = BorrowedFormatItem<'a>;
+
+#[cfg(not(doc))]
+pub use self::BorrowedFormatItem as FormatItem;
use crate::error;
use crate::format_description::Component;
@@ -15,8 +28,8 @@ use crate::format_description::Component;
pub enum BorrowedFormatItem<'a> {
/// Bytes that are formatted as-is.
///
- /// **Note**: If you call the `format` method that returns a `String`, these bytes will be
- /// passed through `String::from_utf8_lossy`.
+ /// **Note**: These bytes **should** be UTF-8, but are not required to be. The value is passed
+ /// through `String::from_utf8_lossy` when necessary.
Literal(&'a [u8]),
/// A minimal representation of a single non-literal item.
Component(Component),
diff --git a/third_party/rust/time/src/format_description/component.rs b/third_party/rust/time/src/format_description/component.rs
index 6725590815..9119c0905c 100644
--- a/third_party/rust/time/src/format_description/component.rs
+++ b/third_party/rust/time/src/format_description/component.rs
@@ -38,4 +38,7 @@ pub enum Component {
Ignore(modifier::Ignore),
/// A Unix timestamp.
UnixTimestamp(modifier::UnixTimestamp),
+ /// The end of input. Parsing this component will fail if there is any input remaining. This
+ /// component neither affects formatting nor consumes any input when parsing.
+ End(modifier::End),
}
diff --git a/third_party/rust/time/src/format_description/mod.rs b/third_party/rust/time/src/format_description/mod.rs
index befae8b569..67882afe78 100644
--- a/third_party/rust/time/src/format_description/mod.rs
+++ b/third_party/rust/time/src/format_description/mod.rs
@@ -15,7 +15,9 @@ mod owned_format_item;
#[cfg(feature = "alloc")]
mod parse;
-pub use borrowed_format_item::BorrowedFormatItem as FormatItem;
+pub use borrowed_format_item::BorrowedFormatItem;
+#[allow(deprecated)]
+pub use borrowed_format_item::FormatItem;
#[cfg(feature = "alloc")]
pub use owned_format_item::OwnedFormatItem;
diff --git a/third_party/rust/time/src/format_description/modifier.rs b/third_party/rust/time/src/format_description/modifier.rs
index cdac1ae974..3a57bb6f66 100644
--- a/third_party/rust/time/src/format_description/modifier.rs
+++ b/third_party/rust/time/src/format_description/modifier.rs
@@ -279,6 +279,13 @@ pub struct UnixTimestamp {
pub sign_is_mandatory: bool,
}
+/// The end of input.
+///
+/// There is currently not customization for this modifier.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct End;
+
/// Generate the provided code if and only if `pub` is present.
macro_rules! if_pub {
(pub $(#[$attr:meta])*; $($x:tt)*) => {
@@ -385,10 +392,10 @@ impl_const_default! {
/// Creates a modifier that indicates the stringified value contains [one or more
/// digits](SubsecondDigits::OneOrMore).
@pub Subsecond => Self { digits: SubsecondDigits::OneOrMore };
- /// Creates a modifier that indicates the value uses the `+` sign for all positive values
- /// and is [padded with zeroes](Padding::Zero).
+ /// Creates a modifier that indicates the value only uses a sign for negative values and is
+ /// [padded with zeroes](Padding::Zero).
@pub OffsetHour => Self {
- sign_is_mandatory: true,
+ sign_is_mandatory: false,
padding: Padding::Zero,
};
/// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
@@ -406,4 +413,6 @@ impl_const_default! {
precision: UnixTimestampPrecision::Second,
sign_is_mandatory: false,
};
+ /// Creates a modifier used to represent the end of input.
+ @pub End => End;
}
diff --git a/third_party/rust/time/src/format_description/owned_format_item.rs b/third_party/rust/time/src/format_description/owned_format_item.rs
index 6f7f7c2337..31ea9f90ba 100644
--- a/third_party/rust/time/src/format_description/owned_format_item.rs
+++ b/third_party/rust/time/src/format_description/owned_format_item.rs
@@ -6,7 +6,7 @@ use alloc::vec::Vec;
use core::fmt;
use crate::error;
-use crate::format_description::{Component, FormatItem};
+use crate::format_description::{BorrowedFormatItem, Component};
/// A complete description of how to format and parse a type.
#[non_exhaustive]
@@ -14,8 +14,8 @@ use crate::format_description::{Component, FormatItem};
pub enum OwnedFormatItem {
/// Bytes that are formatted as-is.
///
- /// **Note**: If you call the `format` method that returns a `String`, these bytes will be
- /// passed through `String::from_utf8_lossy`.
+ /// **Note**: These bytes **should** be UTF-8, but are not required to be. The value is passed
+ /// through `String::from_utf8_lossy` when necessary.
Literal(Box<[u8]>),
/// A minimal representation of a single non-literal item.
Component(Component),
@@ -46,18 +46,20 @@ impl fmt::Debug for OwnedFormatItem {
}
// region: conversions from FormatItem
-impl From<FormatItem<'_>> for OwnedFormatItem {
- fn from(item: FormatItem<'_>) -> Self {
+impl From<BorrowedFormatItem<'_>> for OwnedFormatItem {
+ fn from(item: BorrowedFormatItem<'_>) -> Self {
(&item).into()
}
}
-impl From<&FormatItem<'_>> for OwnedFormatItem {
- fn from(item: &FormatItem<'_>) -> Self {
+impl From<&BorrowedFormatItem<'_>> for OwnedFormatItem {
+ fn from(item: &BorrowedFormatItem<'_>) -> Self {
match item {
- FormatItem::Literal(literal) => Self::Literal(literal.to_vec().into_boxed_slice()),
- FormatItem::Component(component) => Self::Component(*component),
- FormatItem::Compound(compound) => Self::Compound(
+ BorrowedFormatItem::Literal(literal) => {
+ Self::Literal(literal.to_vec().into_boxed_slice())
+ }
+ BorrowedFormatItem::Component(component) => Self::Component(*component),
+ BorrowedFormatItem::Compound(compound) => Self::Compound(
compound
.iter()
.cloned()
@@ -65,8 +67,8 @@ impl From<&FormatItem<'_>> for OwnedFormatItem {
.collect::<Vec<_>>()
.into_boxed_slice(),
),
- FormatItem::Optional(item) => Self::Optional(Box::new((*item).into())),
- FormatItem::First(items) => Self::First(
+ BorrowedFormatItem::Optional(item) => Self::Optional(Box::new((*item).into())),
+ BorrowedFormatItem::First(items) => Self::First(
items
.iter()
.cloned()
@@ -78,13 +80,13 @@ impl From<&FormatItem<'_>> for OwnedFormatItem {
}
}
-impl From<Vec<FormatItem<'_>>> for OwnedFormatItem {
- fn from(items: Vec<FormatItem<'_>>) -> Self {
+impl From<Vec<BorrowedFormatItem<'_>>> for OwnedFormatItem {
+ fn from(items: Vec<BorrowedFormatItem<'_>>) -> Self {
items.as_slice().into()
}
}
-impl<'a, T: AsRef<[FormatItem<'a>]> + ?Sized> From<&T> for OwnedFormatItem {
+impl<'a, T: AsRef<[BorrowedFormatItem<'a>]> + ?Sized> From<&T> for OwnedFormatItem {
fn from(items: &T) -> Self {
Self::Compound(
items
diff --git a/third_party/rust/time/src/format_description/parse/ast.rs b/third_party/rust/time/src/format_description/parse/ast.rs
index 4957fe0968..c7fc5e05a0 100644
--- a/third_party/rust/time/src/format_description/parse/ast.rs
+++ b/third_party/rust/time/src/format_description/parse/ast.rs
@@ -6,6 +6,7 @@ use alloc::vec::Vec;
use core::iter;
use super::{lexer, unused, Error, Location, Spanned, SpannedValue, Unused};
+use crate::internal_macros::bug;
/// One part of a complete format description.
pub(super) enum Item<'a> {
diff --git a/third_party/rust/time/src/format_description/parse/format_item.rs b/third_party/rust/time/src/format_description/parse/format_item.rs
index c0e64d92a2..2e1ee6a74e 100644
--- a/third_party/rust/time/src/format_description/parse/format_item.rs
+++ b/third_party/rust/time/src/format_description/parse/format_item.rs
@@ -6,6 +6,7 @@ use core::num::NonZeroU16;
use core::str::{self, FromStr};
use super::{ast, unused, Error, Span, Spanned};
+use crate::internal_macros::bug;
/// Parse an AST iterator into a sequence of format items.
pub(super) fn parse<'a>(
@@ -101,7 +102,7 @@ impl Item<'_> {
}
}
-impl<'a> TryFrom<Item<'a>> for crate::format_description::FormatItem<'a> {
+impl<'a> TryFrom<Item<'a>> for crate::format_description::BorrowedFormatItem<'a> {
type Error = Error;
fn try_from(item: Item<'a>) -> Result<Self, Self::Error> {
@@ -148,14 +149,9 @@ impl From<Item<'_>> for crate::format_description::OwnedFormatItem {
impl<'a> From<Box<[Item<'a>]>> for crate::format_description::OwnedFormatItem {
fn from(items: Box<[Item<'a>]>) -> Self {
let items = items.into_vec();
- if items.len() == 1 {
- if let Ok([item]) = <[_; 1]>::try_from(items) {
- item.into()
- } else {
- bug!("the length was just checked to be 1")
- }
- } else {
- Self::Compound(items.into_iter().map(Self::from).collect())
+ match <[_; 1]>::try_from(items) {
+ Ok([item]) => item.into(),
+ Err(vec) => Self::Compound(vec.into_iter().map(Into::into).collect()),
}
}
}
@@ -190,6 +186,8 @@ macro_rules! component_definition {
_component_span: Span,
) -> Result<Self, Error>
{
+ // rustc will complain if the modifier is empty.
+ #[allow(unused_mut)]
let mut this = Self {
$($field: None),*
};
@@ -280,6 +278,7 @@ component_definition! {
Day = "day" {
padding = "padding": Option<Padding> => padding,
},
+ End = "end" {},
Hour = "hour" {
padding = "padding": Option<Padding> => padding,
base = "repr": Option<HourBase> => is_12_hour_clock,
diff --git a/third_party/rust/time/src/format_description/parse/lexer.rs b/third_party/rust/time/src/format_description/parse/lexer.rs
index 1604fd4971..2979fff9ee 100644
--- a/third_party/rust/time/src/format_description/parse/lexer.rs
+++ b/third_party/rust/time/src/format_description/parse/lexer.rs
@@ -7,7 +7,7 @@ use super::{unused, Error, Location, Spanned, SpannedValue};
/// An iterator over the lexed tokens.
pub(super) struct Lexed<I: Iterator> {
/// The internal iterator.
- iter: core::iter::Peekable<I>,
+ iter: iter::Peekable<I>,
}
impl<I: Iterator> Iterator for Lexed<I> {
diff --git a/third_party/rust/time/src/format_description/parse/mod.rs b/third_party/rust/time/src/format_description/parse/mod.rs
index 2ab58f172a..602ecf71f6 100644
--- a/third_party/rust/time/src/format_description/parse/mod.rs
+++ b/third_party/rust/time/src/format_description/parse/mod.rs
@@ -3,6 +3,8 @@
use alloc::boxed::Box;
use alloc::vec::Vec;
+use crate::{error, format_description};
+
/// A helper macro to make version restrictions simpler to read and write.
macro_rules! version {
($range:expr) => {
@@ -40,8 +42,7 @@ impl<const N: usize> Version<N> {
/// `parse_borrowed`.
pub fn parse(
s: &str,
-) -> Result<Vec<crate::format_description::FormatItem<'_>>, crate::error::InvalidFormatDescription>
-{
+) -> Result<Vec<format_description::BorrowedFormatItem<'_>>, error::InvalidFormatDescription> {
parse_borrowed::<1>(s)
}
@@ -52,8 +53,7 @@ pub fn parse(
/// description is provided as the const parameter. **It is recommended to use version 2.**
pub fn parse_borrowed<const VERSION: usize>(
s: &str,
-) -> Result<Vec<crate::format_description::FormatItem<'_>>, crate::error::InvalidFormatDescription>
-{
+) -> Result<Vec<format_description::BorrowedFormatItem<'_>>, error::InvalidFormatDescription> {
validate_version!(VERSION);
let mut lexed = lexer::lex::<VERSION>(s.as_bytes());
let ast = ast::parse::<_, VERSION>(&mut lexed);
@@ -75,14 +75,12 @@ pub fn parse_borrowed<const VERSION: usize>(
/// [`OwnedFormatItem`]: crate::format_description::OwnedFormatItem
pub fn parse_owned<const VERSION: usize>(
s: &str,
-) -> Result<crate::format_description::OwnedFormatItem, crate::error::InvalidFormatDescription> {
+) -> Result<format_description::OwnedFormatItem, error::InvalidFormatDescription> {
validate_version!(VERSION);
let mut lexed = lexer::lex::<VERSION>(s.as_bytes());
let ast = ast::parse::<_, VERSION>(&mut lexed);
let format_items = format_item::parse(ast);
- let items = format_items
- .map(|res| res.map(Into::into))
- .collect::<Result<Box<_>, _>>()?;
+ let items = format_items.collect::<Result<Box<_>, _>>()?;
Ok(items.into())
}
@@ -222,10 +220,10 @@ struct Error {
/// The internal error.
_inner: Unused<ErrorInner>,
/// The error needed for interoperability with the rest of `time`.
- public: crate::error::InvalidFormatDescription,
+ public: error::InvalidFormatDescription,
}
-impl From<Error> for crate::error::InvalidFormatDescription {
+impl From<Error> for error::InvalidFormatDescription {
fn from(error: Error) -> Self {
error.public
}
@@ -239,7 +237,6 @@ impl From<Error> for crate::error::InvalidFormatDescription {
struct Unused<T>(core::marker::PhantomData<T>);
/// Indicate that a value is currently unused.
-#[allow(clippy::missing_const_for_fn)] // false positive
fn unused<T>(_: T) -> Unused<T> {
Unused(core::marker::PhantomData)
}
diff --git a/third_party/rust/time/src/format_description/well_known/iso8601.rs b/third_party/rust/time/src/format_description/well_known/iso8601.rs
index f19181a926..6b8ff2ab63 100644
--- a/third_party/rust/time/src/format_description/well_known/iso8601.rs
+++ b/third_party/rust/time/src/format_description/well_known/iso8601.rs
@@ -4,23 +4,9 @@ mod adt_hack;
use core::num::NonZeroU8;
-pub use self::adt_hack::{DoNotRelyOnWhatThisIs, EncodedConfig};
-
-/// A configuration for [`Iso8601`] that only parses values.
-const PARSING_ONLY: EncodedConfig = Config {
- formatted_components: FormattedComponents::None,
- use_separators: false,
- year_is_six_digits: false,
- date_kind: DateKind::Calendar,
- time_precision: TimePrecision::Hour {
- decimal_digits: None,
- },
- offset_precision: OffsetPrecision::Hour,
-}
-.encode();
-
-/// The default configuration for [`Iso8601`].
-const DEFAULT_CONFIG: EncodedConfig = Config::DEFAULT.encode();
+#[doc(hidden, no_inline)]
+pub use self::adt_hack::DoNotRelyOnWhatThisIs;
+pub use self::adt_hack::EncodedConfig;
/// The format described in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html).
///
@@ -43,7 +29,7 @@ const DEFAULT_CONFIG: EncodedConfig = Config::DEFAULT.encode();
/// # Ok::<_, time::Error>(())
/// ```
#[derive(Clone, Copy, PartialEq, Eq)]
-pub struct Iso8601<const CONFIG: EncodedConfig = DEFAULT_CONFIG>;
+pub struct Iso8601<const CONFIG: EncodedConfig = { Config::DEFAULT.encode() }>;
impl<const CONFIG: EncodedConfig> core::fmt::Debug for Iso8601<CONFIG> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
@@ -53,7 +39,18 @@ impl<const CONFIG: EncodedConfig> core::fmt::Debug for Iso8601<CONFIG> {
}
}
-impl Iso8601<DEFAULT_CONFIG> {
+/// Define associated constants for `Iso8601`.
+macro_rules! define_assoc_consts {
+ ($($(#[$doc:meta])* $vis:vis const $const_name:ident = $format:expr;)*) => {$(
+ const $const_name: EncodedConfig = $format.encode();
+ impl Iso8601<$const_name> {
+ $(#[$doc])*
+ $vis const $const_name: Self = Self;
+ }
+ )*};
+}
+
+define_assoc_consts! {
/// An [`Iso8601`] with the default configuration.
///
/// The following is the default behavior:
@@ -66,15 +63,29 @@ impl Iso8601<DEFAULT_CONFIG> {
/// - The time has precision to the second and nine decimal digits.
/// - The UTC offset has precision to the minute.
///
- /// If you need different behavior, use [`Config::DEFAULT`] and [`Config`]'s methods to create
- /// a custom configuration.
- pub const DEFAULT: Self = Self;
-}
-
-impl Iso8601<PARSING_ONLY> {
+ /// If you need different behavior, use another associated constant. For full customization, use
+ /// [`Config::DEFAULT`] and [`Config`]'s methods to create a custom configuration.
+ pub const DEFAULT = Config::DEFAULT;
/// An [`Iso8601`] that can only be used for parsing. Using this to format a value is
/// unspecified behavior.
- pub const PARSING: Self = Self;
+ pub const PARSING = Config::PARSING;
+ /// An [`Iso8601`] that handles only the date, but is otherwise the same as [`Config::DEFAULT`].
+ pub const DATE = Config::DEFAULT.set_formatted_components(FormattedComponents::Date);
+ /// An [`Iso8601`] that handles only the time, but is otherwise the same as [`Config::DEFAULT`].
+ pub const TIME = Config::DEFAULT.set_formatted_components(FormattedComponents::Time);
+ /// An [`Iso8601`] that handles only the UTC offset, but is otherwise the same as
+ /// [`Config::DEFAULT`].
+ pub const OFFSET = Config::DEFAULT.set_formatted_components(FormattedComponents::Offset);
+ /// An [`Iso8601`] that handles the date and time, but is otherwise the same as
+ /// [`Config::DEFAULT`].
+ pub const DATE_TIME = Config::DEFAULT.set_formatted_components(FormattedComponents::DateTime);
+ /// An [`Iso8601`] that handles the date, time, and UTC offset. This is the same as
+ /// [`Config::DEFAULT`].
+ pub const DATE_TIME_OFFSET = Config::DEFAULT;
+ /// An [`Iso8601`] that handles the time and UTC offset, but is otherwise the same as
+ /// [`Config::DEFAULT`].
+ pub const TIME_OFFSET = Config::DEFAULT
+ .set_formatted_components(FormattedComponents::TimeOffset);
}
/// Which components to format.
@@ -114,19 +125,19 @@ pub enum TimePrecision {
/// Format the hour only. Minutes, seconds, and nanoseconds will be represented with the
/// specified number of decimal digits, if any.
Hour {
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
decimal_digits: Option<NonZeroU8>,
},
/// Format the hour and minute. Seconds and nanoseconds will be represented with the specified
/// number of decimal digits, if any.
Minute {
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
decimal_digits: Option<NonZeroU8>,
},
/// Format the hour, minute, and second. Nanoseconds will be represented with the specified
/// number of decimal digits, if any.
Second {
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
decimal_digits: Option<NonZeroU8>,
},
}
@@ -186,6 +197,19 @@ impl Config {
offset_precision: OffsetPrecision::Minute,
};
+ /// A configuration that can only be used for parsing. Using this to format a value is
+ /// unspecified behavior.
+ const PARSING: Self = Self {
+ formatted_components: FormattedComponents::None,
+ use_separators: false,
+ year_is_six_digits: false,
+ date_kind: DateKind::Calendar,
+ time_precision: TimePrecision::Hour {
+ decimal_digits: None,
+ },
+ offset_precision: OffsetPrecision::Hour,
+ };
+
/// Set whether the format the date, time, and/or UTC offset.
pub const fn set_formatted_components(self, formatted_components: FormattedComponents) -> Self {
Self {
diff --git a/third_party/rust/time/src/format_description/well_known/iso8601/adt_hack.rs b/third_party/rust/time/src/format_description/well_known/iso8601/adt_hack.rs
index 1fdecaf269..12b1c725fd 100644
--- a/third_party/rust/time/src/format_description/well_known/iso8601/adt_hack.rs
+++ b/third_party/rust/time/src/format_description/well_known/iso8601/adt_hack.rs
@@ -49,11 +49,10 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
}
impl Config {
- /// Encode the configuration, permitting it to be used as a const parameter of
- /// [`Iso8601`](super::Iso8601).
+ /// Encode the configuration, permitting it to be used as a const parameter of [`Iso8601`].
///
- /// The value returned by this method must only be used as a const parameter to
- /// [`Iso8601`](super::Iso8601). Any other usage is unspecified behavior.
+ /// The value returned by this method must only be used as a const parameter to [`Iso8601`]. Any
+ /// other usage is unspecified behavior.
pub const fn encode(&self) -> EncodedConfig {
let mut bytes = [0; EncodedConfig::BITS as usize / 8];
diff --git a/third_party/rust/time/src/formatting/formattable.rs b/third_party/rust/time/src/formatting/formattable.rs
index 2b4b350883..7ee17a5cc1 100644
--- a/third_party/rust/time/src/formatting/formattable.rs
+++ b/third_party/rust/time/src/formatting/formattable.rs
@@ -1,11 +1,15 @@
//! A trait that can be used to format an item from its components.
+use alloc::string::String;
+use alloc::vec::Vec;
use core::ops::Deref;
use std::io;
+use num_conv::prelude::*;
+
use crate::format_description::well_known::iso8601::EncodedConfig;
use crate::format_description::well_known::{Iso8601, Rfc2822, Rfc3339};
-use crate::format_description::{FormatItem, OwnedFormatItem};
+use crate::format_description::{BorrowedFormatItem, OwnedFormatItem};
use crate::formatting::{
format_component, format_number_pad_zero, iso8601, write, MONTH_NAMES, WEEKDAY_NAMES,
};
@@ -19,8 +23,8 @@ use crate::{error, Date, Time, UtcOffset};
/// a String from their data. See the respective methods for usage examples.
#[cfg_attr(__time_03_docs, doc(notable_trait))]
pub trait Formattable: sealed::Sealed {}
-impl Formattable for FormatItem<'_> {}
-impl Formattable for [FormatItem<'_>] {}
+impl Formattable for BorrowedFormatItem<'_> {}
+impl Formattable for [BorrowedFormatItem<'_>] {}
impl Formattable for OwnedFormatItem {}
impl Formattable for [OwnedFormatItem] {}
impl Formattable for Rfc3339 {}
@@ -59,7 +63,7 @@ mod sealed {
}
// region: custom formats
-impl<'a> sealed::Sealed for FormatItem<'a> {
+impl sealed::Sealed for BorrowedFormatItem<'_> {
fn format_into(
&self,
output: &mut impl io::Write,
@@ -80,7 +84,7 @@ impl<'a> sealed::Sealed for FormatItem<'a> {
}
}
-impl<'a> sealed::Sealed for [FormatItem<'a>] {
+impl sealed::Sealed for [BorrowedFormatItem<'_>] {
fn format_into(
&self,
output: &mut impl io::Write,
@@ -175,14 +179,17 @@ impl sealed::Sealed for Rfc2822 {
bytes += write(
output,
- &WEEKDAY_NAMES[date.weekday().number_days_from_monday() as usize][..3],
+ &WEEKDAY_NAMES[date.weekday().number_days_from_monday().extend::<usize>()][..3],
)?;
bytes += write(output, b", ")?;
bytes += format_number_pad_zero::<2>(output, day)?;
bytes += write(output, b" ")?;
- bytes += write(output, &MONTH_NAMES[month as usize - 1][..3])?;
+ bytes += write(
+ output,
+ &MONTH_NAMES[u8::from(month).extend::<usize>() - 1][..3],
+ )?;
bytes += write(output, b" ")?;
- bytes += format_number_pad_zero::<4>(output, year as u32)?;
+ bytes += format_number_pad_zero::<4>(output, year.cast_unsigned())?;
bytes += write(output, b" ")?;
bytes += format_number_pad_zero::<2>(output, time.hour())?;
bytes += write(output, b":")?;
@@ -217,13 +224,16 @@ impl sealed::Sealed for Rfc3339 {
if !(0..10_000).contains(&year) {
return Err(error::Format::InvalidComponent("year"));
}
+ if offset.whole_hours().unsigned_abs() > 23 {
+ return Err(error::Format::InvalidComponent("offset_hour"));
+ }
if offset.seconds_past_minute() != 0 {
return Err(error::Format::InvalidComponent("offset_second"));
}
- bytes += format_number_pad_zero::<4>(output, year as u32)?;
+ bytes += format_number_pad_zero::<4>(output, year.cast_unsigned())?;
bytes += write(output, b"-")?;
- bytes += format_number_pad_zero::<2>(output, date.month() as u8)?;
+ bytes += format_number_pad_zero::<2>(output, u8::from(date.month()))?;
bytes += write(output, b"-")?;
bytes += format_number_pad_zero::<2>(output, date.day())?;
bytes += write(output, b"T")?;
diff --git a/third_party/rust/time/src/formatting/iso8601.rs b/third_party/rust/time/src/formatting/iso8601.rs
index 29d443ef46..ee49d7d896 100644
--- a/third_party/rust/time/src/formatting/iso8601.rs
+++ b/third_party/rust/time/src/formatting/iso8601.rs
@@ -2,6 +2,8 @@
use std::io;
+use num_conv::prelude::*;
+
use crate::convert::*;
use crate::format_description::well_known::iso8601::{
DateKind, EncodedConfig, OffsetPrecision, TimePrecision,
@@ -26,10 +28,10 @@ pub(super) fn format_date<const CONFIG: EncodedConfig>(
} else if !(0..=9999).contains(&year) {
return Err(error::Format::InvalidComponent("year"));
} else {
- bytes += format_number_pad_zero::<4>(output, year as u32)?;
+ bytes += format_number_pad_zero::<4>(output, year.cast_unsigned())?;
}
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?;
- bytes += format_number_pad_zero::<2>(output, month as u8)?;
+ bytes += format_number_pad_zero::<2>(output, u8::from(month))?;
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?;
bytes += format_number_pad_zero::<2>(output, day)?;
}
@@ -41,7 +43,7 @@ pub(super) fn format_date<const CONFIG: EncodedConfig>(
} else if !(0..=9999).contains(&year) {
return Err(error::Format::InvalidComponent("year"));
} else {
- bytes += format_number_pad_zero::<4>(output, year as u32)?;
+ bytes += format_number_pad_zero::<4>(output, year.cast_unsigned())?;
}
bytes += write_if_else(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-W", b"W")?;
bytes += format_number_pad_zero::<2>(output, week)?;
@@ -56,7 +58,7 @@ pub(super) fn format_date<const CONFIG: EncodedConfig>(
} else if !(0..=9999).contains(&year) {
return Err(error::Format::InvalidComponent("year"));
} else {
- bytes += format_number_pad_zero::<4>(output, year as u32)?;
+ bytes += format_number_pad_zero::<4>(output, year.cast_unsigned())?;
}
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?;
bytes += format_number_pad_zero::<3>(output, day)?;
@@ -85,17 +87,17 @@ pub(super) fn format_time<const CONFIG: EncodedConfig>(
match Iso8601::<CONFIG>::TIME_PRECISION {
TimePrecision::Hour { decimal_digits } => {
let hours = (hours as f64)
- + (minutes as f64) / Minute.per(Hour) as f64
- + (seconds as f64) / Second.per(Hour) as f64
- + (nanoseconds as f64) / Nanosecond.per(Hour) as f64;
+ + (minutes as f64) / Minute::per(Hour) as f64
+ + (seconds as f64) / Second::per(Hour) as f64
+ + (nanoseconds as f64) / Nanosecond::per(Hour) as f64;
format_float(output, hours, 2, decimal_digits)?;
}
TimePrecision::Minute { decimal_digits } => {
bytes += format_number_pad_zero::<2>(output, hours)?;
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?;
let minutes = (minutes as f64)
- + (seconds as f64) / Second.per(Minute) as f64
- + (nanoseconds as f64) / Nanosecond.per(Minute) as f64;
+ + (seconds as f64) / Second::per(Minute) as f64
+ + (nanoseconds as f64) / Nanosecond::per(Minute) as f64;
bytes += format_float(output, minutes, 2, decimal_digits)?;
}
TimePrecision::Second { decimal_digits } => {
@@ -103,7 +105,7 @@ pub(super) fn format_time<const CONFIG: EncodedConfig>(
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?;
bytes += format_number_pad_zero::<2>(output, minutes)?;
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?;
- let seconds = (seconds as f64) + (nanoseconds as f64) / Nanosecond.per(Second) as f64;
+ let seconds = (seconds as f64) + (nanoseconds as f64) / Nanosecond::per(Second) as f64;
bytes += format_float(output, seconds, 2, decimal_digits)?;
}
}
diff --git a/third_party/rust/time/src/formatting/mod.rs b/third_party/rust/time/src/formatting/mod.rs
index e5017063ab..b57c152221 100644
--- a/third_party/rust/time/src/formatting/mod.rs
+++ b/third_party/rust/time/src/formatting/mod.rs
@@ -2,12 +2,14 @@
pub(crate) mod formattable;
mod iso8601;
-
use core::num::NonZeroU8;
use std::io;
+use num_conv::prelude::*;
+
pub use self::formattable::Formattable;
use crate::convert::*;
+use crate::ext::DigitCount;
use crate::format_description::{modifier, Component};
use crate::{error, Date, OffsetDateTime, Time, UtcOffset};
@@ -38,86 +40,6 @@ const WEEKDAY_NAMES: [&[u8]; 7] = [
b"Sunday",
];
-// region: extension trait
-/// A trait that indicates the formatted width of the value can be determined.
-///
-/// Note that this should not be implemented for any signed integers. This forces the caller to
-/// write the sign if desired.
-pub(crate) trait DigitCount {
- /// The number of digits in the stringified value.
- fn num_digits(self) -> u8;
-}
-impl DigitCount for u8 {
- fn num_digits(self) -> u8 {
- // Using a lookup table as with u32 is *not* faster in standalone benchmarks.
- if self < 10 {
- 1
- } else if self < 100 {
- 2
- } else {
- 3
- }
- }
-}
-impl DigitCount for u16 {
- fn num_digits(self) -> u8 {
- // Using a lookup table as with u32 is *not* faster in standalone benchmarks.
- if self < 10 {
- 1
- } else if self < 100 {
- 2
- } else if self < 1_000 {
- 3
- } else if self < 10_000 {
- 4
- } else {
- 5
- }
- }
-}
-
-impl DigitCount for u32 {
- fn num_digits(self) -> u8 {
- /// Lookup table
- const TABLE: &[u64] = &[
- 0x0001_0000_0000,
- 0x0001_0000_0000,
- 0x0001_0000_0000,
- 0x0001_FFFF_FFF6,
- 0x0002_0000_0000,
- 0x0002_0000_0000,
- 0x0002_FFFF_FF9C,
- 0x0003_0000_0000,
- 0x0003_0000_0000,
- 0x0003_FFFF_FC18,
- 0x0004_0000_0000,
- 0x0004_0000_0000,
- 0x0004_0000_0000,
- 0x0004_FFFF_D8F0,
- 0x0005_0000_0000,
- 0x0005_0000_0000,
- 0x0005_FFFE_7960,
- 0x0006_0000_0000,
- 0x0006_0000_0000,
- 0x0006_FFF0_BDC0,
- 0x0007_0000_0000,
- 0x0007_0000_0000,
- 0x0007_0000_0000,
- 0x0007_FF67_6980,
- 0x0008_0000_0000,
- 0x0008_0000_0000,
- 0x0008_FA0A_1F00,
- 0x0009_0000_0000,
- 0x0009_0000_0000,
- 0x0009_C465_3600,
- 0x000A_0000_0000,
- 0x000A_0000_0000,
- ];
- ((self as u64 + TABLE[31_u32.saturating_sub(self.leading_zeros()) as usize]) >> 32) as _
- }
-}
-// endregion extension trait
-
/// Write all bytes to the output, returning the number of bytes written.
pub(crate) fn write(output: &mut impl io::Write, bytes: &[u8]) -> io::Result<usize> {
output.write_all(bytes)?;
@@ -151,14 +73,14 @@ pub(crate) fn format_float(
) -> io::Result<usize> {
match digits_after_decimal {
Some(digits_after_decimal) => {
- let digits_after_decimal = digits_after_decimal.get() as usize;
- let width = digits_before_decimal as usize + 1 + digits_after_decimal;
+ let digits_after_decimal = digits_after_decimal.get().extend();
+ let width = digits_before_decimal.extend::<usize>() + 1 + digits_after_decimal;
write!(output, "{value:0>width$.digits_after_decimal$}")?;
Ok(width)
}
None => {
let value = value.trunc() as u64;
- let width = digits_before_decimal as usize;
+ let width = digits_before_decimal.extend();
write!(output, "{value:0>width$}")?;
Ok(width)
}
@@ -250,7 +172,19 @@ pub(crate) fn format_component(
(UnixTimestamp(modifier), Some(date), Some(time), Some(offset)) => {
fmt_unix_timestamp(output, date, time, offset, modifier)?
}
- _ => return Err(error::Format::InsufficientTypeInformation),
+ (End(modifier::End {}), ..) => 0,
+
+ // This is functionally the same as a wildcard arm, but it will cause an error if a new
+ // component is added. This is to avoid a bug where a new component, the code compiles, and
+ // formatting fails.
+ // Allow unreachable patterns because some branches may be fully matched above.
+ #[allow(unreachable_patterns)]
+ (
+ Day(_) | Month(_) | Ordinal(_) | Weekday(_) | WeekNumber(_) | Year(_) | Hour(_)
+ | Minute(_) | Period(_) | Second(_) | Subsecond(_) | OffsetHour(_) | OffsetMinute(_)
+ | OffsetSecond(_) | Ignore(_) | UnixTimestamp(_) | End(_),
+ ..,
+ ) => return Err(error::Format::InsufficientTypeInformation),
})
}
@@ -275,9 +209,17 @@ fn fmt_month(
}: modifier::Month,
) -> Result<usize, io::Error> {
match repr {
- modifier::MonthRepr::Numerical => format_number::<2>(output, date.month() as u8, padding),
- modifier::MonthRepr::Long => write(output, MONTH_NAMES[date.month() as usize - 1]),
- modifier::MonthRepr::Short => write(output, &MONTH_NAMES[date.month() as usize - 1][..3]),
+ modifier::MonthRepr::Numerical => {
+ format_number::<2>(output, u8::from(date.month()), padding)
+ }
+ modifier::MonthRepr::Long => write(
+ output,
+ MONTH_NAMES[u8::from(date.month()).extend::<usize>() - 1],
+ ),
+ modifier::MonthRepr::Short => write(
+ output,
+ &MONTH_NAMES[u8::from(date.month()).extend::<usize>() - 1][..3],
+ ),
}
}
@@ -303,20 +245,20 @@ fn fmt_weekday(
match repr {
modifier::WeekdayRepr::Short => write(
output,
- &WEEKDAY_NAMES[date.weekday().number_days_from_monday() as usize][..3],
+ &WEEKDAY_NAMES[date.weekday().number_days_from_monday().extend::<usize>()][..3],
),
modifier::WeekdayRepr::Long => write(
output,
- WEEKDAY_NAMES[date.weekday().number_days_from_monday() as usize],
+ WEEKDAY_NAMES[date.weekday().number_days_from_monday().extend::<usize>()],
),
modifier::WeekdayRepr::Sunday => format_number::<1>(
output,
- date.weekday().number_days_from_sunday() + one_indexed as u8,
+ date.weekday().number_days_from_sunday() + u8::from(one_indexed),
modifier::Padding::None,
),
modifier::WeekdayRepr::Monday => format_number::<1>(
output,
- date.weekday().number_days_from_monday() + one_indexed as u8,
+ date.weekday().number_days_from_monday() + u8::from(one_indexed),
modifier::Padding::None,
),
}
@@ -515,10 +457,7 @@ fn fmt_unix_timestamp(
sign_is_mandatory,
}: modifier::UnixTimestamp,
) -> Result<usize, io::Error> {
- let date_time = date
- .with_time(time)
- .assume_offset(offset)
- .to_offset(UtcOffset::UTC);
+ let date_time = OffsetDateTime::new_in_offset(date, time, offset).to_offset(UtcOffset::UTC);
if date_time < OffsetDateTime::UNIX_EPOCH {
write(output, b"-")?;
@@ -532,11 +471,15 @@ fn fmt_unix_timestamp(
}
modifier::UnixTimestampPrecision::Millisecond => format_number_pad_none(
output,
- (date_time.unix_timestamp_nanos() / Nanosecond.per(Millisecond) as i128).unsigned_abs(),
+ (date_time.unix_timestamp_nanos()
+ / Nanosecond::per(Millisecond).cast_signed().extend::<i128>())
+ .unsigned_abs(),
),
modifier::UnixTimestampPrecision::Microsecond => format_number_pad_none(
output,
- (date_time.unix_timestamp_nanos() / Nanosecond.per(Microsecond) as i128).unsigned_abs(),
+ (date_time.unix_timestamp_nanos()
+ / Nanosecond::per(Microsecond).cast_signed().extend::<i128>())
+ .unsigned_abs(),
),
modifier::UnixTimestampPrecision::Nanosecond => {
format_number_pad_none(output, date_time.unix_timestamp_nanos().unsigned_abs())
diff --git a/third_party/rust/time/src/instant.rs b/third_party/rust/time/src/instant.rs
index 58579b101c..4c2a16f6ef 100644
--- a/third_party/rust/time/src/instant.rs
+++ b/third_party/rust/time/src/instant.rs
@@ -1,11 +1,14 @@
//! The [`Instant`] struct and its associated `impl`s.
+#![allow(deprecated)]
+
use core::borrow::Borrow;
use core::cmp::{Ord, Ordering, PartialEq, PartialOrd};
use core::ops::{Add, Sub};
use core::time::Duration as StdDuration;
use std::time::Instant as StdInstant;
+use crate::internal_macros::{impl_add_assign, impl_sub_assign};
use crate::Duration;
/// A measurement of a monotonically non-decreasing clock. Opaque and useful only with [`Duration`].
@@ -25,6 +28,7 @@ use crate::Duration;
///
/// This implementation allows for operations with signed [`Duration`]s, but is otherwise identical
/// to [`std::time::Instant`].
+#[deprecated(since = "0.3.35", note = "import `time::ext::InstantExt` instead")]
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Instant(pub StdInstant);
@@ -34,6 +38,7 @@ impl Instant {
/// Returns an `Instant` corresponding to "now".
///
/// ```rust
+ /// # #![allow(deprecated)]
/// # use time::Instant;
/// println!("{:?}", Instant::now());
/// ```
@@ -45,6 +50,7 @@ impl Instant {
/// be nonnegative if the instant is not synthetically created.
///
/// ```rust
+ /// # #![allow(deprecated)]
/// # use time::{Instant, ext::{NumericalStdDuration, NumericalDuration}};
/// # use std::thread;
/// let instant = Instant::now();
@@ -62,6 +68,7 @@ impl Instant {
/// otherwise.
///
/// ```rust
+ /// # #![allow(deprecated)]
/// # use time::{Instant, ext::NumericalDuration};
/// let now = Instant::now();
/// assert_eq!(now.checked_add(5.seconds()), Some(now + 5.seconds()));
@@ -83,6 +90,7 @@ impl Instant {
/// otherwise.
///
/// ```rust
+ /// # #![allow(deprecated)]
/// # use time::{Instant, ext::NumericalDuration};
/// let now = Instant::now();
/// assert_eq!(now.checked_sub(5.seconds()), Some(now - 5.seconds()));
@@ -103,6 +111,7 @@ impl Instant {
/// Obtain the inner [`std::time::Instant`].
///
/// ```rust
+ /// # #![allow(deprecated)]
/// # use time::Instant;
/// let now = Instant::now();
/// assert_eq!(now.into_inner(), now.0);
@@ -128,6 +137,9 @@ impl From<Instant> for StdInstant {
impl Sub for Instant {
type Output = Duration;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, other: Self) -> Self::Output {
match self.0.cmp(&other.0) {
Ordering::Equal => Duration::ZERO,
diff --git a/third_party/rust/time/src/lib.rs b/third_party/rust/time/src/lib.rs
index 2ab59cf8ef..511df04deb 100644
--- a/third_party/rust/time/src/lib.rs
+++ b/third_party/rust/time/src/lib.rs
@@ -52,14 +52,6 @@
//! Libraries should never enable this feature, as the decision of what format to use should be up
//! to the user.
//!
-//! - `serde-well-known` (_implicitly enables `serde-human-readable`_)
-//!
-//! _This feature flag is deprecated and will be removed in a future breaking release. Use the
-//! `serde-human-readable` feature instead._
-//!
-//! Enables support for serializing and deserializing well-known formats using serde's
-//! [`#[with]` attribute](https://serde.rs/field-attrs.html#with).
-//!
//! - `rand`
//!
//! Enables [rand](https://docs.rs/rand) support for all types.
@@ -76,55 +68,7 @@
#![doc(html_playground_url = "https://play.rust-lang.org")]
#![cfg_attr(__time_03_docs, feature(doc_auto_cfg, doc_notable_trait))]
-#![cfg_attr(coverage_nightly, feature(no_coverage))]
-#![cfg_attr(not(feature = "std"), no_std)]
-#![deny(
- anonymous_parameters,
- clippy::all,
- clippy::alloc_instead_of_core,
- clippy::explicit_auto_deref,
- clippy::obfuscated_if_else,
- clippy::std_instead_of_core,
- clippy::undocumented_unsafe_blocks,
- illegal_floating_point_literal_pattern,
- late_bound_lifetime_arguments,
- path_statements,
- patterns_in_fns_without_body,
- rust_2018_idioms,
- trivial_casts,
- trivial_numeric_casts,
- unreachable_pub,
- unsafe_op_in_unsafe_fn,
- unused_extern_crates,
- rustdoc::broken_intra_doc_links,
- rustdoc::private_intra_doc_links
-)]
-#![warn(
- clippy::dbg_macro,
- clippy::decimal_literal_representation,
- clippy::get_unwrap,
- clippy::missing_docs_in_private_items,
- clippy::nursery,
- clippy::print_stdout,
- clippy::todo,
- clippy::unimplemented,
- clippy::uninlined_format_args,
- clippy::unnested_or_patterns,
- clippy::unwrap_in_result,
- clippy::unwrap_used,
- clippy::use_debug,
- deprecated_in_future,
- missing_copy_implementations,
- missing_debug_implementations,
- unused_qualifications,
- variant_size_differences
-)]
-#![allow(
- clippy::redundant_pub_crate, // suggests bad style
- clippy::option_if_let_else, // suggests terrible code
- clippy::unused_peekable, // temporary due to bug: remove when Rust 1.66 is released
- clippy::std_instead_of_core, // temporary due to bug: remove when Rust 1.66 is released
-)]
+#![no_std]
#![doc(html_favicon_url = "https://avatars0.githubusercontent.com/u/55999857")]
#![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/55999857")]
#![doc(test(attr(deny(warnings))))]
@@ -133,187 +77,10 @@
#[cfg(feature = "alloc")]
extern crate alloc;
-// TODO(jhpratt) remove this after a while
-#[cfg(unsound_local_offset)]
-compile_error!(
- "The `unsound_local_offset` flag was removed in time 0.3.18. If you need this functionality, \
- see the `time::util::local_offset::set_soundness` function."
-);
-
-// region: macros
-/// Helper macro for easily implementing `OpAssign`.
-macro_rules! __impl_assign {
- ($sym:tt $op:ident $fn:ident $target:ty : $($(#[$attr:meta])* $t:ty),+) => {$(
- #[allow(unused_qualifications)]
- $(#[$attr])*
- impl core::ops::$op<$t> for $target {
- fn $fn(&mut self, rhs: $t) {
- *self = *self $sym rhs;
- }
- }
- )+};
-}
-
-/// Implement `AddAssign` for the provided types.
-macro_rules! impl_add_assign {
- ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
- __impl_assign!(+ AddAssign add_assign $target : $($(#[$attr])* $t),+);
- };
-}
-
-/// Implement `SubAssign` for the provided types.
-macro_rules! impl_sub_assign {
- ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
- __impl_assign!(- SubAssign sub_assign $target : $($(#[$attr])* $t),+);
- };
-}
-
-/// Implement `MulAssign` for the provided types.
-macro_rules! impl_mul_assign {
- ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
- __impl_assign!(* MulAssign mul_assign $target : $($(#[$attr])* $t),+);
- };
-}
-
-/// Implement `DivAssign` for the provided types.
-macro_rules! impl_div_assign {
- ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
- __impl_assign!(/ DivAssign div_assign $target : $($(#[$attr])* $t),+);
- };
-}
-
-/// Division of integers, rounding the resulting value towards negative infinity.
-macro_rules! div_floor {
- ($a:expr, $b:expr) => {{
- let _a = $a;
- let _b = $b;
-
- let (_quotient, _remainder) = (_a / _b, _a % _b);
-
- if (_remainder > 0 && _b < 0) || (_remainder < 0 && _b > 0) {
- _quotient - 1
- } else {
- _quotient
- }
- }};
-}
-
-/// Cascade an out-of-bounds value.
-macro_rules! cascade {
- (@ordinal ordinal) => {};
- (@year year) => {};
-
- // Cascade an out-of-bounds value from "from" to "to".
- ($from:ident in $min:literal.. $max:expr => $to:tt) => {
- #[allow(unused_comparisons, unused_assignments)]
- let min = $min;
- let max = $max;
- if $from >= max {
- $from -= max - min;
- $to += 1;
- } else if $from < min {
- $from += max - min;
- $to -= 1;
- }
- };
-
- // Special case the ordinal-to-year cascade, as it has different behavior.
- ($ordinal:ident => $year:ident) => {
- // We need to actually capture the idents. Without this, macro hygiene causes errors.
- cascade!(@ordinal $ordinal);
- cascade!(@year $year);
- #[allow(unused_assignments)]
- if $ordinal > crate::util::days_in_year($year) as i16 {
- $ordinal -= crate::util::days_in_year($year) as i16;
- $year += 1;
- } else if $ordinal < 1 {
- $year -= 1;
- $ordinal += crate::util::days_in_year($year) as i16;
- }
- };
-}
-
-/// Returns `Err(error::ComponentRange)` if the value is not in range.
-macro_rules! ensure_value_in_range {
- ($value:ident in $start:expr => $end:expr) => {{
- let _start = $start;
- let _end = $end;
- #[allow(trivial_numeric_casts, unused_comparisons)]
- if $value < _start || $value > _end {
- return Err(crate::error::ComponentRange {
- name: stringify!($value),
- minimum: _start as _,
- maximum: _end as _,
- value: $value as _,
- conditional_range: false,
- });
- }
- }};
-
- ($value:ident conditionally in $start:expr => $end:expr) => {{
- let _start = $start;
- let _end = $end;
- #[allow(trivial_numeric_casts, unused_comparisons)]
- if $value < _start || $value > _end {
- return Err(crate::error::ComponentRange {
- name: stringify!($value),
- minimum: _start as _,
- maximum: _end as _,
- value: $value as _,
- conditional_range: true,
- });
- }
- }};
-}
-
-/// Try to unwrap an expression, returning if not possible.
-///
-/// This is similar to the `?` operator, but does not perform `.into()`. Because of this, it is
-/// usable in `const` contexts.
-macro_rules! const_try {
- ($e:expr) => {
- match $e {
- Ok(value) => value,
- Err(error) => return Err(error),
- }
- };
-}
-
-/// Try to unwrap an expression, returning if not possible.
-///
-/// This is similar to the `?` operator, but is usable in `const` contexts.
-macro_rules! const_try_opt {
- ($e:expr) => {
- match $e {
- Some(value) => value,
- None => return None,
- }
- };
-}
-
-/// Try to unwrap an expression, panicking if not possible.
-///
-/// This is similar to `$e.expect($message)`, but is usable in `const` contexts.
-macro_rules! expect_opt {
- ($e:expr, $message:literal) => {
- match $e {
- Some(value) => value,
- None => crate::expect_failed($message),
- }
- };
-}
-
-/// `unreachable!()`, but better.
-macro_rules! bug {
- () => { compile_error!("provide an error message to help fix a possible bug") };
- ($descr:literal $($rest:tt)?) => {
- panic!(concat!("internal error: ", $descr) $($rest)?)
- }
-}
-// endregion macros
+#[cfg(feature = "std")]
+extern crate std;
mod date;
-mod date_time;
mod duration;
pub mod error;
pub mod ext;
@@ -323,6 +90,7 @@ pub mod format_description;
pub mod formatting;
#[cfg(feature = "std")]
mod instant;
+mod internal_macros;
#[cfg(feature = "macros")]
pub mod macros;
mod month;
@@ -345,14 +113,13 @@ mod utc_offset;
pub mod util;
mod weekday;
-// Not public yet.
-use time_core::convert;
+pub use time_core::convert;
pub use crate::date::Date;
-use crate::date_time::DateTime;
pub use crate::duration::Duration;
pub use crate::error::Error;
#[cfg(feature = "std")]
+#[allow(deprecated)]
pub use crate::instant::Instant;
pub use crate::month::Month;
pub use crate::offset_date_time::OffsetDateTime;
diff --git a/third_party/rust/time/src/macros.rs b/third_party/rust/time/src/macros.rs
index 4f295e2ed8..d788645683 100644
--- a/third_party/rust/time/src/macros.rs
+++ b/third_party/rust/time/src/macros.rs
@@ -51,8 +51,8 @@ pub use time_macros::date;
pub use time_macros::datetime;
/// Equivalent of performing [`format_description::parse()`] at compile time.
///
-/// Using the macro instead of the function results in a static slice rather than a [`Vec`],
-/// such that it can be used in `#![no_alloc]` situations.
+/// Using the macro instead of the function results in a static slice rather than a
+/// [`Vec`](alloc::vec::Vec), such that it can be used in `#![no_alloc]` situations.
///
/// The resulting expression can be used in `const` or `static` declarations, and implements
/// the sealed traits required for both formatting and parsing.
diff --git a/third_party/rust/time/src/month.rs b/third_party/rust/time/src/month.rs
index 8d8c164b21..55e53b4b8a 100644
--- a/third_party/rust/time/src/month.rs
+++ b/third_party/rust/time/src/month.rs
@@ -4,25 +4,38 @@ use core::fmt;
use core::num::NonZeroU8;
use core::str::FromStr;
+use powerfmt::smart_display::{FormatterOptions, Metadata, SmartDisplay};
+
use self::Month::*;
use crate::error;
/// Months of the year.
-#[allow(clippy::missing_docs_in_private_items)] // variants
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Month {
+ #[allow(missing_docs)]
January = 1,
+ #[allow(missing_docs)]
February = 2,
+ #[allow(missing_docs)]
March = 3,
+ #[allow(missing_docs)]
April = 4,
+ #[allow(missing_docs)]
May = 5,
+ #[allow(missing_docs)]
June = 6,
+ #[allow(missing_docs)]
July = 7,
+ #[allow(missing_docs)]
August = 8,
+ #[allow(missing_docs)]
September = 9,
+ #[allow(missing_docs)]
October = 10,
+ #[allow(missing_docs)]
November = 11,
+ #[allow(missing_docs)]
December = 12,
}
@@ -153,9 +166,35 @@ impl Month {
}
}
-impl fmt::Display for Month {
+mod private {
+ #[non_exhaustive]
+ #[derive(Debug, Clone, Copy)]
+ pub struct MonthMetadata;
+}
+use private::MonthMetadata;
+
+impl SmartDisplay for Month {
+ type Metadata = MonthMetadata;
+
+ fn metadata(&self, _: FormatterOptions) -> Metadata<Self> {
+ match self {
+ January => Metadata::new(7, self, MonthMetadata),
+ February => Metadata::new(8, self, MonthMetadata),
+ March => Metadata::new(5, self, MonthMetadata),
+ April => Metadata::new(5, self, MonthMetadata),
+ May => Metadata::new(3, self, MonthMetadata),
+ June => Metadata::new(4, self, MonthMetadata),
+ July => Metadata::new(4, self, MonthMetadata),
+ August => Metadata::new(6, self, MonthMetadata),
+ September => Metadata::new(9, self, MonthMetadata),
+ October => Metadata::new(7, self, MonthMetadata),
+ November => Metadata::new(8, self, MonthMetadata),
+ December => Metadata::new(8, self, MonthMetadata),
+ }
+ }
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str(match self {
+ f.pad(match self {
January => "January",
February => "February",
March => "March",
@@ -172,6 +211,12 @@ impl fmt::Display for Month {
}
}
+impl fmt::Display for Month {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ SmartDisplay::fmt(self, f)
+ }
+}
+
impl FromStr for Month {
type Err = error::InvalidVariant;
diff --git a/third_party/rust/time/src/offset_date_time.rs b/third_party/rust/time/src/offset_date_time.rs
index d979174e1d..52f1799851 100644
--- a/third_party/rust/time/src/offset_date_time.rs
+++ b/third_party/rust/time/src/offset_date_time.rs
@@ -1,9 +1,8 @@
//! The [`OffsetDateTime`] struct and its associated `impl`s.
-#[cfg(feature = "std")]
+#[cfg(feature = "formatting")]
+use alloc::string::String;
use core::cmp::Ordering;
-#[cfg(feature = "std")]
-use core::convert::From;
use core::fmt;
use core::hash::Hash;
use core::ops::{Add, AddAssign, Sub, SubAssign};
@@ -13,21 +12,61 @@ use std::io;
#[cfg(feature = "std")]
use std::time::SystemTime;
-use crate::date_time::offset_kind;
+use deranged::RangedI64;
+use num_conv::prelude::*;
+use powerfmt::ext::FormatterExt as _;
+use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay};
+use time_core::convert::*;
+
+use crate::date::{MAX_YEAR, MIN_YEAR};
#[cfg(feature = "formatting")]
use crate::formatting::Formattable;
+use crate::internal_macros::{
+ cascade, const_try, const_try_opt, div_floor, ensure_ranged, expect_opt,
+};
#[cfg(feature = "parsing")]
use crate::parsing::Parsable;
-use crate::{error, Date, DateTime, Duration, Month, PrimitiveDateTime, Time, UtcOffset, Weekday};
+use crate::{error, util, Date, Duration, Month, PrimitiveDateTime, Time, UtcOffset, Weekday};
-/// The actual type doing all the work.
-type Inner = DateTime<offset_kind::Fixed>;
+/// The Julian day of the Unix epoch.
+// Safety: `ordinal` is not zero.
+#[allow(clippy::undocumented_unsafe_blocks)]
+const UNIX_EPOCH_JULIAN_DAY: i32 =
+ unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }.to_julian_day();
/// A [`PrimitiveDateTime`] with a [`UtcOffset`].
///
/// All comparisons are performed using the UTC time.
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct OffsetDateTime(pub(crate) Inner);
+#[derive(Clone, Copy, Eq)]
+pub struct OffsetDateTime {
+ local_date_time: PrimitiveDateTime,
+ offset: UtcOffset,
+}
+
+impl PartialEq for OffsetDateTime {
+ fn eq(&self, other: &Self) -> bool {
+ self.to_offset_raw(UtcOffset::UTC) == other.to_offset_raw(UtcOffset::UTC)
+ }
+}
+
+impl PartialOrd for OffsetDateTime {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for OffsetDateTime {
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.to_offset_raw(UtcOffset::UTC)
+ .cmp(&other.to_offset_raw(UtcOffset::UTC))
+ }
+}
+
+impl Hash for OffsetDateTime {
+ fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
+ self.to_offset_raw(UtcOffset::UTC).hash(state);
+ }
+}
impl OffsetDateTime {
/// Midnight, 1 January, 1970 (UTC).
@@ -37,7 +76,12 @@ impl OffsetDateTime {
/// # use time_macros::datetime;
/// assert_eq!(OffsetDateTime::UNIX_EPOCH, datetime!(1970-01-01 0:00 UTC),);
/// ```
- pub const UNIX_EPOCH: Self = Self(Inner::UNIX_EPOCH);
+ pub const UNIX_EPOCH: Self = Self::new_in_offset(
+ // Safety: `ordinal` is not zero.
+ unsafe { Date::__from_ordinal_date_unchecked(1970, 1) },
+ Time::MIDNIGHT,
+ UtcOffset::UTC,
+ );
// region: now
/// Create a new `OffsetDateTime` with the current date and time in UTC.
@@ -50,7 +94,21 @@ impl OffsetDateTime {
/// ```
#[cfg(feature = "std")]
pub fn now_utc() -> Self {
- Self(Inner::now_utc())
+ #[cfg(all(
+ target_family = "wasm",
+ not(any(target_os = "emscripten", target_os = "wasi")),
+ feature = "wasm-bindgen"
+ ))]
+ {
+ js_sys::Date::new_0().into()
+ }
+
+ #[cfg(not(all(
+ target_family = "wasm",
+ not(any(target_os = "emscripten", target_os = "wasi")),
+ feature = "wasm-bindgen"
+ )))]
+ SystemTime::now().into()
}
/// Attempt to create a new `OffsetDateTime` with the current date and time in the local offset.
@@ -64,10 +122,47 @@ impl OffsetDateTime {
/// ```
#[cfg(feature = "local-offset")]
pub fn now_local() -> Result<Self, error::IndeterminateOffset> {
- Inner::now_local().map(Self)
+ let t = Self::now_utc();
+ Ok(t.to_offset(UtcOffset::local_offset_at(t)?))
}
// endregion now
+ /// Create a new `OffsetDateTime` with the given [`Date`], [`Time`], and [`UtcOffset`].
+ ///
+ /// ```
+ /// # use time::{Date, Month, OffsetDateTime, Time, UtcOffset};
+ /// # use time_macros::datetime;
+ /// let dt = OffsetDateTime::new_in_offset(
+ /// Date::from_calendar_date(2024, Month::January, 1)?,
+ /// Time::from_hms_nano(12, 59, 59, 500_000_000)?,
+ /// UtcOffset::from_hms(-5, 0, 0)?,
+ /// );
+ /// assert_eq!(dt, datetime!(2024-01-01 12:59:59.5 -5));
+ /// # Ok::<_, time::error::Error>(())
+ /// ```
+ pub const fn new_in_offset(date: Date, time: Time, offset: UtcOffset) -> Self {
+ Self {
+ local_date_time: date.with_time(time),
+ offset,
+ }
+ }
+
+ /// Create a new `OffsetDateTime` with the given [`Date`] and [`Time`] in the UTC timezone.
+ ///
+ /// ```
+ /// # use time::{Date, Month, OffsetDateTime, Time};
+ /// # use time_macros::datetime;
+ /// let dt = OffsetDateTime::new_utc(
+ /// Date::from_calendar_date(2024, Month::January, 1)?,
+ /// Time::from_hms_nano(12, 59, 59, 500_000_000)?,
+ /// );
+ /// assert_eq!(dt, datetime!(2024-01-01 12:59:59.5 UTC));
+ /// # Ok::<_, time::error::Error>(())
+ /// ```
+ pub const fn new_utc(date: Date, time: Time) -> Self {
+ PrimitiveDateTime::new(date, time).assume_utc()
+ }
+
/// Convert the `OffsetDateTime` from the current [`UtcOffset`] to the provided [`UtcOffset`].
///
/// ```rust
@@ -94,7 +189,10 @@ impl OffsetDateTime {
///
/// This method panics if the local date-time in the new offset is outside the supported range.
pub const fn to_offset(self, offset: UtcOffset) -> Self {
- Self(self.0.to_offset(offset))
+ expect_opt!(
+ self.checked_to_offset(offset),
+ "local datetime out of valid range"
+ )
}
/// Convert the `OffsetDateTime` from the current [`UtcOffset`] to the provided [`UtcOffset`],
@@ -118,7 +216,74 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn checked_to_offset(self, offset: UtcOffset) -> Option<Self> {
- Some(Self(const_try_opt!(self.0.checked_to_offset(offset))))
+ if self.offset.whole_hours() == offset.whole_hours()
+ && self.offset.minutes_past_hour() == offset.minutes_past_hour()
+ && self.offset.seconds_past_minute() == offset.seconds_past_minute()
+ {
+ return Some(self.replace_offset(offset));
+ }
+
+ let (year, ordinal, time) = self.to_offset_raw(offset);
+
+ if year > MAX_YEAR || year < MIN_YEAR {
+ return None;
+ }
+
+ Some(Self::new_in_offset(
+ // Safety: `ordinal` is not zero.
+ unsafe { Date::__from_ordinal_date_unchecked(year, ordinal) },
+ time,
+ offset,
+ ))
+ }
+
+ /// Equivalent to `.to_offset(UtcOffset::UTC)`, but returning the year, ordinal, and time. This
+ /// avoids constructing an invalid [`Date`] if the new value is out of range.
+ pub(crate) const fn to_offset_raw(self, offset: UtcOffset) -> (i32, u16, Time) {
+ let from = self.offset;
+ let to = offset;
+
+ // Fast path for when no conversion is necessary.
+ if from.whole_hours() == to.whole_hours()
+ && from.minutes_past_hour() == to.minutes_past_hour()
+ && from.seconds_past_minute() == to.seconds_past_minute()
+ {
+ return (self.year(), self.ordinal(), self.time());
+ }
+
+ let mut second = self.second() as i16 - from.seconds_past_minute() as i16
+ + to.seconds_past_minute() as i16;
+ let mut minute =
+ self.minute() as i16 - from.minutes_past_hour() as i16 + to.minutes_past_hour() as i16;
+ let mut hour = self.hour() as i8 - from.whole_hours() + to.whole_hours();
+ let (mut year, ordinal) = self.to_ordinal_date();
+ let mut ordinal = ordinal as i16;
+
+ // Cascade the values twice. This is needed because the values are adjusted twice above.
+ cascade!(second in 0..Second::per(Minute) as i16 => minute);
+ cascade!(second in 0..Second::per(Minute) as i16 => minute);
+ cascade!(minute in 0..Minute::per(Hour) as i16 => hour);
+ cascade!(minute in 0..Minute::per(Hour) as i16 => hour);
+ cascade!(hour in 0..Hour::per(Day) as i8 => ordinal);
+ cascade!(hour in 0..Hour::per(Day) as i8 => ordinal);
+ cascade!(ordinal => year);
+
+ debug_assert!(ordinal > 0);
+ debug_assert!(ordinal <= util::days_in_year(year) as i16);
+
+ (
+ year,
+ ordinal as _,
+ // Safety: The cascades above ensure the values are in range.
+ unsafe {
+ Time::__from_hms_nanos_unchecked(
+ hour as _,
+ minute as _,
+ second as _,
+ self.nanosecond(),
+ )
+ },
+ )
}
// region: constructors
@@ -151,7 +316,34 @@ impl OffsetDateTime {
/// # Ok::<_, time::Error>(())
/// ```
pub const fn from_unix_timestamp(timestamp: i64) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(Inner::from_unix_timestamp(timestamp))))
+ type Timestamp = RangedI64<
+ {
+ OffsetDateTime::new_in_offset(Date::MIN, Time::MIDNIGHT, UtcOffset::UTC)
+ .unix_timestamp()
+ },
+ {
+ OffsetDateTime::new_in_offset(Date::MAX, Time::MAX, UtcOffset::UTC).unix_timestamp()
+ },
+ >;
+ ensure_ranged!(Timestamp: timestamp);
+
+ // Use the unchecked method here, as the input validity has already been verified.
+ let date = Date::from_julian_day_unchecked(
+ UNIX_EPOCH_JULIAN_DAY + div_floor!(timestamp, Second::per(Day) as i64) as i32,
+ );
+
+ let seconds_within_day = timestamp.rem_euclid(Second::per(Day) as _);
+ // Safety: All values are in range.
+ let time = unsafe {
+ Time::__from_hms_nanos_unchecked(
+ (seconds_within_day / Second::per(Hour) as i64) as _,
+ ((seconds_within_day % Second::per(Hour) as i64) / Minute::per(Hour) as i64) as _,
+ (seconds_within_day % Second::per(Minute) as i64) as _,
+ 0,
+ )
+ };
+
+ Ok(Self::new_in_offset(date, time, UtcOffset::UTC))
}
/// Construct an `OffsetDateTime` from the provided Unix timestamp (in nanoseconds). Calling
@@ -170,9 +362,24 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn from_unix_timestamp_nanos(timestamp: i128) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(Inner::from_unix_timestamp_nanos(
- timestamp
- ))))
+ let datetime = const_try!(Self::from_unix_timestamp(div_floor!(
+ timestamp,
+ Nanosecond::per(Second) as i128
+ ) as i64));
+
+ Ok(Self::new_in_offset(
+ datetime.date(),
+ // Safety: `nanosecond` is in range due to `rem_euclid`.
+ unsafe {
+ Time::__from_hms_nanos_unchecked(
+ datetime.hour(),
+ datetime.minute(),
+ datetime.second(),
+ timestamp.rem_euclid(Nanosecond::per(Second) as _) as u32,
+ )
+ },
+ UtcOffset::UTC,
+ ))
}
// endregion constructors
@@ -185,7 +392,7 @@ impl OffsetDateTime {
/// assert_eq!(datetime!(2019-01-01 0:00 +1).offset(), offset!(+1));
/// ```
pub const fn offset(self) -> UtcOffset {
- self.0.offset()
+ self.offset
}
/// Get the [Unix timestamp](https://en.wikipedia.org/wiki/Unix_time).
@@ -196,7 +403,13 @@ impl OffsetDateTime {
/// assert_eq!(datetime!(1970-01-01 0:00 -1).unix_timestamp(), 3_600);
/// ```
pub const fn unix_timestamp(self) -> i64 {
- self.0.unix_timestamp()
+ let days =
+ (self.to_julian_day() as i64 - UNIX_EPOCH_JULIAN_DAY as i64) * Second::per(Day) as i64;
+ let hours = self.hour() as i64 * Second::per(Hour) as i64;
+ let minutes = self.minute() as i64 * Second::per(Minute) as i64;
+ let seconds = self.second() as i64;
+ let offset_seconds = self.offset.whole_seconds() as i64;
+ days + hours + minutes + seconds - offset_seconds
}
/// Get the Unix timestamp in nanoseconds.
@@ -210,7 +423,12 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn unix_timestamp_nanos(self) -> i128 {
- self.0.unix_timestamp_nanos()
+ self.unix_timestamp() as i128 * Nanosecond::per(Second) as i128 + self.nanosecond() as i128
+ }
+
+ /// Get the [`PrimitiveDateTime`] in the stored offset.
+ const fn date_time(self) -> PrimitiveDateTime {
+ self.local_date_time
}
/// Get the [`Date`] in the stored offset.
@@ -226,7 +444,7 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn date(self) -> Date {
- self.0.date()
+ self.date_time().date()
}
/// Get the [`Time`] in the stored offset.
@@ -242,7 +460,7 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn time(self) -> Time {
- self.0.time()
+ self.date_time().time()
}
// region: date getters
@@ -260,7 +478,7 @@ impl OffsetDateTime {
/// assert_eq!(datetime!(2020-01-01 0:00 UTC).year(), 2020);
/// ```
pub const fn year(self) -> i32 {
- self.0.year()
+ self.date().year()
}
/// Get the month of the date in the stored offset.
@@ -277,7 +495,7 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn month(self) -> Month {
- self.0.month()
+ self.date().month()
}
/// Get the day of the date in the stored offset.
@@ -295,7 +513,7 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn day(self) -> u8 {
- self.0.day()
+ self.date().day()
}
/// Get the day of the year of the date in the stored offset.
@@ -313,7 +531,7 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn ordinal(self) -> u16 {
- self.0.ordinal()
+ self.date().ordinal()
}
/// Get the ISO week number of the date in the stored offset.
@@ -328,7 +546,7 @@ impl OffsetDateTime {
/// assert_eq!(datetime!(2021-01-01 0:00 UTC).iso_week(), 53);
/// ```
pub const fn iso_week(self) -> u8 {
- self.0.iso_week()
+ self.date().iso_week()
}
/// Get the week number where week 1 begins on the first Sunday.
@@ -343,7 +561,7 @@ impl OffsetDateTime {
/// assert_eq!(datetime!(2021-01-01 0:00 UTC).sunday_based_week(), 0);
/// ```
pub const fn sunday_based_week(self) -> u8 {
- self.0.sunday_based_week()
+ self.date().sunday_based_week()
}
/// Get the week number where week 1 begins on the first Monday.
@@ -358,7 +576,7 @@ impl OffsetDateTime {
/// assert_eq!(datetime!(2021-01-01 0:00 UTC).monday_based_week(), 0);
/// ```
pub const fn monday_based_week(self) -> u8 {
- self.0.monday_based_week()
+ self.date().monday_based_week()
}
/// Get the year, month, and day.
@@ -372,7 +590,7 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn to_calendar_date(self) -> (i32, Month, u8) {
- self.0.to_calendar_date()
+ self.date().to_calendar_date()
}
/// Get the year and ordinal day number.
@@ -385,7 +603,7 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn to_ordinal_date(self) -> (i32, u16) {
- self.0.to_ordinal_date()
+ self.date().to_ordinal_date()
}
/// Get the ISO 8601 year, week number, and weekday.
@@ -415,7 +633,7 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) {
- self.0.to_iso_week_date()
+ self.date().to_iso_week_date()
}
/// Get the weekday of the date in the stored offset.
@@ -428,7 +646,7 @@ impl OffsetDateTime {
/// assert_eq!(datetime!(2019-03-01 0:00 UTC).weekday(), Friday);
/// ```
pub const fn weekday(self) -> Weekday {
- self.0.weekday()
+ self.date().weekday()
}
/// Get the Julian day for the date. The time is not taken into account for this calculation.
@@ -444,7 +662,7 @@ impl OffsetDateTime {
/// assert_eq!(datetime!(2019-12-31 0:00 UTC).to_julian_day(), 2_458_849);
/// ```
pub const fn to_julian_day(self) -> i32 {
- self.0.to_julian_day()
+ self.date().to_julian_day()
}
// endregion date getters
@@ -457,7 +675,7 @@ impl OffsetDateTime {
/// assert_eq!(datetime!(2020-01-01 23:59:59 UTC).to_hms(), (23, 59, 59));
/// ```
pub const fn to_hms(self) -> (u8, u8, u8) {
- self.0.as_hms()
+ self.time().as_hms()
}
/// Get the clock hour, minute, second, and millisecond.
@@ -474,7 +692,7 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn to_hms_milli(self) -> (u8, u8, u8, u16) {
- self.0.as_hms_milli()
+ self.time().as_hms_milli()
}
/// Get the clock hour, minute, second, and microsecond.
@@ -491,7 +709,7 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn to_hms_micro(self) -> (u8, u8, u8, u32) {
- self.0.as_hms_micro()
+ self.time().as_hms_micro()
}
/// Get the clock hour, minute, second, and nanosecond.
@@ -508,7 +726,7 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn to_hms_nano(self) -> (u8, u8, u8, u32) {
- self.0.as_hms_nano()
+ self.time().as_hms_nano()
}
/// Get the clock hour in the stored offset.
@@ -526,7 +744,7 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn hour(self) -> u8 {
- self.0.hour()
+ self.time().hour()
}
/// Get the minute within the hour in the stored offset.
@@ -544,7 +762,7 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn minute(self) -> u8 {
- self.0.minute()
+ self.time().minute()
}
/// Get the second within the minute in the stored offset.
@@ -562,7 +780,7 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn second(self) -> u8 {
- self.0.second()
+ self.time().second()
}
// Because a `UtcOffset` is limited in resolution to one second, any subsecond value will not
@@ -578,7 +796,7 @@ impl OffsetDateTime {
/// assert_eq!(datetime!(2019-01-01 23:59:59.999 UTC).millisecond(), 999);
/// ```
pub const fn millisecond(self) -> u16 {
- self.0.millisecond()
+ self.time().millisecond()
}
/// Get the microseconds within the second in the stored offset.
@@ -594,7 +812,7 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn microsecond(self) -> u32 {
- self.0.microsecond()
+ self.time().microsecond()
}
/// Get the nanoseconds within the second in the stored offset.
@@ -610,7 +828,7 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn nanosecond(self) -> u32 {
- self.0.nanosecond()
+ self.time().nanosecond()
}
// endregion time getters
// endregion getters
@@ -633,7 +851,7 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn checked_add(self, duration: Duration) -> Option<Self> {
- Some(Self(const_try_opt!(self.0.checked_add(duration))))
+ Some(const_try_opt!(self.date_time().checked_add(duration)).assume_offset(self.offset()))
}
/// Computes `self - duration`, returning `None` if an overflow occurred.
@@ -653,7 +871,7 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn checked_sub(self, duration: Duration) -> Option<Self> {
- Some(Self(const_try_opt!(self.0.checked_sub(duration))))
+ Some(const_try_opt!(self.date_time().checked_sub(duration)).assume_offset(self.offset()))
}
// endregion: checked arithmetic
@@ -704,7 +922,13 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn saturating_add(self, duration: Duration) -> Self {
- Self(self.0.saturating_add(duration))
+ if let Some(datetime) = self.checked_add(duration) {
+ datetime
+ } else if duration.is_negative() {
+ PrimitiveDateTime::MIN.assume_offset(self.offset())
+ } else {
+ PrimitiveDateTime::MAX.assume_offset(self.offset())
+ }
}
/// Computes `self - duration`, saturating value on overflow.
@@ -753,7 +977,13 @@ impl OffsetDateTime {
/// );
/// ```
pub const fn saturating_sub(self, duration: Duration) -> Self {
- Self(self.0.saturating_sub(duration))
+ if let Some(datetime) = self.checked_sub(duration) {
+ datetime
+ } else if duration.is_negative() {
+ PrimitiveDateTime::MAX.assume_offset(self.offset())
+ } else {
+ PrimitiveDateTime::MIN.assume_offset(self.offset())
+ }
}
// endregion: saturating arithmetic
}
@@ -781,7 +1011,7 @@ impl OffsetDateTime {
/// ```
#[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_time(self, time: Time) -> Self {
- Self(self.0.replace_time(time))
+ Self::new_in_offset(self.date(), time, self.offset())
}
/// Replace the date, which is assumed to be in the stored offset. The time and offset
@@ -800,7 +1030,7 @@ impl OffsetDateTime {
/// ```
#[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_date(self, date: Date) -> Self {
- Self(self.0.replace_date(date))
+ Self::new_in_offset(date, self.time(), self.offset())
}
/// Replace the date and time, which are assumed to be in the stored offset. The offset
@@ -819,7 +1049,7 @@ impl OffsetDateTime {
/// ```
#[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_date_time(self, date_time: PrimitiveDateTime) -> Self {
- Self(self.0.replace_date_time(date_time.0))
+ date_time.assume_offset(self.offset())
}
/// Replace the offset. The date and time components remain unchanged.
@@ -833,7 +1063,7 @@ impl OffsetDateTime {
/// ```
#[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_offset(self, offset: UtcOffset) -> Self {
- Self(self.0.replace_offset(offset))
+ self.date_time().assume_offset(offset)
}
/// Replace the year. The month and day will be unchanged.
@@ -847,8 +1077,9 @@ impl OffsetDateTime {
/// assert!(datetime!(2022 - 02 - 18 12:00 +01).replace_year(-1_000_000_000).is_err()); // -1_000_000_000 isn't a valid year
/// assert!(datetime!(2022 - 02 - 18 12:00 +01).replace_year(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid year
/// ```
+ #[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_year(year))))
+ Ok(const_try!(self.date_time().replace_year(year)).assume_offset(self.offset()))
}
/// Replace the month of the year.
@@ -862,8 +1093,9 @@ impl OffsetDateTime {
/// );
/// assert!(datetime!(2022 - 01 - 30 12:00 +01).replace_month(Month::February).is_err()); // 30 isn't a valid day in February
/// ```
+ #[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_month(month))))
+ Ok(const_try!(self.date_time().replace_month(month)).assume_offset(self.offset()))
}
/// Replace the day of the month.
@@ -877,8 +1109,22 @@ impl OffsetDateTime {
/// assert!(datetime!(2022 - 02 - 18 12:00 +01).replace_day(0).is_err()); // 00 isn't a valid day
/// assert!(datetime!(2022 - 02 - 18 12:00 +01).replace_day(30).is_err()); // 30 isn't a valid day in February
/// ```
+ #[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_day(day))))
+ Ok(const_try!(self.date_time().replace_day(day)).assume_offset(self.offset()))
+ }
+
+ /// Replace the day of the year.
+ ///
+ /// ```rust
+ /// # use time_macros::datetime;
+ /// assert_eq!(datetime!(2022-049 12:00 +01).replace_ordinal(1), Ok(datetime!(2022-001 12:00 +01)));
+ /// assert!(datetime!(2022-049 12:00 +01).replace_ordinal(0).is_err()); // 0 isn't a valid ordinal
+ /// assert!(datetime!(2022-049 12:00 +01).replace_ordinal(366).is_err()); // 2022 isn't a leap year
+ /// ```
+ #[must_use = "This method does not mutate the original `OffsetDateTime`."]
+ pub const fn replace_ordinal(self, ordinal: u16) -> Result<Self, error::ComponentRange> {
+ Ok(const_try!(self.date_time().replace_ordinal(ordinal)).assume_offset(self.offset()))
}
/// Replace the clock hour.
@@ -891,8 +1137,9 @@ impl OffsetDateTime {
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_hour(24).is_err()); // 24 isn't a valid hour
/// ```
+ #[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_hour(self, hour: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_hour(hour))))
+ Ok(const_try!(self.date_time().replace_hour(hour)).assume_offset(self.offset()))
}
/// Replace the minutes within the hour.
@@ -905,8 +1152,9 @@ impl OffsetDateTime {
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_minute(60).is_err()); // 60 isn't a valid minute
/// ```
+ #[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_minute(self, minute: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_minute(minute))))
+ Ok(const_try!(self.date_time().replace_minute(minute)).assume_offset(self.offset()))
}
/// Replace the seconds within the minute.
@@ -919,8 +1167,9 @@ impl OffsetDateTime {
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_second(60).is_err()); // 60 isn't a valid second
/// ```
+ #[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_second(self, second: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_second(second))))
+ Ok(const_try!(self.date_time().replace_second(second)).assume_offset(self.offset()))
}
/// Replace the milliseconds within the second.
@@ -933,11 +1182,15 @@ impl OffsetDateTime {
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_millisecond(1_000).is_err()); // 1_000 isn't a valid millisecond
/// ```
+ #[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_millisecond(
self,
millisecond: u16,
) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_millisecond(millisecond))))
+ Ok(
+ const_try!(self.date_time().replace_millisecond(millisecond))
+ .assume_offset(self.offset()),
+ )
}
/// Replace the microseconds within the second.
@@ -950,11 +1203,15 @@ impl OffsetDateTime {
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_microsecond(1_000_000).is_err()); // 1_000_000 isn't a valid microsecond
/// ```
+ #[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_microsecond(
self,
microsecond: u32,
) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_microsecond(microsecond))))
+ Ok(
+ const_try!(self.date_time().replace_microsecond(microsecond))
+ .assume_offset(self.offset()),
+ )
}
/// Replace the nanoseconds within the second.
@@ -967,8 +1224,12 @@ impl OffsetDateTime {
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006 +01).replace_nanosecond(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid nanosecond
/// ```
+ #[must_use = "This method does not mutate the original `OffsetDateTime`."]
pub const fn replace_nanosecond(self, nanosecond: u32) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_nanosecond(nanosecond))))
+ Ok(
+ const_try!(self.date_time().replace_nanosecond(nanosecond))
+ .assume_offset(self.offset()),
+ )
}
}
// endregion replacement
@@ -983,7 +1244,12 @@ impl OffsetDateTime {
output: &mut impl io::Write,
format: &(impl Formattable + ?Sized),
) -> Result<usize, error::Format> {
- self.0.format_into(output, format)
+ format.format_into(
+ output,
+ Some(self.date()),
+ Some(self.time()),
+ Some(self.offset()),
+ )
}
/// Format the `OffsetDateTime` using the provided [format
@@ -1003,7 +1269,7 @@ impl OffsetDateTime {
/// # Ok::<_, time::Error>(())
/// ```
pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
- self.0.format(format)
+ format.format(Some(self.date()), Some(self.time()), Some(self.offset()))
}
}
@@ -1029,13 +1295,56 @@ impl OffsetDateTime {
input: &str,
description: &(impl Parsable + ?Sized),
) -> Result<Self, error::Parse> {
- Inner::parse(input, description).map(Self)
+ description.parse_offset_date_time(input.as_bytes())
+ }
+
+ /// A helper method to check if the `OffsetDateTime` is a valid representation of a leap second.
+ /// Leap seconds, when parsed, are represented as the preceding nanosecond. However, leap
+ /// seconds can only occur as the last second of a month UTC.
+ #[cfg(feature = "parsing")]
+ pub(crate) const fn is_valid_leap_second_stand_in(self) -> bool {
+ // This comparison doesn't need to be adjusted for the stored offset, so check it first for
+ // speed.
+ if self.nanosecond() != 999_999_999 {
+ return false;
+ }
+
+ let (year, ordinal, time) = self.to_offset_raw(UtcOffset::UTC);
+ let Ok(date) = Date::from_ordinal_date(year, ordinal) else {
+ return false;
+ };
+
+ time.hour() == 23
+ && time.minute() == 59
+ && time.second() == 59
+ && date.day() == util::days_in_year_month(year, date.month())
+ }
+}
+
+impl SmartDisplay for OffsetDateTime {
+ type Metadata = ();
+
+ fn metadata(&self, _: FormatterOptions) -> Metadata<Self> {
+ let width =
+ smart_display::padded_width_of!(self.date(), " ", self.time(), " ", self.offset());
+ Metadata::new(width, self, ())
+ }
+
+ fn fmt_with_metadata(
+ &self,
+ f: &mut fmt::Formatter<'_>,
+ metadata: Metadata<Self>,
+ ) -> fmt::Result {
+ f.pad_with_width(
+ metadata.unpadded_width(),
+ format_args!("{} {} {}", self.date(), self.time(), self.offset()),
+ )
}
}
impl fmt::Display for OffsetDateTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0.fmt(f)
+ SmartDisplay::fmt(self, f)
}
}
@@ -1050,64 +1359,121 @@ impl fmt::Debug for OffsetDateTime {
impl Add<Duration> for OffsetDateTime {
type Output = Self;
- fn add(self, rhs: Duration) -> Self::Output {
- Self(self.0.add(rhs))
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
+ fn add(self, duration: Duration) -> Self::Output {
+ self.checked_add(duration)
+ .expect("resulting value is out of range")
}
}
impl Add<StdDuration> for OffsetDateTime {
type Output = Self;
- fn add(self, rhs: StdDuration) -> Self::Output {
- Self(self.0.add(rhs))
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
+ fn add(self, duration: StdDuration) -> Self::Output {
+ let (is_next_day, time) = self.time().adjusting_add_std(duration);
+
+ Self::new_in_offset(
+ if is_next_day {
+ (self.date() + duration)
+ .next_day()
+ .expect("resulting value is out of range")
+ } else {
+ self.date() + duration
+ },
+ time,
+ self.offset,
+ )
}
}
impl AddAssign<Duration> for OffsetDateTime {
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn add_assign(&mut self, rhs: Duration) {
- self.0.add_assign(rhs);
+ *self = *self + rhs;
}
}
impl AddAssign<StdDuration> for OffsetDateTime {
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn add_assign(&mut self, rhs: StdDuration) {
- self.0.add_assign(rhs);
+ *self = *self + rhs;
}
}
impl Sub<Duration> for OffsetDateTime {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, rhs: Duration) -> Self::Output {
- Self(self.0.sub(rhs))
+ self.checked_sub(rhs)
+ .expect("resulting value is out of range")
}
}
impl Sub<StdDuration> for OffsetDateTime {
type Output = Self;
- fn sub(self, rhs: StdDuration) -> Self::Output {
- Self(self.0.sub(rhs))
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
+ fn sub(self, duration: StdDuration) -> Self::Output {
+ let (is_previous_day, time) = self.time().adjusting_sub_std(duration);
+
+ Self::new_in_offset(
+ if is_previous_day {
+ (self.date() - duration)
+ .previous_day()
+ .expect("resulting value is out of range")
+ } else {
+ self.date() - duration
+ },
+ time,
+ self.offset,
+ )
}
}
impl SubAssign<Duration> for OffsetDateTime {
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub_assign(&mut self, rhs: Duration) {
- self.0.sub_assign(rhs);
+ *self = *self - rhs;
}
}
impl SubAssign<StdDuration> for OffsetDateTime {
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub_assign(&mut self, rhs: StdDuration) {
- self.0.sub_assign(rhs);
+ *self = *self - rhs;
}
}
impl Sub for OffsetDateTime {
type Output = Duration;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, rhs: Self) -> Self::Output {
- self.0.sub(rhs.0)
+ let base = self.date_time() - rhs.date_time();
+ let adjustment = Duration::seconds(
+ (self.offset.whole_seconds() - rhs.offset.whole_seconds()).extend::<i64>(),
+ );
+ base - adjustment
}
}
@@ -1115,8 +1481,11 @@ impl Sub for OffsetDateTime {
impl Sub<SystemTime> for OffsetDateTime {
type Output = Duration;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, rhs: SystemTime) -> Self::Output {
- self.0.sub(rhs)
+ self - Self::from(rhs)
}
}
@@ -1124,50 +1493,94 @@ impl Sub<SystemTime> for OffsetDateTime {
impl Sub<OffsetDateTime> for SystemTime {
type Output = Duration;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, rhs: OffsetDateTime) -> Self::Output {
- self.sub(rhs.0)
+ OffsetDateTime::from(self) - rhs
+ }
+}
+
+#[cfg(feature = "std")]
+impl Add<Duration> for SystemTime {
+ type Output = Self;
+
+ fn add(self, duration: Duration) -> Self::Output {
+ if duration.is_zero() {
+ self
+ } else if duration.is_positive() {
+ self + duration.unsigned_abs()
+ } else {
+ debug_assert!(duration.is_negative());
+ self - duration.unsigned_abs()
+ }
}
}
+crate::internal_macros::impl_add_assign!(SystemTime: #[cfg(feature = "std")] Duration);
+
+#[cfg(feature = "std")]
+impl Sub<Duration> for SystemTime {
+ type Output = Self;
+
+ fn sub(self, duration: Duration) -> Self::Output {
+ (OffsetDateTime::from(self) - duration).into()
+ }
+}
+
+crate::internal_macros::impl_sub_assign!(SystemTime: #[cfg(feature = "std")] Duration);
+
#[cfg(feature = "std")]
impl PartialEq<SystemTime> for OffsetDateTime {
fn eq(&self, rhs: &SystemTime) -> bool {
- self.0.eq(rhs)
+ self == &Self::from(*rhs)
}
}
#[cfg(feature = "std")]
impl PartialEq<OffsetDateTime> for SystemTime {
fn eq(&self, rhs: &OffsetDateTime) -> bool {
- self.eq(&rhs.0)
+ &OffsetDateTime::from(*self) == rhs
}
}
#[cfg(feature = "std")]
impl PartialOrd<SystemTime> for OffsetDateTime {
fn partial_cmp(&self, other: &SystemTime) -> Option<Ordering> {
- self.0.partial_cmp(other)
+ self.partial_cmp(&Self::from(*other))
}
}
#[cfg(feature = "std")]
impl PartialOrd<OffsetDateTime> for SystemTime {
fn partial_cmp(&self, other: &OffsetDateTime) -> Option<Ordering> {
- self.partial_cmp(&other.0)
+ OffsetDateTime::from(*self).partial_cmp(other)
}
}
#[cfg(feature = "std")]
impl From<SystemTime> for OffsetDateTime {
fn from(system_time: SystemTime) -> Self {
- Self(Inner::from(system_time))
+ match system_time.duration_since(SystemTime::UNIX_EPOCH) {
+ Ok(duration) => Self::UNIX_EPOCH + duration,
+ Err(err) => Self::UNIX_EPOCH - err.duration(),
+ }
}
}
#[cfg(feature = "std")]
impl From<OffsetDateTime> for SystemTime {
fn from(datetime: OffsetDateTime) -> Self {
- datetime.0.into()
+ let duration = datetime - OffsetDateTime::UNIX_EPOCH;
+
+ if duration.is_zero() {
+ Self::UNIX_EPOCH
+ } else if duration.is_positive() {
+ Self::UNIX_EPOCH + duration.unsigned_abs()
+ } else {
+ debug_assert!(duration.is_negative());
+ Self::UNIX_EPOCH - duration.unsigned_abs()
+ }
}
}
@@ -1177,8 +1590,14 @@ impl From<OffsetDateTime> for SystemTime {
feature = "wasm-bindgen"
))]
impl From<js_sys::Date> for OffsetDateTime {
+ /// # Panics
+ ///
+ /// This may panic if the timestamp can not be represented.
fn from(js_date: js_sys::Date) -> Self {
- Self(Inner::from(js_date))
+ // get_time() returns milliseconds
+ let timestamp_nanos = (js_date.get_time() * Nanosecond::per(Millisecond) as f64) as i128;
+ Self::from_unix_timestamp_nanos(timestamp_nanos)
+ .expect("invalid timestamp: Timestamp cannot fit in range")
}
}
@@ -1189,8 +1608,11 @@ impl From<js_sys::Date> for OffsetDateTime {
))]
impl From<OffsetDateTime> for js_sys::Date {
fn from(datetime: OffsetDateTime) -> Self {
- datetime.0.into()
+ // new Date() takes milliseconds
+ let timestamp = (datetime.unix_timestamp_nanos()
+ / Nanosecond::per(Millisecond).cast_signed().extend::<i128>())
+ as f64;
+ Self::new(&timestamp.into())
}
}
-
// endregion trait impls
diff --git a/third_party/rust/time/src/parsing/combinator/mod.rs b/third_party/rust/time/src/parsing/combinator/mod.rs
index 3b4bc7a81b..e3e1df76cb 100644
--- a/third_party/rust/time/src/parsing/combinator/mod.rs
+++ b/third_party/rust/time/src/parsing/combinator/mod.rs
@@ -2,6 +2,8 @@
pub(crate) mod rfc;
+use num_conv::prelude::*;
+
use crate::format_description::modifier::Padding;
use crate::parsing::shim::{Integer, IntegerParseBytes};
use crate::parsing::ParsedItem;
@@ -135,7 +137,7 @@ pub(crate) fn n_to_m_digits_padded<'a, const N: u8, const M: u8, T: Integer>(
None => break,
}
}
- let pad_width = (orig_input.len() - input.len()) as u8;
+ let pad_width = (orig_input.len() - input.len()).truncate::<u8>();
orig_input = input;
for _ in 0..(N - pad_width) {
diff --git a/third_party/rust/time/src/parsing/combinator/rfc/iso8601.rs b/third_party/rust/time/src/parsing/combinator/rfc/iso8601.rs
index 613a9057ff..91eea2dc25 100644
--- a/third_party/rust/time/src/parsing/combinator/rfc/iso8601.rs
+++ b/third_party/rust/time/src/parsing/combinator/rfc/iso8601.rs
@@ -4,6 +4,8 @@
use core::num::{NonZeroU16, NonZeroU8};
+use num_conv::prelude::*;
+
use crate::parsing::combinator::{any_digit, ascii_char, exactly_n_digits, first_match, sign};
use crate::parsing::ParsedItem;
use crate::{Month, Weekday};
@@ -62,10 +64,10 @@ impl ExtendedKind {
pub(crate) fn year(input: &[u8]) -> Option<ParsedItem<'_, i32>> {
Some(match sign(input) {
Some(ParsedItem(input, sign)) => exactly_n_digits::<6, u32>(input)?.map(|val| {
- let val = val as i32;
+ let val = val.cast_signed();
if sign == b'-' { -val } else { val }
}),
- None => exactly_n_digits::<4, u32>(input)?.map(|val| val as _),
+ None => exactly_n_digits::<4, u32>(input)?.map(|val| val.cast_signed()),
})
}
diff --git a/third_party/rust/time/src/parsing/component.rs b/third_party/rust/time/src/parsing/component.rs
index 23035893e4..e2a7e7c0d9 100644
--- a/third_party/rust/time/src/parsing/component.rs
+++ b/third_party/rust/time/src/parsing/component.rs
@@ -2,6 +2,8 @@
use core::num::{NonZeroU16, NonZeroU8};
+use num_conv::prelude::*;
+
use crate::convert::*;
use crate::format_description::modifier;
#[cfg(feature = "large-dates")]
@@ -25,14 +27,14 @@ pub(crate) fn parse_year(input: &[u8], modifiers: modifier::Year) -> Option<Pars
let ParsedItem(input, year) =
n_to_m_digits_padded::<4, 6, u32>(modifiers.padding)(input)?;
match sign {
- Some(b'-') => Some(ParsedItem(input, -(year as i32))),
+ Some(b'-') => Some(ParsedItem(input, -year.cast_signed())),
None if modifiers.sign_is_mandatory || year >= 10_000 => None,
- _ => Some(ParsedItem(input, year as i32)),
+ _ => Some(ParsedItem(input, year.cast_signed())),
}
}
- modifier::YearRepr::LastTwo => {
- Some(exactly_n_digits_padded::<2, u32>(modifiers.padding)(input)?.map(|v| v as i32))
- }
+ modifier::YearRepr::LastTwo => Some(
+ exactly_n_digits_padded::<2, u32>(modifiers.padding)(input)?.map(|v| v.cast_signed()),
+ ),
}
}
@@ -243,11 +245,11 @@ pub(crate) fn parse_subsecond(
Nine => exactly_n_digits::<9, _>(input)?,
OneOrMore => {
let ParsedItem(mut input, mut value) =
- any_digit(input)?.map(|v| (v - b'0') as u32 * 100_000_000);
+ any_digit(input)?.map(|v| (v - b'0').extend::<u32>() * 100_000_000);
let mut multiplier = 10_000_000;
while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
- value += (digit - b'0') as u32 * multiplier;
+ value += (digit - b'0').extend::<u32>() * multiplier;
input = new_input;
multiplier /= 10;
}
@@ -269,9 +271,9 @@ pub(crate) fn parse_offset_hour(
let ParsedItem(input, sign) = opt(sign)(input);
let ParsedItem(input, hour) = exactly_n_digits_padded::<2, u8>(modifiers.padding)(input)?;
match sign {
- Some(b'-') => Some(ParsedItem(input, (-(hour as i8), true))),
+ Some(b'-') => Some(ParsedItem(input, (-hour.cast_signed(), true))),
None if modifiers.sign_is_mandatory => None,
- _ => Some(ParsedItem(input, (hour as i8, false))),
+ _ => Some(ParsedItem(input, (hour.cast_signed(), false))),
}
}
@@ -282,7 +284,7 @@ pub(crate) fn parse_offset_minute(
) -> Option<ParsedItem<'_, i8>> {
Some(
exactly_n_digits_padded::<2, u8>(modifiers.padding)(input)?
- .map(|offset_minute| offset_minute as _),
+ .map(|offset_minute| offset_minute.cast_signed()),
)
}
@@ -293,7 +295,7 @@ pub(crate) fn parse_offset_second(
) -> Option<ParsedItem<'_, i8>> {
Some(
exactly_n_digits_padded::<2, u8>(modifiers.padding)(input)?
- .map(|offset_second| offset_second as _),
+ .map(|offset_second| offset_second.cast_signed()),
)
}
// endregion offset components
@@ -304,7 +306,7 @@ pub(crate) fn parse_ignore(
modifiers: modifier::Ignore,
) -> Option<ParsedItem<'_, ()>> {
let modifier::Ignore { count } = modifiers;
- let input = input.get((count.get() as usize)..)?;
+ let input = input.get((count.get().extend())..)?;
Some(ParsedItem(input, ()))
}
@@ -315,19 +317,30 @@ pub(crate) fn parse_unix_timestamp(
) -> Option<ParsedItem<'_, i128>> {
let ParsedItem(input, sign) = opt(sign)(input);
let ParsedItem(input, nano_timestamp) = match modifiers.precision {
- modifier::UnixTimestampPrecision::Second => {
- n_to_m_digits::<1, 14, u128>(input)?.map(|val| val * Nanosecond.per(Second) as u128)
- }
+ modifier::UnixTimestampPrecision::Second => n_to_m_digits::<1, 14, u128>(input)?
+ .map(|val| val * Nanosecond::per(Second).extend::<u128>()),
modifier::UnixTimestampPrecision::Millisecond => n_to_m_digits::<1, 17, u128>(input)?
- .map(|val| val * Nanosecond.per(Millisecond) as u128),
+ .map(|val| val * Nanosecond::per(Millisecond).extend::<u128>()),
modifier::UnixTimestampPrecision::Microsecond => n_to_m_digits::<1, 20, u128>(input)?
- .map(|val| val * Nanosecond.per(Microsecond) as u128),
+ .map(|val| val * Nanosecond::per(Microsecond).extend::<u128>()),
modifier::UnixTimestampPrecision::Nanosecond => n_to_m_digits::<1, 23, _>(input)?,
};
match sign {
- Some(b'-') => Some(ParsedItem(input, -(nano_timestamp as i128))),
+ Some(b'-') => Some(ParsedItem(input, -nano_timestamp.cast_signed())),
None if modifiers.sign_is_mandatory => None,
- _ => Some(ParsedItem(input, nano_timestamp as _)),
+ _ => Some(ParsedItem(input, nano_timestamp.cast_signed())),
+ }
+}
+
+/// Parse the `end` component, which represents the end of input. If any input is remaining, `None`
+/// is returned.
+pub(crate) const fn parse_end(input: &[u8], end: modifier::End) -> Option<ParsedItem<'_, ()>> {
+ let modifier::End {} = end;
+
+ if input.is_empty() {
+ Some(ParsedItem(input, ()))
+ } else {
+ None
}
}
diff --git a/third_party/rust/time/src/parsing/iso8601.rs b/third_party/rust/time/src/parsing/iso8601.rs
index 87e517e376..000835b0dd 100644
--- a/third_party/rust/time/src/parsing/iso8601.rs
+++ b/third_party/rust/time/src/parsing/iso8601.rs
@@ -1,5 +1,7 @@
//! Parse parts of an ISO 8601-formatted value.
+use num_conv::prelude::*;
+
use crate::convert::*;
use crate::error;
use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
@@ -34,7 +36,7 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
None => ExtendedKind::Basic, // no separator before mandatory month/ordinal/week
};
- let mut ret_error = match (|| {
+ let parsed_month_day = (|| {
let ParsedItem(mut input, month) = month(input).ok_or(InvalidComponent("month"))?;
if extended_kind.is_extended() {
input = ascii_char::<b'-'>(input)
@@ -43,7 +45,8 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
}
let ParsedItem(input, day) = day(input).ok_or(InvalidComponent("day"))?;
Ok(ParsedItem(input, (month, day)))
- })() {
+ })();
+ let mut ret_error = match parsed_month_day {
Ok(ParsedItem(input, (month, day))) => {
*parsed = parsed
.with_year(year)
@@ -67,7 +70,7 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
return Ok(input);
}
- match (|| {
+ let parsed_week_weekday = (|| {
let input = ascii_char::<b'W'>(input)
.ok_or((false, InvalidLiteral))?
.into_inner();
@@ -81,7 +84,8 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
let ParsedItem(input, weekday) =
dayk(input).ok_or((true, InvalidComponent("weekday")))?;
Ok(ParsedItem(input, (week, weekday)))
- })() {
+ })();
+ match parsed_week_weekday {
Ok(ParsedItem(input, (week, weekday))) => {
*parsed = parsed
.with_iso_year(year)
@@ -125,16 +129,16 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
*parsed = parsed
.with_hour_24(hour)
.ok_or(InvalidComponent("hour"))?
- .with_minute((fractional_part * Second.per(Minute) as f64) as _)
+ .with_minute((fractional_part * Second::per(Minute) as f64) as _)
.ok_or(InvalidComponent("minute"))?
.with_second(
- (fractional_part * Second.per(Hour) as f64 % Minute.per(Hour) as f64)
+ (fractional_part * Second::per(Hour) as f64 % Minute::per(Hour) as f64)
as _,
)
.ok_or(InvalidComponent("second"))?
.with_subsecond(
- (fractional_part * Nanosecond.per(Hour) as f64
- % Nanosecond.per(Second) as f64) as _,
+ (fractional_part * Nanosecond::per(Hour) as f64
+ % Nanosecond::per(Second) as f64) as _,
)
.ok_or(InvalidComponent("subsecond"))?;
return Ok(input);
@@ -162,11 +166,11 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
*parsed = parsed
.with_minute(minute)
.ok_or(InvalidComponent("minute"))?
- .with_second((fractional_part * Second.per(Minute) as f64) as _)
+ .with_second((fractional_part * Second::per(Minute) as f64) as _)
.ok_or(InvalidComponent("second"))?
.with_subsecond(
- (fractional_part * Nanosecond.per(Minute) as f64
- % Nanosecond.per(Second) as f64) as _,
+ (fractional_part * Nanosecond::per(Minute) as f64
+ % Nanosecond::per(Second) as f64) as _,
)
.ok_or(InvalidComponent("subsecond"))?;
return Ok(input);
@@ -209,7 +213,7 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
Some(ParsedItem(input, (second, Some(fractional_part)))) => (
input,
second,
- round(fractional_part * Nanosecond.per(Second) as f64) as _,
+ round(fractional_part * Nanosecond::per(Second) as f64) as _,
),
None if extended_kind.is_extended() => {
return Err(error::Parse::ParseFromDescription(InvalidComponent(
@@ -254,9 +258,9 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
.and_then(|parsed_item| {
parsed_item.consume_value(|hour| {
parsed.set_offset_hour(if sign == b'-' {
- -(hour as i8)
+ -hour.cast_signed()
} else {
- hour as _
+ hour.cast_signed()
})
})
})
@@ -276,9 +280,9 @@ impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
input = new_input;
parsed
.set_offset_minute_signed(if sign == b'-' {
- -(min as i8)
+ -min.cast_signed()
} else {
- min as _
+ min.cast_signed()
})
.ok_or(InvalidComponent("offset minute"))?;
}
diff --git a/third_party/rust/time/src/parsing/mod.rs b/third_party/rust/time/src/parsing/mod.rs
index 4a2aa829f1..e65cfd5664 100644
--- a/third_party/rust/time/src/parsing/mod.rs
+++ b/third_party/rust/time/src/parsing/mod.rs
@@ -31,6 +31,13 @@ impl<'a, T> ParsedItem<'a, T> {
f(self.1)?;
Some(self.0)
}
+
+ /// Filter the value with the provided function. If the function returns `false`, the value
+ /// is discarded and `None` is returned. Otherwise, the value is preserved and `Some(self)` is
+ /// returned.
+ pub(crate) fn filter(self, f: impl FnOnce(&T) -> bool) -> Option<ParsedItem<'a, T>> {
+ f(&self.1).then_some(self)
+ }
}
impl<'a> ParsedItem<'a, ()> {
diff --git a/third_party/rust/time/src/parsing/parsable.rs b/third_party/rust/time/src/parsing/parsable.rs
index 78bbe64f0f..1679f63686 100644
--- a/third_party/rust/time/src/parsing/parsable.rs
+++ b/third_party/rust/time/src/parsing/parsable.rs
@@ -2,21 +2,24 @@
use core::ops::Deref;
-use crate::date_time::{maybe_offset_from_offset, MaybeOffset};
+use num_conv::prelude::*;
+
use crate::error::TryFromParsed;
use crate::format_description::well_known::iso8601::EncodedConfig;
use crate::format_description::well_known::{Iso8601, Rfc2822, Rfc3339};
-use crate::format_description::FormatItem;
+use crate::format_description::BorrowedFormatItem;
#[cfg(feature = "alloc")]
use crate::format_description::OwnedFormatItem;
+use crate::internal_macros::bug;
use crate::parsing::{Parsed, ParsedItem};
-use crate::{error, Date, DateTime, Month, Time, UtcOffset, Weekday};
+use crate::{error, Date, Month, OffsetDateTime, Time, UtcOffset, Weekday};
/// A type that can be parsed.
#[cfg_attr(__time_03_docs, doc(notable_trait))]
+#[doc(alias = "Parseable")]
pub trait Parsable: sealed::Sealed {}
-impl Parsable for FormatItem<'_> {}
-impl Parsable for [FormatItem<'_>] {}
+impl Parsable for BorrowedFormatItem<'_> {}
+impl Parsable for [BorrowedFormatItem<'_>] {}
#[cfg(feature = "alloc")]
impl Parsable for OwnedFormatItem {}
#[cfg(feature = "alloc")]
@@ -31,6 +34,7 @@ impl<T: Deref> Parsable for T where T::Target: Parsable {}
mod sealed {
#[allow(clippy::wildcard_imports)]
use super::*;
+ use crate::PrimitiveDateTime;
/// Parse the item using a format description and an input.
pub trait Sealed {
@@ -52,7 +56,9 @@ mod sealed {
if self.parse_into(input, &mut parsed)?.is_empty() {
Ok(parsed)
} else {
- Err(error::Parse::UnexpectedTrailingCharacters)
+ Err(error::Parse::ParseFromDescription(
+ error::ParseFromDescription::UnexpectedTrailingCharacters,
+ ))
}
}
@@ -71,18 +77,23 @@ mod sealed {
Ok(self.parse(input)?.try_into()?)
}
- /// Parse a [`DateTime`] from the format description.
- fn parse_date_time<O: MaybeOffset>(
+ /// Parse a [`PrimitiveDateTime`] from the format description.
+ fn parse_primitive_date_time(
&self,
input: &[u8],
- ) -> Result<DateTime<O>, error::Parse> {
+ ) -> Result<PrimitiveDateTime, error::Parse> {
+ Ok(self.parse(input)?.try_into()?)
+ }
+
+ /// Parse a [`OffsetDateTime`] from the format description.
+ fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
Ok(self.parse(input)?.try_into()?)
}
}
}
// region: custom formats
-impl sealed::Sealed for FormatItem<'_> {
+impl sealed::Sealed for BorrowedFormatItem<'_> {
fn parse_into<'a>(
&self,
input: &'a [u8],
@@ -92,7 +103,7 @@ impl sealed::Sealed for FormatItem<'_> {
}
}
-impl sealed::Sealed for [FormatItem<'_>] {
+impl sealed::Sealed for [BorrowedFormatItem<'_>] {
fn parse_into<'a>(
&self,
input: &'a [u8],
@@ -154,8 +165,8 @@ impl sealed::Sealed for Rfc2822 {
let colon = ascii_char::<b':'>;
let comma = ascii_char::<b','>;
- let input = opt(fws)(input).into_inner();
- let input = first_match(
+ let input = opt(cfws)(input).into_inner();
+ let weekday = first_match(
[
(b"Mon".as_slice(), Weekday::Monday),
(b"Tue".as_slice(), Weekday::Tuesday),
@@ -166,11 +177,16 @@ impl sealed::Sealed for Rfc2822 {
(b"Sun".as_slice(), Weekday::Sunday),
],
false,
- )(input)
- .and_then(|item| item.consume_value(|value| parsed.set_weekday(value)))
- .ok_or(InvalidComponent("weekday"))?;
- let input = comma(input).ok_or(InvalidLiteral)?.into_inner();
- let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
+ )(input);
+ let input = if let Some(item) = weekday {
+ let input = item
+ .consume_value(|value| parsed.set_weekday(value))
+ .ok_or(InvalidComponent("weekday"))?;
+ let input = comma(input).ok_or(InvalidLiteral)?.into_inner();
+ opt(cfws)(input).into_inner()
+ } else {
+ input
+ };
let input = n_to_m_digits::<1, 2, _>(input)
.and_then(|item| item.consume_value(|value| parsed.set_day(value)))
.ok_or(InvalidComponent("day"))?;
@@ -199,7 +215,9 @@ impl sealed::Sealed for Rfc2822 {
Some(item) => {
let input = item
.flat_map(|year| if year >= 1900 { Some(year) } else { None })
- .and_then(|item| item.consume_value(|value| parsed.set_year(value as _)))
+ .and_then(|item| {
+ item.consume_value(|value| parsed.set_year(value.cast_signed()))
+ })
.ok_or(InvalidComponent("year"))?;
fws(input).ok_or(InvalidLiteral)?.into_inner()
}
@@ -207,7 +225,7 @@ impl sealed::Sealed for Rfc2822 {
let input = exactly_n_digits::<2, u32>(input)
.and_then(|item| {
item.map(|year| if year < 50 { year + 2000 } else { year + 1900 })
- .map(|year| year as _)
+ .map(|year| year.cast_signed())
.consume_value(|value| parsed.set_year(value))
})
.ok_or(InvalidComponent("year"))?;
@@ -237,7 +255,7 @@ impl sealed::Sealed for Rfc2822 {
};
// The RFC explicitly allows leap seconds.
- parsed.set_flag(Parsed::LEAP_SECOND_ALLOWED_FLAG, true);
+ parsed.leap_second_allowed = true;
#[allow(clippy::unnecessary_lazy_evaluations)] // rust-lang/rust-clippy#8522
let zone_literal = first_match(
@@ -280,9 +298,9 @@ impl sealed::Sealed for Rfc2822 {
.and_then(|item| {
item.map(|offset_hour| {
if offset_sign == b'-' {
- -(offset_hour as i8)
+ -offset_hour.cast_signed()
} else {
- offset_hour as _
+ offset_hour.cast_signed()
}
})
.consume_value(|value| parsed.set_offset_hour(value))
@@ -290,14 +308,16 @@ impl sealed::Sealed for Rfc2822 {
.ok_or(InvalidComponent("offset hour"))?;
let input = exactly_n_digits::<2, u8>(input)
.and_then(|item| {
- item.consume_value(|value| parsed.set_offset_minute_signed(value as _))
+ item.consume_value(|value| parsed.set_offset_minute_signed(value.cast_signed()))
})
.ok_or(InvalidComponent("offset minute"))?;
+ let input = opt(cfws)(input).into_inner();
+
Ok(input)
}
- fn parse_date_time<O: MaybeOffset>(&self, input: &[u8]) -> Result<DateTime<O>, error::Parse> {
+ fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
use crate::parsing::combinator::rfc::rfc2822::{cfws, fws};
use crate::parsing::combinator::{
@@ -307,10 +327,10 @@ impl sealed::Sealed for Rfc2822 {
let colon = ascii_char::<b':'>;
let comma = ascii_char::<b','>;
- let input = opt(fws)(input).into_inner();
+ let input = opt(cfws)(input).into_inner();
// This parses the weekday, but we don't actually use the value anywhere. Because of this,
// just return `()` to avoid unnecessary generated code.
- let ParsedItem(input, ()) = first_match(
+ let weekday = first_match(
[
(b"Mon".as_slice(), ()),
(b"Tue".as_slice(), ()),
@@ -321,10 +341,14 @@ impl sealed::Sealed for Rfc2822 {
(b"Sun".as_slice(), ()),
],
false,
- )(input)
- .ok_or(InvalidComponent("weekday"))?;
- let input = comma(input).ok_or(InvalidLiteral)?.into_inner();
- let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
+ )(input);
+ let input = if let Some(item) = weekday {
+ let input = item.into_inner();
+ let input = comma(input).ok_or(InvalidLiteral)?.into_inner();
+ opt(cfws)(input).into_inner()
+ } else {
+ input
+ };
let ParsedItem(input, day) =
n_to_m_digits::<1, 2, _>(input).ok_or(InvalidComponent("day"))?;
let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
@@ -417,26 +441,28 @@ impl sealed::Sealed for Rfc2822 {
.map(|item| {
item.map(|offset_hour| {
if offset_sign == b'-' {
- -(offset_hour as i8)
+ -offset_hour.cast_signed()
} else {
- offset_hour as _
+ offset_hour.cast_signed()
}
})
})
.ok_or(InvalidComponent("offset hour"))?;
let ParsedItem(input, offset_minute) =
exactly_n_digits::<2, u8>(input).ok_or(InvalidComponent("offset minute"))?;
- (input, offset_hour, offset_minute as i8)
+ (input, offset_hour, offset_minute.cast_signed())
};
+ let input = opt(cfws)(input).into_inner();
+
if !input.is_empty() {
- return Err(error::Parse::UnexpectedTrailingCharacters);
+ return Err(error::Parse::ParseFromDescription(
+ error::ParseFromDescription::UnexpectedTrailingCharacters,
+ ));
}
let mut nanosecond = 0;
- let leap_second_input = if !O::HAS_LOGICAL_OFFSET {
- false
- } else if second == 60 {
+ let leap_second_input = if second == 60 {
second = 59;
nanosecond = 999_999_999;
true
@@ -445,14 +471,10 @@ impl sealed::Sealed for Rfc2822 {
};
let dt = (|| {
- let date = Date::from_calendar_date(year as _, month, day)?;
+ let date = Date::from_calendar_date(year.cast_signed(), month, day)?;
let time = Time::from_hms_nano(hour, minute, second, nanosecond)?;
let offset = UtcOffset::from_hms(offset_hour, offset_minute, 0)?;
- Ok(DateTime {
- date,
- time,
- offset: maybe_offset_from_offset::<O>(offset),
- })
+ Ok(OffsetDateTime::new_in_offset(date, time, offset))
})()
.map_err(TryFromParsed::ComponentRange)?;
@@ -487,7 +509,7 @@ impl sealed::Sealed for Rfc3339 {
let colon = ascii_char::<b':'>;
let input = exactly_n_digits::<4, u32>(input)
- .and_then(|item| item.consume_value(|value| parsed.set_year(value as _)))
+ .and_then(|item| item.consume_value(|value| parsed.set_year(value.cast_signed())))
.ok_or(InvalidComponent("year"))?;
let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
let input = exactly_n_digits::<2, _>(input)
@@ -515,11 +537,11 @@ impl sealed::Sealed for Rfc3339 {
let input = if let Some(ParsedItem(input, ())) = ascii_char::<b'.'>(input) {
let ParsedItem(mut input, mut value) = any_digit(input)
.ok_or(InvalidComponent("subsecond"))?
- .map(|v| (v - b'0') as u32 * 100_000_000);
+ .map(|v| (v - b'0').extend::<u32>() * 100_000_000);
let mut multiplier = 10_000_000;
while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
- value += (digit - b'0') as u32 * multiplier;
+ value += (digit - b'0').extend::<u32>() * multiplier;
input = new_input;
multiplier /= 10;
}
@@ -533,7 +555,7 @@ impl sealed::Sealed for Rfc3339 {
};
// The RFC explicitly allows leap seconds.
- parsed.set_flag(Parsed::LEAP_SECOND_ALLOWED_FLAG, true);
+ parsed.leap_second_allowed = true;
if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) {
parsed
@@ -551,14 +573,15 @@ impl sealed::Sealed for Rfc3339 {
let ParsedItem(input, offset_sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
let input = exactly_n_digits::<2, u8>(input)
.and_then(|item| {
- item.map(|offset_hour| {
- if offset_sign == b'-' {
- -(offset_hour as i8)
- } else {
- offset_hour as _
- }
- })
- .consume_value(|value| parsed.set_offset_hour(value))
+ item.filter(|&offset_hour| offset_hour <= 23)?
+ .map(|offset_hour| {
+ if offset_sign == b'-' {
+ -offset_hour.cast_signed()
+ } else {
+ offset_hour.cast_signed()
+ }
+ })
+ .consume_value(|value| parsed.set_offset_hour(value))
})
.ok_or(InvalidComponent("offset hour"))?;
let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
@@ -566,9 +589,9 @@ impl sealed::Sealed for Rfc3339 {
.and_then(|item| {
item.map(|offset_minute| {
if offset_sign == b'-' {
- -(offset_minute as i8)
+ -offset_minute.cast_signed()
} else {
- offset_minute as _
+ offset_minute.cast_signed()
}
})
.consume_value(|value| parsed.set_offset_minute_signed(value))
@@ -578,7 +601,7 @@ impl sealed::Sealed for Rfc3339 {
Ok(input)
}
- fn parse_date_time<O: MaybeOffset>(&self, input: &[u8]) -> Result<DateTime<O>, error::Parse> {
+ fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
use crate::parsing::combinator::{
any_digit, ascii_char, ascii_char_ignore_case, exactly_n_digits, sign,
@@ -610,11 +633,11 @@ impl sealed::Sealed for Rfc3339 {
if let Some(ParsedItem(input, ())) = ascii_char::<b'.'>(input) {
let ParsedItem(mut input, mut value) = any_digit(input)
.ok_or(InvalidComponent("subsecond"))?
- .map(|v| (v - b'0') as u32 * 100_000_000);
+ .map(|v| (v - b'0').extend::<u32>() * 100_000_000);
let mut multiplier = 10_000_000;
while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
- value += (digit - b'0') as u32 * multiplier;
+ value += (digit - b'0').extend::<u32>() * multiplier;
input = new_input;
multiplier /= 10;
}
@@ -629,21 +652,22 @@ impl sealed::Sealed for Rfc3339 {
} else {
let ParsedItem(input, offset_sign) =
sign(input).ok_or(InvalidComponent("offset hour"))?;
- let ParsedItem(input, offset_hour) =
- exactly_n_digits::<2, u8>(input).ok_or(InvalidComponent("offset hour"))?;
+ let ParsedItem(input, offset_hour) = exactly_n_digits::<2, u8>(input)
+ .and_then(|parsed| parsed.filter(|&offset_hour| offset_hour <= 23))
+ .ok_or(InvalidComponent("offset hour"))?;
let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
let ParsedItem(input, offset_minute) =
exactly_n_digits::<2, u8>(input).ok_or(InvalidComponent("offset minute"))?;
UtcOffset::from_hms(
if offset_sign == b'-' {
- -(offset_hour as i8)
+ -offset_hour.cast_signed()
} else {
- offset_hour as _
+ offset_hour.cast_signed()
},
if offset_sign == b'-' {
- -(offset_minute as i8)
+ -offset_minute.cast_signed()
} else {
- offset_minute as _
+ offset_minute.cast_signed()
},
0,
)
@@ -662,7 +686,9 @@ impl sealed::Sealed for Rfc3339 {
};
if !input.is_empty() {
- return Err(error::Parse::UnexpectedTrailingCharacters);
+ return Err(error::Parse::ParseFromDescription(
+ error::ParseFromDescription::UnexpectedTrailingCharacters,
+ ));
}
// The RFC explicitly permits leap seconds. We don't currently support them, so treat it as
@@ -677,12 +703,11 @@ impl sealed::Sealed for Rfc3339 {
};
let date = Month::from_number(month)
- .and_then(|month| Date::from_calendar_date(year as _, month, day))
+ .and_then(|month| Date::from_calendar_date(year.cast_signed(), month, day))
.map_err(TryFromParsed::ComponentRange)?;
let time = Time::from_hms_nano(hour, minute, second, nanosecond)
.map_err(TryFromParsed::ComponentRange)?;
- let offset = maybe_offset_from_offset::<O>(offset);
- let dt = DateTime { date, time, offset };
+ let dt = OffsetDateTime::new_in_offset(date, time, offset);
if leap_second_input && !dt.is_valid_leap_second_stand_in() {
return Err(error::Parse::TryFromParsed(TryFromParsed::ComponentRange(
@@ -714,6 +739,8 @@ impl<const CONFIG: EncodedConfig> sealed::Sealed for Iso8601<CONFIG> {
let mut offset_is_present = false;
let mut first_error = None;
+ parsed.leap_second_allowed = true;
+
match Self::parse_date(parsed, &mut extended_kind)(input) {
Ok(new_input) => {
input = new_input;
diff --git a/third_party/rust/time/src/parsing/parsed.rs b/third_party/rust/time/src/parsing/parsed.rs
index 04c74a7202..3c6c4f5119 100644
--- a/third_party/rust/time/src/parsing/parsed.rs
+++ b/third_party/rust/time/src/parsing/parsed.rs
@@ -1,16 +1,22 @@
//! Information parsed from an input and format description.
-use core::mem::MaybeUninit;
use core::num::{NonZeroU16, NonZeroU8};
-use crate::date_time::{maybe_offset_from_offset, offset_kind, DateTime, MaybeOffset};
+use deranged::{
+ OptionRangedI128, OptionRangedI32, OptionRangedI8, OptionRangedU16, OptionRangedU32,
+ OptionRangedU8, RangedI128, RangedI32, RangedI8, RangedU16, RangedU32, RangedU8,
+};
+use num_conv::prelude::*;
+
+use crate::convert::{Day, Hour, Minute, Nanosecond, Second};
+use crate::date::{MAX_YEAR, MIN_YEAR};
use crate::error::TryFromParsed::InsufficientInformation;
-use crate::format_description::modifier::{WeekNumberRepr, YearRepr};
#[cfg(feature = "alloc")]
use crate::format_description::OwnedFormatItem;
-use crate::format_description::{Component, FormatItem};
+use crate::format_description::{modifier, BorrowedFormatItem, Component};
+use crate::internal_macros::{bug, const_try_opt};
use crate::parsing::component::{
- parse_day, parse_hour, parse_ignore, parse_minute, parse_month, parse_offset_hour,
+ parse_day, parse_end, parse_hour, parse_ignore, parse_minute, parse_month, parse_offset_hour,
parse_offset_minute, parse_offset_second, parse_ordinal, parse_period, parse_second,
parse_subsecond, parse_unix_timestamp, parse_week_number, parse_weekday, parse_year, Period,
};
@@ -32,7 +38,7 @@ mod sealed {
}
}
-impl sealed::AnyFormatItem for FormatItem<'_> {
+impl sealed::AnyFormatItem for BorrowedFormatItem<'_> {
fn parse_item<'a>(
&self,
parsed: &mut Parsed,
@@ -99,10 +105,6 @@ impl sealed::AnyFormatItem for OwnedFormatItem {
}
}
-/// The type of the `flags` field in [`Parsed`]. Allows for changing a single location and having it
-/// effect all uses.
-type Flag = u32;
-
/// All information parsed.
///
/// This information is directly used to construct the final values.
@@ -111,114 +113,111 @@ type Flag = u32;
/// control over values, in the instance that the default parser is insufficient.
#[derive(Debug, Clone, Copy)]
pub struct Parsed {
- /// Bitflags indicating whether a particular field is present.
- flags: Flag,
/// Calendar year.
- year: MaybeUninit<i32>,
+ year: OptionRangedI32<{ MIN_YEAR }, { MAX_YEAR }>,
/// The last two digits of the calendar year.
- year_last_two: MaybeUninit<u8>,
+ year_last_two: OptionRangedU8<0, 99>,
/// Year of the [ISO week date](https://en.wikipedia.org/wiki/ISO_week_date).
- iso_year: MaybeUninit<i32>,
+ iso_year: OptionRangedI32<{ MIN_YEAR }, { MAX_YEAR }>,
/// The last two digits of the ISO week year.
- iso_year_last_two: MaybeUninit<u8>,
+ iso_year_last_two: OptionRangedU8<0, 99>,
/// Month of the year.
month: Option<Month>,
/// Week of the year, where week one begins on the first Sunday of the calendar year.
- sunday_week_number: MaybeUninit<u8>,
+ sunday_week_number: OptionRangedU8<0, 53>,
/// Week of the year, where week one begins on the first Monday of the calendar year.
- monday_week_number: MaybeUninit<u8>,
+ monday_week_number: OptionRangedU8<0, 53>,
/// Week of the year, where week one is the Monday-to-Sunday period containing January 4.
- iso_week_number: Option<NonZeroU8>,
+ iso_week_number: OptionRangedU8<1, 53>,
/// Day of the week.
weekday: Option<Weekday>,
/// Day of the year.
- ordinal: Option<NonZeroU16>,
+ ordinal: OptionRangedU16<1, 366>,
/// Day of the month.
- day: Option<NonZeroU8>,
+ day: OptionRangedU8<1, 31>,
/// Hour within the day.
- hour_24: MaybeUninit<u8>,
+ hour_24: OptionRangedU8<0, { Hour::per(Day) - 1 }>,
/// Hour within the 12-hour period (midnight to noon or vice versa). This is typically used in
/// conjunction with AM/PM, which is indicated by the `hour_12_is_pm` field.
- hour_12: Option<NonZeroU8>,
+ hour_12: OptionRangedU8<1, 12>,
/// Whether the `hour_12` field indicates a time that "PM".
hour_12_is_pm: Option<bool>,
/// Minute within the hour.
- minute: MaybeUninit<u8>,
+ // minute: MaybeUninit<u8>,
+ minute: OptionRangedU8<0, { Minute::per(Hour) - 1 }>,
/// Second within the minute.
- second: MaybeUninit<u8>,
+ // do not subtract one, as leap seconds may be allowed
+ second: OptionRangedU8<0, { Second::per(Minute) }>,
/// Nanosecond within the second.
- subsecond: MaybeUninit<u32>,
+ subsecond: OptionRangedU32<0, { Nanosecond::per(Second) - 1 }>,
/// Whole hours of the UTC offset.
- offset_hour: MaybeUninit<i8>,
+ offset_hour: OptionRangedI8<-23, 23>,
/// Minutes within the hour of the UTC offset.
- offset_minute: MaybeUninit<i8>,
+ offset_minute:
+ OptionRangedI8<{ -((Minute::per(Hour) - 1) as i8) }, { (Minute::per(Hour) - 1) as _ }>,
/// Seconds within the minute of the UTC offset.
- offset_second: MaybeUninit<i8>,
+ offset_second:
+ OptionRangedI8<{ -((Second::per(Minute) - 1) as i8) }, { (Second::per(Minute) - 1) as _ }>,
/// The Unix timestamp in nanoseconds.
- unix_timestamp_nanos: MaybeUninit<i128>,
-}
-
-#[allow(clippy::missing_docs_in_private_items)]
-impl Parsed {
- const YEAR_FLAG: Flag = 1 << 0;
- const YEAR_LAST_TWO_FLAG: Flag = 1 << 1;
- const ISO_YEAR_FLAG: Flag = 1 << 2;
- const ISO_YEAR_LAST_TWO_FLAG: Flag = 1 << 3;
- const SUNDAY_WEEK_NUMBER_FLAG: Flag = 1 << 4;
- const MONDAY_WEEK_NUMBER_FLAG: Flag = 1 << 5;
- const HOUR_24_FLAG: Flag = 1 << 6;
- const MINUTE_FLAG: Flag = 1 << 7;
- const SECOND_FLAG: Flag = 1 << 8;
- const SUBSECOND_FLAG: Flag = 1 << 9;
- const OFFSET_HOUR_FLAG: Flag = 1 << 10;
- const OFFSET_MINUTE_FLAG: Flag = 1 << 11;
- const OFFSET_SECOND_FLAG: Flag = 1 << 12;
+ unix_timestamp_nanos: OptionRangedI128<
+ {
+ OffsetDateTime::new_in_offset(Date::MIN, Time::MIDNIGHT, UtcOffset::UTC)
+ .unix_timestamp_nanos()
+ },
+ {
+ OffsetDateTime::new_in_offset(Date::MAX, Time::MAX, UtcOffset::UTC)
+ .unix_timestamp_nanos()
+ },
+ >,
+ /// Indicates whether the [`UtcOffset`] is negative. This information is obtained when parsing
+ /// the offset hour, but may not otherwise be stored due to "-0" being equivalent to "0".
+ offset_is_negative: Option<bool>,
/// Indicates whether a leap second is permitted to be parsed. This is required by some
/// well-known formats.
- pub(super) const LEAP_SECOND_ALLOWED_FLAG: Flag = 1 << 13;
- /// Indicates whether the `UtcOffset` is negative. This information is obtained when parsing the
- /// offset hour, but may not otherwise be stored due to "-0" being equivalent to "0".
- const OFFSET_IS_NEGATIVE_FLAG: Flag = 1 << 14;
- /// Does the value at `OFFSET_IS_NEGATIVE_FLAG` have any semantic meaning, or is it just the
- /// default value? If the latter, the value should be considered to have no meaning.
- const OFFSET_IS_NEGATIVE_FLAG_IS_INITIALIZED: Flag = 1 << 15;
- const UNIX_TIMESTAMP_NANOS_FLAG: Flag = 1 << 16;
+ pub(super) leap_second_allowed: bool,
+}
+
+impl Default for Parsed {
+ fn default() -> Self {
+ Self::new()
+ }
}
impl Parsed {
/// Create a new instance of `Parsed` with no information known.
pub const fn new() -> Self {
Self {
- flags: 0,
- year: MaybeUninit::uninit(),
- year_last_two: MaybeUninit::uninit(),
- iso_year: MaybeUninit::uninit(),
- iso_year_last_two: MaybeUninit::uninit(),
+ year: OptionRangedI32::None,
+ year_last_two: OptionRangedU8::None,
+ iso_year: OptionRangedI32::None,
+ iso_year_last_two: OptionRangedU8::None,
month: None,
- sunday_week_number: MaybeUninit::uninit(),
- monday_week_number: MaybeUninit::uninit(),
- iso_week_number: None,
+ sunday_week_number: OptionRangedU8::None,
+ monday_week_number: OptionRangedU8::None,
+ iso_week_number: OptionRangedU8::None,
weekday: None,
- ordinal: None,
- day: None,
- hour_24: MaybeUninit::uninit(),
- hour_12: None,
+ ordinal: OptionRangedU16::None,
+ day: OptionRangedU8::None,
+ hour_24: OptionRangedU8::None,
+ hour_12: OptionRangedU8::None,
hour_12_is_pm: None,
- minute: MaybeUninit::uninit(),
- second: MaybeUninit::uninit(),
- subsecond: MaybeUninit::uninit(),
- offset_hour: MaybeUninit::uninit(),
- offset_minute: MaybeUninit::uninit(),
- offset_second: MaybeUninit::uninit(),
- unix_timestamp_nanos: MaybeUninit::uninit(),
+ minute: OptionRangedU8::None,
+ second: OptionRangedU8::None,
+ subsecond: OptionRangedU32::None,
+ offset_hour: OptionRangedI8::None,
+ offset_minute: OptionRangedI8::None,
+ offset_second: OptionRangedI8::None,
+ unix_timestamp_nanos: OptionRangedI128::None,
+ offset_is_negative: None,
+ leap_second_allowed: false,
}
}
- /// Parse a single [`FormatItem`] or [`OwnedFormatItem`], mutating the struct. The remaining
- /// input is returned as the `Ok` value.
+ /// Parse a single [`BorrowedFormatItem`] or [`OwnedFormatItem`], mutating the struct. The
+ /// remaining input is returned as the `Ok` value.
///
- /// If a [`FormatItem::Optional`] or [`OwnedFormatItem::Optional`] is passed, parsing will not
- /// fail; the input will be returned as-is if the expected format is not present.
+ /// If a [`BorrowedFormatItem::Optional`] or [`OwnedFormatItem::Optional`] is passed, parsing
+ /// will not fail; the input will be returned as-is if the expected format is not present.
pub fn parse_item<'a>(
&mut self,
input: &'a [u8],
@@ -227,11 +226,11 @@ impl Parsed {
item.parse_item(self, input)
}
- /// Parse a sequence of [`FormatItem`]s or [`OwnedFormatItem`]s, mutating the struct. The
- /// remaining input is returned as the `Ok` value.
+ /// Parse a sequence of [`BorrowedFormatItem`]s or [`OwnedFormatItem`]s, mutating the struct.
+ /// The remaining input is returned as the `Ok` value.
///
- /// This method will fail if any of the contained [`FormatItem`]s or [`OwnedFormatItem`]s fail
- /// to parse. `self` will not be mutated in this instance.
+ /// This method will fail if any of the contained [`BorrowedFormatItem`]s or
+ /// [`OwnedFormatItem`]s fail to parse. `self` will not be mutated in this instance.
pub fn parse_items<'a>(
&mut self,
mut input: &'a [u8],
@@ -283,11 +282,11 @@ impl Parsed {
let ParsedItem(remaining, value) =
parse_week_number(input, modifiers).ok_or(InvalidComponent("week number"))?;
match modifiers.repr {
- WeekNumberRepr::Iso => {
+ modifier::WeekNumberRepr::Iso => {
NonZeroU8::new(value).and_then(|value| self.set_iso_week_number(value))
}
- WeekNumberRepr::Sunday => self.set_sunday_week_number(value),
- WeekNumberRepr::Monday => self.set_monday_week_number(value),
+ modifier::WeekNumberRepr::Sunday => self.set_sunday_week_number(value),
+ modifier::WeekNumberRepr::Monday => self.set_monday_week_number(value),
}
.ok_or(InvalidComponent("week number"))?;
Ok(remaining)
@@ -296,10 +295,14 @@ impl Parsed {
let ParsedItem(remaining, value) =
parse_year(input, modifiers).ok_or(InvalidComponent("year"))?;
match (modifiers.iso_week_based, modifiers.repr) {
- (false, YearRepr::Full) => self.set_year(value),
- (false, YearRepr::LastTwo) => self.set_year_last_two(value as _),
- (true, YearRepr::Full) => self.set_iso_year(value),
- (true, YearRepr::LastTwo) => self.set_iso_year_last_two(value as _),
+ (false, modifier::YearRepr::Full) => self.set_year(value),
+ (false, modifier::YearRepr::LastTwo) => {
+ self.set_year_last_two(value.cast_unsigned().truncate())
+ }
+ (true, modifier::YearRepr::Full) => self.set_iso_year(value),
+ (true, modifier::YearRepr::LastTwo) => {
+ self.set_iso_year_last_two(value.cast_unsigned().truncate())
+ }
}
.ok_or(InvalidComponent("year"))?;
Ok(remaining)
@@ -332,9 +335,9 @@ impl Parsed {
Component::OffsetHour(modifiers) => parse_offset_hour(input, modifiers)
.and_then(|parsed| {
parsed.consume_value(|(value, is_negative)| {
- self.set_flag(Self::OFFSET_IS_NEGATIVE_FLAG_IS_INITIALIZED, true);
- self.set_flag(Self::OFFSET_IS_NEGATIVE_FLAG, is_negative);
- self.set_offset_hour(value)
+ self.set_offset_hour(value)?;
+ self.offset_is_negative = Some(is_negative);
+ Some(())
})
})
.ok_or(InvalidComponent("offset hour")),
@@ -356,145 +359,152 @@ impl Parsed {
parsed.consume_value(|value| self.set_unix_timestamp_nanos(value))
})
.ok_or(InvalidComponent("unix_timestamp")),
+ Component::End(modifiers) => parse_end(input, modifiers)
+ .map(ParsedItem::<()>::into_inner)
+ .ok_or(error::ParseFromDescription::UnexpectedTrailingCharacters),
}
}
+}
- /// Get the value of the provided flag.
- const fn get_flag(&self, flag: Flag) -> bool {
- self.flags & flag == flag
+/// Getter methods
+impl Parsed {
+ /// Obtain the `year` component.
+ pub const fn year(&self) -> Option<i32> {
+ self.year.get_primitive()
}
- /// Set the value of the provided flag.
- pub(super) fn set_flag(&mut self, flag: Flag, value: bool) {
- if value {
- self.flags |= flag;
- } else {
- self.flags &= !flag;
- }
+ /// Obtain the `year_last_two` component.
+ pub const fn year_last_two(&self) -> Option<u8> {
+ self.year_last_two.get_primitive()
}
-}
-/// Generate getters for each of the fields.
-macro_rules! getters {
- ($($(@$flag:ident)? $name:ident: $ty:ty),+ $(,)?) => {$(
- getters!(! $(@$flag)? $name: $ty);
- )*};
- (! $name:ident : $ty:ty) => {
- /// Obtain the named component.
- pub const fn $name(&self) -> Option<$ty> {
- self.$name
- }
- };
- (! @$flag:ident $name:ident : $ty:ty) => {
- /// Obtain the named component.
- pub const fn $name(&self) -> Option<$ty> {
- if !self.get_flag(Self::$flag) {
- None
- } else {
- // SAFETY: We just checked if the field is present.
- Some(unsafe { self.$name.assume_init() })
- }
- }
- };
-}
+ /// Obtain the `iso_year` component.
+ pub const fn iso_year(&self) -> Option<i32> {
+ self.iso_year.get_primitive()
+ }
-/// Getter methods
-impl Parsed {
- getters! {
- @YEAR_FLAG year: i32,
- @YEAR_LAST_TWO_FLAG year_last_two: u8,
- @ISO_YEAR_FLAG iso_year: i32,
- @ISO_YEAR_LAST_TWO_FLAG iso_year_last_two: u8,
- month: Month,
- @SUNDAY_WEEK_NUMBER_FLAG sunday_week_number: u8,
- @MONDAY_WEEK_NUMBER_FLAG monday_week_number: u8,
- iso_week_number: NonZeroU8,
- weekday: Weekday,
- ordinal: NonZeroU16,
- day: NonZeroU8,
- @HOUR_24_FLAG hour_24: u8,
- hour_12: NonZeroU8,
- hour_12_is_pm: bool,
- @MINUTE_FLAG minute: u8,
- @SECOND_FLAG second: u8,
- @SUBSECOND_FLAG subsecond: u32,
- @OFFSET_HOUR_FLAG offset_hour: i8,
- @UNIX_TIMESTAMP_NANOS_FLAG unix_timestamp_nanos: i128,
- }
-
- /// Obtain the absolute value of the offset minute.
+ /// Obtain the `iso_year_last_two` component.
+ pub const fn iso_year_last_two(&self) -> Option<u8> {
+ self.iso_year_last_two.get_primitive()
+ }
+
+ /// Obtain the `month` component.
+ pub const fn month(&self) -> Option<Month> {
+ self.month
+ }
+
+ /// Obtain the `sunday_week_number` component.
+ pub const fn sunday_week_number(&self) -> Option<u8> {
+ self.sunday_week_number.get_primitive()
+ }
+
+ /// Obtain the `monday_week_number` component.
+ pub const fn monday_week_number(&self) -> Option<u8> {
+ self.monday_week_number.get_primitive()
+ }
+
+ /// Obtain the `iso_week_number` component.
+ pub const fn iso_week_number(&self) -> Option<NonZeroU8> {
+ NonZeroU8::new(const_try_opt!(self.iso_week_number.get_primitive()))
+ }
+
+ /// Obtain the `weekday` component.
+ pub const fn weekday(&self) -> Option<Weekday> {
+ self.weekday
+ }
+
+ /// Obtain the `ordinal` component.
+ pub const fn ordinal(&self) -> Option<NonZeroU16> {
+ NonZeroU16::new(const_try_opt!(self.ordinal.get_primitive()))
+ }
+
+ /// Obtain the `day` component.
+ pub const fn day(&self) -> Option<NonZeroU8> {
+ NonZeroU8::new(const_try_opt!(self.day.get_primitive()))
+ }
+
+ /// Obtain the `hour_24` component.
+ pub const fn hour_24(&self) -> Option<u8> {
+ self.hour_24.get_primitive()
+ }
+
+ /// Obtain the `hour_12` component.
+ pub const fn hour_12(&self) -> Option<NonZeroU8> {
+ NonZeroU8::new(const_try_opt!(self.hour_12.get_primitive()))
+ }
+
+ /// Obtain the `hour_12_is_pm` component.
+ pub const fn hour_12_is_pm(&self) -> Option<bool> {
+ self.hour_12_is_pm
+ }
+
+ /// Obtain the `minute` component.
+ pub const fn minute(&self) -> Option<u8> {
+ self.minute.get_primitive()
+ }
+
+ /// Obtain the `second` component.
+ pub const fn second(&self) -> Option<u8> {
+ self.second.get_primitive()
+ }
+
+ /// Obtain the `subsecond` component.
+ pub const fn subsecond(&self) -> Option<u32> {
+ self.subsecond.get_primitive()
+ }
+
+ /// Obtain the `offset_hour` component.
+ pub const fn offset_hour(&self) -> Option<i8> {
+ self.offset_hour.get_primitive()
+ }
+
+ /// Obtain the absolute value of the `offset_minute` component.
#[doc(hidden)]
#[deprecated(since = "0.3.8", note = "use `parsed.offset_minute_signed()` instead")]
pub const fn offset_minute(&self) -> Option<u8> {
Some(const_try_opt!(self.offset_minute_signed()).unsigned_abs())
}
- /// Obtain the offset minute as an `i8`.
+ /// Obtain the `offset_minute` component.
pub const fn offset_minute_signed(&self) -> Option<i8> {
- if !self.get_flag(Self::OFFSET_MINUTE_FLAG) {
- None
- } else {
- // SAFETY: We just checked if the field is present.
- let value = unsafe { self.offset_minute.assume_init() };
-
- if self.get_flag(Self::OFFSET_IS_NEGATIVE_FLAG_IS_INITIALIZED)
- && (value.is_negative() != self.get_flag(Self::OFFSET_IS_NEGATIVE_FLAG))
- {
- Some(-value)
- } else {
- Some(value)
- }
+ match (self.offset_minute.get_primitive(), self.offset_is_negative) {
+ (Some(offset_minute), Some(true)) => Some(-offset_minute),
+ (Some(offset_minute), _) => Some(offset_minute),
+ (None, _) => None,
}
}
- /// Obtain the absolute value of the offset second.
+ /// Obtain the absolute value of the `offset_second` component.
#[doc(hidden)]
#[deprecated(since = "0.3.8", note = "use `parsed.offset_second_signed()` instead")]
pub const fn offset_second(&self) -> Option<u8> {
Some(const_try_opt!(self.offset_second_signed()).unsigned_abs())
}
- /// Obtain the offset second as an `i8`.
+ /// Obtain the `offset_second` component.
pub const fn offset_second_signed(&self) -> Option<i8> {
- if !self.get_flag(Self::OFFSET_SECOND_FLAG) {
- None
- } else {
- // SAFETY: We just checked if the field is present.
- let value = unsafe { self.offset_second.assume_init() };
-
- if self.get_flag(Self::OFFSET_IS_NEGATIVE_FLAG_IS_INITIALIZED)
- && (value.is_negative() != self.get_flag(Self::OFFSET_IS_NEGATIVE_FLAG))
- {
- Some(-value)
- } else {
- Some(value)
- }
+ match (self.offset_second.get_primitive(), self.offset_is_negative) {
+ (Some(offset_second), Some(true)) => Some(-offset_second),
+ (Some(offset_second), _) => Some(offset_second),
+ (None, _) => None,
}
}
+
+ /// Obtain the `unix_timestamp_nanos` component.
+ pub const fn unix_timestamp_nanos(&self) -> Option<i128> {
+ self.unix_timestamp_nanos.get_primitive()
+ }
}
-/// Generate setters for each of the fields.
-///
-/// This macro should only be used for fields where the value is not validated beyond its type.
+/// Generate setters based on the builders.
macro_rules! setters {
- ($($(@$flag:ident)? $setter_name:ident $name:ident: $ty:ty),+ $(,)?) => {$(
- setters!(! $(@$flag)? $setter_name $name: $ty);
- )*};
- (! $setter_name:ident $name:ident : $ty:ty) => {
- /// Set the named component.
- pub fn $setter_name(&mut self, value: $ty) -> Option<()> {
- self.$name = Some(value);
- Some(())
- }
- };
- (! @$flag:ident $setter_name:ident $name:ident : $ty:ty) => {
- /// Set the named component.
- pub fn $setter_name(&mut self, value: $ty) -> Option<()> {
- self.$name = MaybeUninit::new(value);
- self.set_flag(Self::$flag, true);
+ ($($name:ident $setter:ident $builder:ident $type:ty;)*) => {$(
+ #[doc = concat!("Set the `", stringify!($setter), "` component.")]
+ pub fn $setter(&mut self, value: $type) -> Option<()> {
+ *self = self.$builder(value)?;
Some(())
}
- };
+ )*};
}
/// Setter methods
@@ -503,92 +513,56 @@ macro_rules! setters {
/// setters _may_ fail if the value is invalid, though behavior is not guaranteed.
impl Parsed {
setters! {
- @YEAR_FLAG set_year year: i32,
- @YEAR_LAST_TWO_FLAG set_year_last_two year_last_two: u8,
- @ISO_YEAR_FLAG set_iso_year iso_year: i32,
- @ISO_YEAR_LAST_TWO_FLAG set_iso_year_last_two iso_year_last_two: u8,
- set_month month: Month,
- @SUNDAY_WEEK_NUMBER_FLAG set_sunday_week_number sunday_week_number: u8,
- @MONDAY_WEEK_NUMBER_FLAG set_monday_week_number monday_week_number: u8,
- set_iso_week_number iso_week_number: NonZeroU8,
- set_weekday weekday: Weekday,
- set_ordinal ordinal: NonZeroU16,
- set_day day: NonZeroU8,
- @HOUR_24_FLAG set_hour_24 hour_24: u8,
- set_hour_12 hour_12: NonZeroU8,
- set_hour_12_is_pm hour_12_is_pm: bool,
- @MINUTE_FLAG set_minute minute: u8,
- @SECOND_FLAG set_second second: u8,
- @SUBSECOND_FLAG set_subsecond subsecond: u32,
- @OFFSET_HOUR_FLAG set_offset_hour offset_hour: i8,
- @UNIX_TIMESTAMP_NANOS_FLAG set_unix_timestamp_nanos unix_timestamp_nanos: i128,
- }
-
- /// Set the named component.
+ year set_year with_year i32;
+ year_last_two set_year_last_two with_year_last_two u8;
+ iso_year set_iso_year with_iso_year i32;
+ iso_year_last_two set_iso_year_last_two with_iso_year_last_two u8;
+ month set_month with_month Month;
+ sunday_week_number set_sunday_week_number with_sunday_week_number u8;
+ monday_week_number set_monday_week_number with_monday_week_number u8;
+ iso_week_number set_iso_week_number with_iso_week_number NonZeroU8;
+ weekday set_weekday with_weekday Weekday;
+ ordinal set_ordinal with_ordinal NonZeroU16;
+ day set_day with_day NonZeroU8;
+ hour_24 set_hour_24 with_hour_24 u8;
+ hour_12 set_hour_12 with_hour_12 NonZeroU8;
+ hour_12_is_pm set_hour_12_is_pm with_hour_12_is_pm bool;
+ minute set_minute with_minute u8;
+ second set_second with_second u8;
+ subsecond set_subsecond with_subsecond u32;
+ offset_hour set_offset_hour with_offset_hour i8;
+ offset_minute set_offset_minute_signed with_offset_minute_signed i8;
+ offset_second set_offset_second_signed with_offset_second_signed i8;
+ unix_timestamp_nanos set_unix_timestamp_nanos with_unix_timestamp_nanos i128;
+ }
+
+ /// Set the `offset_minute` component.
#[doc(hidden)]
#[deprecated(
since = "0.3.8",
note = "use `parsed.set_offset_minute_signed()` instead"
)]
pub fn set_offset_minute(&mut self, value: u8) -> Option<()> {
- if value > i8::MAX as u8 {
+ if value > i8::MAX.cast_unsigned() {
None
} else {
- self.set_offset_minute_signed(value as _)
+ self.set_offset_minute_signed(value.cast_signed())
}
}
/// Set the `offset_minute` component.
- pub fn set_offset_minute_signed(&mut self, value: i8) -> Option<()> {
- self.offset_minute = MaybeUninit::new(value);
- self.set_flag(Self::OFFSET_MINUTE_FLAG, true);
- Some(())
- }
-
- /// Set the named component.
#[doc(hidden)]
#[deprecated(
since = "0.3.8",
note = "use `parsed.set_offset_second_signed()` instead"
)]
pub fn set_offset_second(&mut self, value: u8) -> Option<()> {
- if value > i8::MAX as u8 {
+ if value > i8::MAX.cast_unsigned() {
None
} else {
- self.set_offset_second_signed(value as _)
+ self.set_offset_second_signed(value.cast_signed())
}
}
-
- /// Set the `offset_second` component.
- pub fn set_offset_second_signed(&mut self, value: i8) -> Option<()> {
- self.offset_second = MaybeUninit::new(value);
- self.set_flag(Self::OFFSET_SECOND_FLAG, true);
- Some(())
- }
-}
-
-/// Generate build methods for each of the fields.
-///
-/// This macro should only be used for fields where the value is not validated beyond its type.
-macro_rules! builders {
- ($($(@$flag:ident)? $builder_name:ident $name:ident: $ty:ty),+ $(,)?) => {$(
- builders!(! $(@$flag)? $builder_name $name: $ty);
- )*};
- (! $builder_name:ident $name:ident : $ty:ty) => {
- /// Set the named component and return `self`.
- pub const fn $builder_name(mut self, value: $ty) -> Option<Self> {
- self.$name = Some(value);
- Some(self)
- }
- };
- (! @$flag:ident $builder_name:ident $name:ident : $ty:ty) => {
- /// Set the named component and return `self`.
- pub const fn $builder_name(mut self, value: $ty) -> Option<Self> {
- self.$name = MaybeUninit::new(value);
- self.flags |= Self::$flag;
- Some(self)
- }
- };
}
/// Builder methods
@@ -596,29 +570,115 @@ macro_rules! builders {
/// All builder methods return `Option<Self>`, which is `Some` if the value was set, and `None` if
/// not. The builder methods _may_ fail if the value is invalid, though behavior is not guaranteed.
impl Parsed {
- builders! {
- @YEAR_FLAG with_year year: i32,
- @YEAR_LAST_TWO_FLAG with_year_last_two year_last_two: u8,
- @ISO_YEAR_FLAG with_iso_year iso_year: i32,
- @ISO_YEAR_LAST_TWO_FLAG with_iso_year_last_two iso_year_last_two: u8,
- with_month month: Month,
- @SUNDAY_WEEK_NUMBER_FLAG with_sunday_week_number sunday_week_number: u8,
- @MONDAY_WEEK_NUMBER_FLAG with_monday_week_number monday_week_number: u8,
- with_iso_week_number iso_week_number: NonZeroU8,
- with_weekday weekday: Weekday,
- with_ordinal ordinal: NonZeroU16,
- with_day day: NonZeroU8,
- @HOUR_24_FLAG with_hour_24 hour_24: u8,
- with_hour_12 hour_12: NonZeroU8,
- with_hour_12_is_pm hour_12_is_pm: bool,
- @MINUTE_FLAG with_minute minute: u8,
- @SECOND_FLAG with_second second: u8,
- @SUBSECOND_FLAG with_subsecond subsecond: u32,
- @OFFSET_HOUR_FLAG with_offset_hour offset_hour: i8,
- @UNIX_TIMESTAMP_NANOS_FLAG with_unix_timestamp_nanos unix_timestamp_nanos: i128,
- }
-
- /// Set the named component and return `self`.
+ /// Set the `year` component and return `self`.
+ pub const fn with_year(mut self, value: i32) -> Option<Self> {
+ self.year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value)));
+ Some(self)
+ }
+
+ /// Set the `year_last_two` component and return `self`.
+ pub const fn with_year_last_two(mut self, value: u8) -> Option<Self> {
+ self.year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
+ Some(self)
+ }
+
+ /// Set the `iso_year` component and return `self`.
+ pub const fn with_iso_year(mut self, value: i32) -> Option<Self> {
+ self.iso_year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value)));
+ Some(self)
+ }
+
+ /// Set the `iso_year_last_two` component and return `self`.
+ pub const fn with_iso_year_last_two(mut self, value: u8) -> Option<Self> {
+ self.iso_year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
+ Some(self)
+ }
+
+ /// Set the `month` component and return `self`.
+ pub const fn with_month(mut self, value: Month) -> Option<Self> {
+ self.month = Some(value);
+ Some(self)
+ }
+
+ /// Set the `sunday_week_number` component and return `self`.
+ pub const fn with_sunday_week_number(mut self, value: u8) -> Option<Self> {
+ self.sunday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
+ Some(self)
+ }
+
+ /// Set the `monday_week_number` component and return `self`.
+ pub const fn with_monday_week_number(mut self, value: u8) -> Option<Self> {
+ self.monday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
+ Some(self)
+ }
+
+ /// Set the `iso_week_number` component and return `self`.
+ pub const fn with_iso_week_number(mut self, value: NonZeroU8) -> Option<Self> {
+ self.iso_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
+ Some(self)
+ }
+
+ /// Set the `weekday` component and return `self`.
+ pub const fn with_weekday(mut self, value: Weekday) -> Option<Self> {
+ self.weekday = Some(value);
+ Some(self)
+ }
+
+ /// Set the `ordinal` component and return `self`.
+ pub const fn with_ordinal(mut self, value: NonZeroU16) -> Option<Self> {
+ self.ordinal = OptionRangedU16::Some(const_try_opt!(RangedU16::new(value.get())));
+ Some(self)
+ }
+
+ /// Set the `day` component and return `self`.
+ pub const fn with_day(mut self, value: NonZeroU8) -> Option<Self> {
+ self.day = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
+ Some(self)
+ }
+
+ /// Set the `hour_24` component and return `self`.
+ pub const fn with_hour_24(mut self, value: u8) -> Option<Self> {
+ self.hour_24 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
+ Some(self)
+ }
+
+ /// Set the `hour_12` component and return `self`.
+ pub const fn with_hour_12(mut self, value: NonZeroU8) -> Option<Self> {
+ self.hour_12 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get())));
+ Some(self)
+ }
+
+ /// Set the `hour_12_is_pm` component and return `self`.
+ pub const fn with_hour_12_is_pm(mut self, value: bool) -> Option<Self> {
+ self.hour_12_is_pm = Some(value);
+ Some(self)
+ }
+
+ /// Set the `minute` component and return `self`.
+ pub const fn with_minute(mut self, value: u8) -> Option<Self> {
+ self.minute = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
+ Some(self)
+ }
+
+ /// Set the `second` component and return `self`.
+ pub const fn with_second(mut self, value: u8) -> Option<Self> {
+ self.second = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value)));
+ Some(self)
+ }
+
+ /// Set the `subsecond` component and return `self`.
+ pub const fn with_subsecond(mut self, value: u32) -> Option<Self> {
+ self.subsecond = OptionRangedU32::Some(const_try_opt!(RangedU32::new(value)));
+ Some(self)
+ }
+
+ /// Set the `offset_hour` component and return `self`.
+ pub const fn with_offset_hour(mut self, value: i8) -> Option<Self> {
+ self.offset_hour = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
+ Some(self)
+ }
+
+ /// Set the `offset_minute` component and return `self`.
#[doc(hidden)]
#[deprecated(
since = "0.3.8",
@@ -634,12 +694,11 @@ impl Parsed {
/// Set the `offset_minute` component and return `self`.
pub const fn with_offset_minute_signed(mut self, value: i8) -> Option<Self> {
- self.offset_minute = MaybeUninit::new(value);
- self.flags |= Self::OFFSET_MINUTE_FLAG;
+ self.offset_minute = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
Some(self)
}
- /// Set the named component and return `self`.
+ /// Set the `offset_minute` component and return `self`.
#[doc(hidden)]
#[deprecated(
since = "0.3.8",
@@ -655,8 +714,13 @@ impl Parsed {
/// Set the `offset_second` component and return `self`.
pub const fn with_offset_second_signed(mut self, value: i8) -> Option<Self> {
- self.offset_second = MaybeUninit::new(value);
- self.flags |= Self::OFFSET_SECOND_FLAG;
+ self.offset_second = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value)));
+ Some(self)
+ }
+
+ /// Set the `unix_timestamp_nanos` component and return `self`.
+ pub const fn with_unix_timestamp_nanos(mut self, value: i128) -> Option<Self> {
+ self.unix_timestamp_nanos = OptionRangedI128::Some(const_try_opt!(RangedI128::new(value)));
Some(self)
}
}
@@ -682,7 +746,8 @@ impl TryFrom<Parsed> for Date {
/// Get the value needed to adjust the ordinal day for Sunday and Monday-based week
/// numbering.
const fn adjustment(year: i32) -> i16 {
- match Date::__from_ordinal_date_unchecked(year, 1).weekday() {
+ // Safety: `ordinal` is not zero.
+ match unsafe { Date::__from_ordinal_date_unchecked(year, 1) }.weekday() {
Weekday::Monday => 7,
Weekday::Tuesday => 1,
Weekday::Wednesday => 2,
@@ -706,15 +771,17 @@ impl TryFrom<Parsed> for Date {
)?),
(year, sunday_week_number, weekday) => Ok(Self::from_ordinal_date(
year,
- (sunday_week_number as i16 * 7 + weekday.number_days_from_sunday() as i16
+ (sunday_week_number.cast_signed().extend::<i16>() * 7
+ + weekday.number_days_from_sunday().cast_signed().extend::<i16>()
- adjustment(year)
- + 1) as u16,
+ + 1).cast_unsigned(),
)?),
(year, monday_week_number, weekday) => Ok(Self::from_ordinal_date(
year,
- (monday_week_number as i16 * 7 + weekday.number_days_from_monday() as i16
+ (monday_week_number.cast_signed().extend::<i16>() * 7
+ + weekday.number_days_from_monday().cast_signed().extend::<i16>()
- adjustment(year)
- + 1) as u16,
+ + 1).cast_unsigned(),
)?),
_ => Err(InsufficientInformation),
}
@@ -733,6 +800,7 @@ impl TryFrom<Parsed> for Time {
(_, Some(hour), Some(true)) => hour.get() + 12,
_ => return Err(InsufficientInformation),
};
+
if parsed.hour_24().is_none()
&& parsed.hour_12().is_some()
&& parsed.hour_12_is_pm().is_some()
@@ -742,10 +810,17 @@ impl TryFrom<Parsed> for Time {
{
return Ok(Self::from_hms_nano(hour, 0, 0, 0)?);
}
- let minute = parsed.minute().ok_or(InsufficientInformation)?;
- let second = parsed.second().unwrap_or(0);
- let subsecond = parsed.subsecond().unwrap_or(0);
- Ok(Self::from_hms_nano(hour, minute, second, subsecond)?)
+
+ // Reject combinations such as hour-second with minute omitted.
+ match (parsed.minute(), parsed.second(), parsed.subsecond()) {
+ (None, None, None) => Ok(Self::from_hms_nano(hour, 0, 0, 0)?),
+ (Some(minute), None, None) => Ok(Self::from_hms_nano(hour, minute, 0, 0)?),
+ (Some(minute), Some(second), None) => Ok(Self::from_hms_nano(hour, minute, second, 0)?),
+ (Some(minute), Some(second), Some(subsecond)) => {
+ Ok(Self::from_hms_nano(hour, minute, second, subsecond)?)
+ }
+ _ => Err(InsufficientInformation),
+ }
}
}
@@ -772,58 +847,46 @@ impl TryFrom<Parsed> for UtcOffset {
}
impl TryFrom<Parsed> for PrimitiveDateTime {
- type Error = <DateTime<offset_kind::None> as TryFrom<Parsed>>::Error;
+ type Error = error::TryFromParsed;
fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
- parsed.try_into().map(Self)
+ Ok(Self::new(parsed.try_into()?, parsed.try_into()?))
}
}
impl TryFrom<Parsed> for OffsetDateTime {
- type Error = <DateTime<offset_kind::Fixed> as TryFrom<Parsed>>::Error;
-
- fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
- parsed.try_into().map(Self)
- }
-}
-
-impl<O: MaybeOffset> TryFrom<Parsed> for DateTime<O> {
type Error = error::TryFromParsed;
- #[allow(clippy::unwrap_in_result)] // We know the values are valid.
fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
- if O::HAS_LOGICAL_OFFSET {
- if let Some(timestamp) = parsed.unix_timestamp_nanos() {
- let DateTime { date, time, offset } =
- DateTime::<offset_kind::Fixed>::from_unix_timestamp_nanos(timestamp)?;
- return Ok(Self {
- date,
- time,
- offset: maybe_offset_from_offset::<O>(offset),
- });
+ if let Some(timestamp) = parsed.unix_timestamp_nanos() {
+ let mut value = Self::from_unix_timestamp_nanos(timestamp)?;
+ if let Some(subsecond) = parsed.subsecond() {
+ value = value.replace_nanosecond(subsecond)?;
}
+ return Ok(value);
}
// Some well-known formats explicitly allow leap seconds. We don't currently support them,
// so treat it as the nearest preceding moment that can be represented. Because leap seconds
// always fall at the end of a month UTC, reject any that are at other times.
- let leap_second_input =
- if parsed.get_flag(Parsed::LEAP_SECOND_ALLOWED_FLAG) && parsed.second() == Some(60) {
- parsed.set_second(59).expect("59 is a valid second");
- parsed
- .set_subsecond(999_999_999)
- .expect("999_999_999 is a valid subsecond");
- true
- } else {
- false
- };
-
- let dt = Self {
- date: Date::try_from(parsed)?,
- time: Time::try_from(parsed)?,
- offset: O::try_from_parsed(parsed)?,
+ let leap_second_input = if parsed.leap_second_allowed && parsed.second() == Some(60) {
+ if parsed.set_second(59).is_none() {
+ bug!("59 is a valid second");
+ }
+ if parsed.set_subsecond(999_999_999).is_none() {
+ bug!("999_999_999 is a valid subsecond");
+ }
+ true
+ } else {
+ false
};
+ let dt = Self::new_in_offset(
+ Date::try_from(parsed)?,
+ Time::try_from(parsed)?,
+ UtcOffset::try_from(parsed)?,
+ );
+
if leap_second_input && !dt.is_valid_leap_second_stand_in() {
return Err(error::TryFromParsed::ComponentRange(
error::ComponentRange {
diff --git a/third_party/rust/time/src/primitive_date_time.rs b/third_party/rust/time/src/primitive_date_time.rs
index 9850f96d4d..85ae075e80 100644
--- a/third_party/rust/time/src/primitive_date_time.rs
+++ b/third_party/rust/time/src/primitive_date_time.rs
@@ -1,24 +1,29 @@
//! The [`PrimitiveDateTime`] struct and its associated `impl`s.
+#[cfg(feature = "formatting")]
+use alloc::string::String;
use core::fmt;
use core::ops::{Add, AddAssign, Sub, SubAssign};
use core::time::Duration as StdDuration;
#[cfg(feature = "formatting")]
use std::io;
-use crate::date_time::offset_kind;
+use powerfmt::ext::FormatterExt as _;
+use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay};
+
#[cfg(feature = "formatting")]
use crate::formatting::Formattable;
+use crate::internal_macros::{const_try, const_try_opt};
#[cfg(feature = "parsing")]
use crate::parsing::Parsable;
-use crate::{error, Date, DateTime, Duration, Month, OffsetDateTime, Time, UtcOffset, Weekday};
-
-/// The actual type doing all the work.
-type Inner = DateTime<offset_kind::None>;
+use crate::{error, util, Date, Duration, Month, OffsetDateTime, Time, UtcOffset, Weekday};
/// Combined date and time.
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
-pub struct PrimitiveDateTime(#[allow(clippy::missing_docs_in_private_items)] pub(crate) Inner);
+pub struct PrimitiveDateTime {
+ date: Date,
+ time: Time,
+}
impl PrimitiveDateTime {
/// The smallest value that can be represented by `PrimitiveDateTime`.
@@ -48,7 +53,10 @@ impl PrimitiveDateTime {
doc = "assert_eq!(PrimitiveDateTime::MIN, datetime!(-9999-01-01 0:00));"
)]
/// ```
- pub const MIN: Self = Self(Inner::MIN);
+ pub const MIN: Self = Self {
+ date: Date::MIN,
+ time: Time::MIDNIGHT,
+ };
/// The largest value that can be represented by `PrimitiveDateTime`.
///
@@ -77,7 +85,10 @@ impl PrimitiveDateTime {
doc = "assert_eq!(PrimitiveDateTime::MAX, datetime!(+9999-12-31 23:59:59.999_999_999));"
)]
/// ```
- pub const MAX: Self = Self(Inner::MAX);
+ pub const MAX: Self = Self {
+ date: Date::MAX,
+ time: Time::MAX,
+ };
/// Create a new `PrimitiveDateTime` from the provided [`Date`] and [`Time`].
///
@@ -90,7 +101,7 @@ impl PrimitiveDateTime {
/// );
/// ```
pub const fn new(date: Date, time: Time) -> Self {
- Self(Inner::new(date, time))
+ Self { date, time }
}
// region: component getters
@@ -101,7 +112,7 @@ impl PrimitiveDateTime {
/// assert_eq!(datetime!(2019-01-01 0:00).date(), date!(2019-01-01));
/// ```
pub const fn date(self) -> Date {
- self.0.date()
+ self.date
}
/// Get the [`Time`] component of the `PrimitiveDateTime`.
@@ -109,8 +120,9 @@ impl PrimitiveDateTime {
/// ```rust
/// # use time_macros::{datetime, time};
/// assert_eq!(datetime!(2019-01-01 0:00).time(), time!(0:00));
+ /// ```
pub const fn time(self) -> Time {
- self.0.time()
+ self.time
}
// endregion component getters
@@ -124,7 +136,7 @@ impl PrimitiveDateTime {
/// assert_eq!(datetime!(2020-01-01 0:00).year(), 2020);
/// ```
pub const fn year(self) -> i32 {
- self.0.year()
+ self.date().year()
}
/// Get the month of the date.
@@ -136,7 +148,7 @@ impl PrimitiveDateTime {
/// assert_eq!(datetime!(2019-12-31 0:00).month(), Month::December);
/// ```
pub const fn month(self) -> Month {
- self.0.month()
+ self.date().month()
}
/// Get the day of the date.
@@ -149,7 +161,7 @@ impl PrimitiveDateTime {
/// assert_eq!(datetime!(2019-12-31 0:00).day(), 31);
/// ```
pub const fn day(self) -> u8 {
- self.0.day()
+ self.date().day()
}
/// Get the day of the year.
@@ -162,7 +174,7 @@ impl PrimitiveDateTime {
/// assert_eq!(datetime!(2019-12-31 0:00).ordinal(), 365);
/// ```
pub const fn ordinal(self) -> u16 {
- self.0.ordinal()
+ self.date().ordinal()
}
/// Get the ISO week number.
@@ -178,7 +190,7 @@ impl PrimitiveDateTime {
/// assert_eq!(datetime!(2021-01-01 0:00).iso_week(), 53);
/// ```
pub const fn iso_week(self) -> u8 {
- self.0.iso_week()
+ self.date().iso_week()
}
/// Get the week number where week 1 begins on the first Sunday.
@@ -193,7 +205,7 @@ impl PrimitiveDateTime {
/// assert_eq!(datetime!(2021-01-01 0:00).sunday_based_week(), 0);
/// ```
pub const fn sunday_based_week(self) -> u8 {
- self.0.sunday_based_week()
+ self.date().sunday_based_week()
}
/// Get the week number where week 1 begins on the first Monday.
@@ -208,7 +220,7 @@ impl PrimitiveDateTime {
/// assert_eq!(datetime!(2021-01-01 0:00).monday_based_week(), 0);
/// ```
pub const fn monday_based_week(self) -> u8 {
- self.0.monday_based_week()
+ self.date().monday_based_week()
}
/// Get the year, month, and day.
@@ -222,7 +234,7 @@ impl PrimitiveDateTime {
/// );
/// ```
pub const fn to_calendar_date(self) -> (i32, Month, u8) {
- self.0.to_calendar_date()
+ self.date().to_calendar_date()
}
/// Get the year and ordinal day number.
@@ -232,7 +244,7 @@ impl PrimitiveDateTime {
/// assert_eq!(datetime!(2019-01-01 0:00).to_ordinal_date(), (2019, 1));
/// ```
pub const fn to_ordinal_date(self) -> (i32, u16) {
- self.0.to_ordinal_date()
+ self.date().to_ordinal_date()
}
/// Get the ISO 8601 year, week number, and weekday.
@@ -262,7 +274,7 @@ impl PrimitiveDateTime {
/// );
/// ```
pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) {
- self.0.to_iso_week_date()
+ self.date().to_iso_week_date()
}
/// Get the weekday.
@@ -284,7 +296,7 @@ impl PrimitiveDateTime {
/// assert_eq!(datetime!(2019-12-01 0:00).weekday(), Sunday);
/// ```
pub const fn weekday(self) -> Weekday {
- self.0.weekday()
+ self.date().weekday()
}
/// Get the Julian day for the date. The time is not taken into account for this calculation.
@@ -300,7 +312,7 @@ impl PrimitiveDateTime {
/// assert_eq!(datetime!(2019-12-31 0:00).to_julian_day(), 2_458_849);
/// ```
pub const fn to_julian_day(self) -> i32 {
- self.0.to_julian_day()
+ self.date().to_julian_day()
}
// endregion date getters
@@ -313,7 +325,7 @@ impl PrimitiveDateTime {
/// assert_eq!(datetime!(2020-01-01 23:59:59).as_hms(), (23, 59, 59));
/// ```
pub const fn as_hms(self) -> (u8, u8, u8) {
- self.0.as_hms()
+ self.time().as_hms()
}
/// Get the clock hour, minute, second, and millisecond.
@@ -327,7 +339,7 @@ impl PrimitiveDateTime {
/// );
/// ```
pub const fn as_hms_milli(self) -> (u8, u8, u8, u16) {
- self.0.as_hms_milli()
+ self.time().as_hms_milli()
}
/// Get the clock hour, minute, second, and microsecond.
@@ -341,7 +353,7 @@ impl PrimitiveDateTime {
/// );
/// ```
pub const fn as_hms_micro(self) -> (u8, u8, u8, u32) {
- self.0.as_hms_micro()
+ self.time().as_hms_micro()
}
/// Get the clock hour, minute, second, and nanosecond.
@@ -355,7 +367,7 @@ impl PrimitiveDateTime {
/// );
/// ```
pub const fn as_hms_nano(self) -> (u8, u8, u8, u32) {
- self.0.as_hms_nano()
+ self.time().as_hms_nano()
}
/// Get the clock hour.
@@ -368,7 +380,7 @@ impl PrimitiveDateTime {
/// assert_eq!(datetime!(2019-01-01 23:59:59).hour(), 23);
/// ```
pub const fn hour(self) -> u8 {
- self.0.hour()
+ self.time().hour()
}
/// Get the minute within the hour.
@@ -381,7 +393,7 @@ impl PrimitiveDateTime {
/// assert_eq!(datetime!(2019-01-01 23:59:59).minute(), 59);
/// ```
pub const fn minute(self) -> u8 {
- self.0.minute()
+ self.time().minute()
}
/// Get the second within the minute.
@@ -394,7 +406,7 @@ impl PrimitiveDateTime {
/// assert_eq!(datetime!(2019-01-01 23:59:59).second(), 59);
/// ```
pub const fn second(self) -> u8 {
- self.0.second()
+ self.time().second()
}
/// Get the milliseconds within the second.
@@ -407,7 +419,7 @@ impl PrimitiveDateTime {
/// assert_eq!(datetime!(2019-01-01 23:59:59.999).millisecond(), 999);
/// ```
pub const fn millisecond(self) -> u16 {
- self.0.millisecond()
+ self.time().millisecond()
}
/// Get the microseconds within the second.
@@ -423,7 +435,7 @@ impl PrimitiveDateTime {
/// );
/// ```
pub const fn microsecond(self) -> u32 {
- self.0.microsecond()
+ self.time().microsecond()
}
/// Get the nanoseconds within the second.
@@ -439,7 +451,7 @@ impl PrimitiveDateTime {
/// );
/// ```
pub const fn nanosecond(self) -> u32 {
- self.0.nanosecond()
+ self.time().nanosecond()
}
// endregion time getters
@@ -463,7 +475,7 @@ impl PrimitiveDateTime {
/// );
/// ```
pub const fn assume_offset(self, offset: UtcOffset) -> OffsetDateTime {
- OffsetDateTime(self.0.assume_offset(offset))
+ OffsetDateTime::new_in_offset(self.date, self.time, offset)
}
/// Assuming that the existing `PrimitiveDateTime` represents a moment in UTC, return an
@@ -477,7 +489,7 @@ impl PrimitiveDateTime {
/// );
/// ```
pub const fn assume_utc(self) -> OffsetDateTime {
- OffsetDateTime(self.0.assume_utc())
+ self.assume_offset(UtcOffset::UTC)
}
// endregion attach offset
@@ -499,7 +511,17 @@ impl PrimitiveDateTime {
/// );
/// ```
pub const fn checked_add(self, duration: Duration) -> Option<Self> {
- Some(Self(const_try_opt!(self.0.checked_add(duration))))
+ let (date_adjustment, time) = self.time.adjusting_add(duration);
+ let date = const_try_opt!(self.date.checked_add(duration));
+
+ Some(Self {
+ date: match date_adjustment {
+ util::DateAdjustment::Previous => const_try_opt!(date.previous_day()),
+ util::DateAdjustment::Next => const_try_opt!(date.next_day()),
+ util::DateAdjustment::None => date,
+ },
+ time,
+ })
}
/// Computes `self - duration`, returning `None` if an overflow occurred.
@@ -519,7 +541,17 @@ impl PrimitiveDateTime {
/// );
/// ```
pub const fn checked_sub(self, duration: Duration) -> Option<Self> {
- Some(Self(const_try_opt!(self.0.checked_sub(duration))))
+ let (date_adjustment, time) = self.time.adjusting_sub(duration);
+ let date = const_try_opt!(self.date.checked_sub(duration));
+
+ Some(Self {
+ date: match date_adjustment {
+ util::DateAdjustment::Previous => const_try_opt!(date.previous_day()),
+ util::DateAdjustment::Next => const_try_opt!(date.next_day()),
+ util::DateAdjustment::None => date,
+ },
+ time,
+ })
}
// endregion: checked arithmetic
@@ -545,7 +577,13 @@ impl PrimitiveDateTime {
/// );
/// ```
pub const fn saturating_add(self, duration: Duration) -> Self {
- Self(self.0.saturating_add(duration))
+ if let Some(datetime) = self.checked_add(duration) {
+ datetime
+ } else if duration.is_negative() {
+ Self::MIN
+ } else {
+ Self::MAX
+ }
}
/// Computes `self - duration`, saturating value on overflow.
@@ -569,7 +607,13 @@ impl PrimitiveDateTime {
/// );
/// ```
pub const fn saturating_sub(self, duration: Duration) -> Self {
- Self(self.0.saturating_sub(duration))
+ if let Some(datetime) = self.checked_sub(duration) {
+ datetime
+ } else if duration.is_negative() {
+ Self::MAX
+ } else {
+ Self::MIN
+ }
}
// endregion: saturating arithmetic
}
@@ -588,7 +632,10 @@ impl PrimitiveDateTime {
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_time(self, time: Time) -> Self {
- Self(self.0.replace_time(time))
+ Self {
+ date: self.date,
+ time,
+ }
}
/// Replace the date, preserving the time.
@@ -602,7 +649,10 @@ impl PrimitiveDateTime {
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_date(self, date: Date) -> Self {
- Self(self.0.replace_date(date))
+ Self {
+ date,
+ time: self.time,
+ }
}
/// Replace the year. The month and day will be unchanged.
@@ -618,7 +668,10 @@ impl PrimitiveDateTime {
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_year(year))))
+ Ok(Self {
+ date: const_try!(self.date.replace_year(year)),
+ time: self.time,
+ })
}
/// Replace the month of the year.
@@ -634,7 +687,10 @@ impl PrimitiveDateTime {
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_month(month))))
+ Ok(Self {
+ date: const_try!(self.date.replace_month(month)),
+ time: self.time,
+ })
}
/// Replace the day of the month.
@@ -650,7 +706,26 @@ impl PrimitiveDateTime {
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_day(day))))
+ Ok(Self {
+ date: const_try!(self.date.replace_day(day)),
+ time: self.time,
+ })
+ }
+
+ /// Replace the day of the year.
+ ///
+ /// ```rust
+ /// # use time_macros::datetime;
+ /// assert_eq!(datetime!(2022-049 12:00).replace_ordinal(1), Ok(datetime!(2022-001 12:00)));
+ /// assert!(datetime!(2022-049 12:00).replace_ordinal(0).is_err()); // 0 isn't a valid ordinal
+ /// assert!(datetime!(2022-049 12:00).replace_ordinal(366).is_err()); // 2022 isn't a leap year
+ /// ````
+ #[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
+ pub const fn replace_ordinal(self, ordinal: u16) -> Result<Self, error::ComponentRange> {
+ Ok(Self {
+ date: const_try!(self.date.replace_ordinal(ordinal)),
+ time: self.time,
+ })
}
/// Replace the clock hour.
@@ -665,7 +740,10 @@ impl PrimitiveDateTime {
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_hour(self, hour: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_hour(hour))))
+ Ok(Self {
+ date: self.date,
+ time: const_try!(self.time.replace_hour(hour)),
+ })
}
/// Replace the minutes within the hour.
@@ -680,7 +758,10 @@ impl PrimitiveDateTime {
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_minute(self, minute: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_minute(minute))))
+ Ok(Self {
+ date: self.date,
+ time: const_try!(self.time.replace_minute(minute)),
+ })
}
/// Replace the seconds within the minute.
@@ -695,7 +776,10 @@ impl PrimitiveDateTime {
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_second(self, second: u8) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_second(second))))
+ Ok(Self {
+ date: self.date,
+ time: const_try!(self.time.replace_second(second)),
+ })
}
/// Replace the milliseconds within the second.
@@ -713,7 +797,10 @@ impl PrimitiveDateTime {
self,
millisecond: u16,
) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_millisecond(millisecond))))
+ Ok(Self {
+ date: self.date,
+ time: const_try!(self.time.replace_millisecond(millisecond)),
+ })
}
/// Replace the microseconds within the second.
@@ -731,7 +818,10 @@ impl PrimitiveDateTime {
self,
microsecond: u32,
) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_microsecond(microsecond))))
+ Ok(Self {
+ date: self.date,
+ time: const_try!(self.time.replace_microsecond(microsecond)),
+ })
}
/// Replace the nanoseconds within the second.
@@ -746,7 +836,10 @@ impl PrimitiveDateTime {
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_nanosecond(self, nanosecond: u32) -> Result<Self, error::ComponentRange> {
- Ok(Self(const_try!(self.0.replace_nanosecond(nanosecond))))
+ Ok(Self {
+ date: self.date,
+ time: const_try!(self.time.replace_nanosecond(nanosecond)),
+ })
}
}
// endregion replacement
@@ -761,7 +854,7 @@ impl PrimitiveDateTime {
output: &mut impl io::Write,
format: &(impl Formattable + ?Sized),
) -> Result<usize, error::Format> {
- self.0.format_into(output, format)
+ format.format_into(output, Some(self.date), Some(self.time), None)
}
/// Format the `PrimitiveDateTime` using the provided [format
@@ -778,7 +871,7 @@ impl PrimitiveDateTime {
/// # Ok::<_, time::Error>(())
/// ```
pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
- self.0.format(format)
+ format.format(Some(self.date), Some(self.time), None)
}
}
@@ -801,13 +894,33 @@ impl PrimitiveDateTime {
input: &str,
description: &(impl Parsable + ?Sized),
) -> Result<Self, error::Parse> {
- Inner::parse(input, description).map(Self)
+ description.parse_primitive_date_time(input.as_bytes())
+ }
+}
+
+impl SmartDisplay for PrimitiveDateTime {
+ type Metadata = ();
+
+ fn metadata(&self, _: FormatterOptions) -> Metadata<Self> {
+ let width = smart_display::padded_width_of!(self.date, " ", self.time);
+ Metadata::new(width, self, ())
+ }
+
+ fn fmt_with_metadata(
+ &self,
+ f: &mut fmt::Formatter<'_>,
+ metadata: Metadata<Self>,
+ ) -> fmt::Result {
+ f.pad_with_width(
+ metadata.unpadded_width(),
+ format_args!("{} {}", self.date, self.time),
+ )
}
}
impl fmt::Display for PrimitiveDateTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0.fmt(f)
+ SmartDisplay::fmt(self, f)
}
}
@@ -822,64 +935,115 @@ impl fmt::Debug for PrimitiveDateTime {
impl Add<Duration> for PrimitiveDateTime {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn add(self, duration: Duration) -> Self::Output {
- Self(self.0.add(duration))
+ self.checked_add(duration)
+ .expect("resulting value is out of range")
}
}
impl Add<StdDuration> for PrimitiveDateTime {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn add(self, duration: StdDuration) -> Self::Output {
- Self(self.0.add(duration))
+ let (is_next_day, time) = self.time.adjusting_add_std(duration);
+
+ Self {
+ date: if is_next_day {
+ (self.date + duration)
+ .next_day()
+ .expect("resulting value is out of range")
+ } else {
+ self.date + duration
+ },
+ time,
+ }
}
}
impl AddAssign<Duration> for PrimitiveDateTime {
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn add_assign(&mut self, duration: Duration) {
- self.0.add_assign(duration);
+ *self = *self + duration;
}
}
impl AddAssign<StdDuration> for PrimitiveDateTime {
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn add_assign(&mut self, duration: StdDuration) {
- self.0.add_assign(duration);
+ *self = *self + duration;
}
}
impl Sub<Duration> for PrimitiveDateTime {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, duration: Duration) -> Self::Output {
- Self(self.0.sub(duration))
+ self.checked_sub(duration)
+ .expect("resulting value is out of range")
}
}
impl Sub<StdDuration> for PrimitiveDateTime {
type Output = Self;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, duration: StdDuration) -> Self::Output {
- Self(self.0.sub(duration))
+ let (is_previous_day, time) = self.time.adjusting_sub_std(duration);
+
+ Self {
+ date: if is_previous_day {
+ (self.date - duration)
+ .previous_day()
+ .expect("resulting value is out of range")
+ } else {
+ self.date - duration
+ },
+ time,
+ }
}
}
impl SubAssign<Duration> for PrimitiveDateTime {
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub_assign(&mut self, duration: Duration) {
- self.0.sub_assign(duration);
+ *self = *self - duration;
}
}
impl SubAssign<StdDuration> for PrimitiveDateTime {
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub_assign(&mut self, duration: StdDuration) {
- self.0.sub_assign(duration);
+ *self = *self - duration;
}
}
impl Sub for PrimitiveDateTime {
type Output = Duration;
+ /// # Panics
+ ///
+ /// This may panic if an overflow occurs.
fn sub(self, rhs: Self) -> Self::Output {
- self.0.sub(rhs.0)
+ (self.date - rhs.date) + (self.time - rhs.time)
}
}
// endregion trait impls
diff --git a/third_party/rust/time/src/quickcheck.rs b/third_party/rust/time/src/quickcheck.rs
index 4a788b517e..312a09e196 100644
--- a/third_party/rust/time/src/quickcheck.rs
+++ b/third_party/rust/time/src/quickcheck.rs
@@ -2,7 +2,7 @@
//!
//! This enables users to write tests such as this, and have test values provided automatically:
//!
-//! ```
+//! ```ignore
//! # #![allow(dead_code)]
//! use quickcheck::quickcheck;
//! use time::Date;
@@ -38,8 +38,6 @@ use alloc::boxed::Box;
use quickcheck::{empty_shrinker, single_shrinker, Arbitrary, Gen};
-use crate::convert::*;
-use crate::date_time::{DateTime, MaybeOffset};
use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
/// Obtain an arbitrary value between the minimum and maximum inclusive.
@@ -73,25 +71,22 @@ impl Arbitrary for Date {
impl Arbitrary for Duration {
fn arbitrary(g: &mut Gen) -> Self {
- Self::nanoseconds_i128(arbitrary_between!(
- i128;
- g,
- Self::MIN.whole_nanoseconds(),
- Self::MAX.whole_nanoseconds()
- ))
+ Self::new_ranged(<_>::arbitrary(g), <_>::arbitrary(g))
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
- (self.subsec_nanoseconds(), self.whole_seconds())
+ (self.subsec_nanoseconds_ranged(), self.whole_seconds())
.shrink()
.map(|(mut nanoseconds, seconds)| {
// Coerce the sign if necessary.
- if (seconds > 0 && nanoseconds < 0) || (seconds < 0 && nanoseconds > 0) {
- nanoseconds *= -1;
+ if (seconds > 0 && nanoseconds.get() < 0)
+ || (seconds < 0 && nanoseconds.get() > 0)
+ {
+ nanoseconds = nanoseconds.neg();
}
- Self::new_unchecked(seconds, nanoseconds)
+ Self::new_ranged_unchecked(seconds, nanoseconds)
}),
)
}
@@ -99,20 +94,20 @@ impl Arbitrary for Duration {
impl Arbitrary for Time {
fn arbitrary(g: &mut Gen) -> Self {
- Self::__from_hms_nanos_unchecked(
- arbitrary_between!(u8; g, 0, Hour.per(Day) - 1),
- arbitrary_between!(u8; g, 0, Minute.per(Hour) - 1),
- arbitrary_between!(u8; g, 0, Second.per(Minute) - 1),
- arbitrary_between!(u32; g, 0, Nanosecond.per(Second) - 1),
+ Self::from_hms_nanos_ranged(
+ <_>::arbitrary(g),
+ <_>::arbitrary(g),
+ <_>::arbitrary(g),
+ <_>::arbitrary(g),
)
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
- self.as_hms_nano()
+ self.as_hms_nano_ranged()
.shrink()
.map(|(hour, minute, second, nanosecond)| {
- Self::__from_hms_nanos_unchecked(hour, minute, second, nanosecond)
+ Self::from_hms_nanos_ranged(hour, minute, second, nanosecond)
}),
)
}
@@ -120,58 +115,42 @@ impl Arbitrary for Time {
impl Arbitrary for PrimitiveDateTime {
fn arbitrary(g: &mut Gen) -> Self {
- Self(<_>::arbitrary(g))
+ Self::new(<_>::arbitrary(g), <_>::arbitrary(g))
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
- Box::new(self.0.shrink().map(Self))
+ Box::new(
+ (self.date(), self.time())
+ .shrink()
+ .map(|(date, time)| Self::new(date, time)),
+ )
}
}
impl Arbitrary for UtcOffset {
fn arbitrary(g: &mut Gen) -> Self {
- let seconds =
- arbitrary_between!(i32; g, -(Second.per(Day) as i32 - 1), Second.per(Day) as i32 - 1);
- Self::__from_hms_unchecked(
- (seconds / Second.per(Hour) as i32) as _,
- ((seconds % Second.per(Hour) as i32) / Minute.per(Hour) as i32) as _,
- (seconds % Second.per(Minute) as i32) as _,
- )
+ Self::from_hms_ranged(<_>::arbitrary(g), <_>::arbitrary(g), <_>::arbitrary(g))
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
- self.as_hms().shrink().map(|(hours, minutes, seconds)| {
- Self::__from_hms_unchecked(hours, minutes, seconds)
- }),
+ self.as_hms_ranged()
+ .shrink()
+ .map(|(hours, minutes, seconds)| Self::from_hms_ranged(hours, minutes, seconds)),
)
}
}
impl Arbitrary for OffsetDateTime {
fn arbitrary(g: &mut Gen) -> Self {
- Self(<_>::arbitrary(g))
- }
-
- fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
- Box::new(self.0.shrink().map(Self))
- }
-}
-
-impl<O: MaybeOffset + 'static> Arbitrary for DateTime<O> {
- fn arbitrary(g: &mut Gen) -> Self {
- Self {
- date: <_>::arbitrary(g),
- time: <_>::arbitrary(g),
- offset: <_>::arbitrary(g),
- }
+ Self::new_in_offset(<_>::arbitrary(g), <_>::arbitrary(g), <_>::arbitrary(g))
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
- (self.date, self.time, self.offset)
+ (self.date(), self.time(), self.offset())
.shrink()
- .map(|(date, time, offset)| Self { date, time, offset }),
+ .map(|(date, time, offset)| Self::new_in_offset(date, time, offset)),
)
}
}
diff --git a/third_party/rust/time/src/rand.rs b/third_party/rust/time/src/rand.rs
index e5181f9bdd..eac6fe66d1 100644
--- a/third_party/rust/time/src/rand.rs
+++ b/third_party/rust/time/src/rand.rs
@@ -3,17 +3,11 @@
use rand::distributions::{Distribution, Standard};
use rand::Rng;
-use crate::convert::*;
use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
impl Distribution<Time> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Time {
- Time::__from_hms_nanos_unchecked(
- rng.gen_range(0..Hour.per(Day)),
- rng.gen_range(0..Minute.per(Hour)),
- rng.gen_range(0..Second.per(Minute)),
- rng.gen_range(0..Nanosecond.per(Second)),
- )
+ Time::from_hms_nanos_ranged(rng.gen(), rng.gen(), rng.gen(), rng.gen())
}
}
@@ -27,12 +21,7 @@ impl Distribution<Date> for Standard {
impl Distribution<UtcOffset> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> UtcOffset {
- let seconds = rng.gen_range(-(Second.per(Day) as i32 - 1)..=(Second.per(Day) as i32 - 1));
- UtcOffset::__from_hms_unchecked(
- (seconds / Second.per(Hour) as i32) as _,
- ((seconds % Second.per(Hour) as i32) / Minute.per(Hour) as i32) as _,
- (seconds % Second.per(Minute) as i32) as _,
- )
+ UtcOffset::from_hms_ranged(rng.gen(), rng.gen(), rng.gen())
}
}
@@ -51,9 +40,7 @@ impl Distribution<OffsetDateTime> for Standard {
impl Distribution<Duration> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Duration {
- Duration::nanoseconds_i128(
- rng.gen_range(Duration::MIN.whole_nanoseconds()..=Duration::MAX.whole_nanoseconds()),
- )
+ Duration::new_ranged(rng.gen(), rng.gen())
}
}
diff --git a/third_party/rust/time/src/serde/mod.rs b/third_party/rust/time/src/serde/mod.rs
index 7e3403fb3b..fadc44306e 100644
--- a/third_party/rust/time/src/serde/mod.rs
+++ b/third_party/rust/time/src/serde/mod.rs
@@ -22,6 +22,8 @@ pub mod rfc3339;
pub mod timestamp;
mod visitor;
+#[cfg(feature = "serde-human-readable")]
+use alloc::string::ToString;
use core::marker::PhantomData;
#[cfg(feature = "serde-human-readable")]
@@ -117,9 +119,9 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
doc = "use ::serde::Deserialize;"
)]
/// use time::serde;
-/// use time::format_description::FormatItem;
+/// use time::format_description::BorrowedFormatItem;
///
-/// const DATE_TIME_FORMAT: &[FormatItem<'_>] = time::macros::format_description!(
+/// const DATE_TIME_FORMAT: &[BorrowedFormatItem<'_>] = time::macros::format_description!(
/// "hour=[hour], minute=[minute]"
/// );
///
@@ -206,18 +208,18 @@ pub use time_macros::serde_format_description as format_description;
use self::visitor::Visitor;
#[cfg(feature = "parsing")]
-use crate::format_description::{modifier, Component, FormatItem};
+use crate::format_description::{modifier, BorrowedFormatItem, Component};
use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
// region: Date
/// The format used when serializing and deserializing a human-readable `Date`.
#[cfg(feature = "parsing")]
-const DATE_FORMAT: &[FormatItem<'_>] = &[
- FormatItem::Component(Component::Year(modifier::Year::default())),
- FormatItem::Literal(b"-"),
- FormatItem::Component(Component::Month(modifier::Month::default())),
- FormatItem::Literal(b"-"),
- FormatItem::Component(Component::Day(modifier::Day::default())),
+const DATE_FORMAT: &[BorrowedFormatItem<'_>] = &[
+ BorrowedFormatItem::Component(Component::Year(modifier::Year::default())),
+ BorrowedFormatItem::Literal(b"-"),
+ BorrowedFormatItem::Component(Component::Month(modifier::Month::default())),
+ BorrowedFormatItem::Literal(b"-"),
+ BorrowedFormatItem::Component(Component::Day(modifier::Day::default())),
];
impl Serialize for Date {
@@ -275,12 +277,12 @@ impl<'a> Deserialize<'a> for Duration {
// region: OffsetDateTime
/// The format used when serializing and deserializing a human-readable `OffsetDateTime`.
#[cfg(feature = "parsing")]
-const OFFSET_DATE_TIME_FORMAT: &[FormatItem<'_>] = &[
- FormatItem::Compound(DATE_FORMAT),
- FormatItem::Literal(b" "),
- FormatItem::Compound(TIME_FORMAT),
- FormatItem::Literal(b" "),
- FormatItem::Compound(UTC_OFFSET_FORMAT),
+const OFFSET_DATE_TIME_FORMAT: &[BorrowedFormatItem<'_>] = &[
+ BorrowedFormatItem::Compound(DATE_FORMAT),
+ BorrowedFormatItem::Literal(b" "),
+ BorrowedFormatItem::Compound(TIME_FORMAT),
+ BorrowedFormatItem::Literal(b" "),
+ BorrowedFormatItem::Compound(UTC_OFFSET_FORMAT),
];
impl Serialize for OffsetDateTime {
@@ -322,10 +324,10 @@ impl<'a> Deserialize<'a> for OffsetDateTime {
// region: PrimitiveDateTime
/// The format used when serializing and deserializing a human-readable `PrimitiveDateTime`.
#[cfg(feature = "parsing")]
-const PRIMITIVE_DATE_TIME_FORMAT: &[FormatItem<'_>] = &[
- FormatItem::Compound(DATE_FORMAT),
- FormatItem::Literal(b" "),
- FormatItem::Compound(TIME_FORMAT),
+const PRIMITIVE_DATE_TIME_FORMAT: &[BorrowedFormatItem<'_>] = &[
+ BorrowedFormatItem::Compound(DATE_FORMAT),
+ BorrowedFormatItem::Literal(b" "),
+ BorrowedFormatItem::Compound(TIME_FORMAT),
];
impl Serialize for PrimitiveDateTime {
@@ -364,14 +366,14 @@ impl<'a> Deserialize<'a> for PrimitiveDateTime {
// region: Time
/// The format used when serializing and deserializing a human-readable `Time`.
#[cfg(feature = "parsing")]
-const TIME_FORMAT: &[FormatItem<'_>] = &[
- FormatItem::Component(Component::Hour(<modifier::Hour>::default())),
- FormatItem::Literal(b":"),
- FormatItem::Component(Component::Minute(<modifier::Minute>::default())),
- FormatItem::Literal(b":"),
- FormatItem::Component(Component::Second(<modifier::Second>::default())),
- FormatItem::Literal(b"."),
- FormatItem::Component(Component::Subsecond(<modifier::Subsecond>::default())),
+const TIME_FORMAT: &[BorrowedFormatItem<'_>] = &[
+ BorrowedFormatItem::Component(Component::Hour(modifier::Hour::default())),
+ BorrowedFormatItem::Literal(b":"),
+ BorrowedFormatItem::Component(Component::Minute(modifier::Minute::default())),
+ BorrowedFormatItem::Literal(b":"),
+ BorrowedFormatItem::Component(Component::Second(modifier::Second::default())),
+ BorrowedFormatItem::Literal(b"."),
+ BorrowedFormatItem::Component(Component::Subsecond(modifier::Subsecond::default())),
];
impl Serialize for Time {
@@ -402,14 +404,20 @@ impl<'a> Deserialize<'a> for Time {
// region: UtcOffset
/// The format used when serializing and deserializing a human-readable `UtcOffset`.
#[cfg(feature = "parsing")]
-const UTC_OFFSET_FORMAT: &[FormatItem<'_>] = &[
- FormatItem::Component(Component::OffsetHour(modifier::OffsetHour::default())),
- FormatItem::Optional(&FormatItem::Compound(&[
- FormatItem::Literal(b":"),
- FormatItem::Component(Component::OffsetMinute(modifier::OffsetMinute::default())),
- FormatItem::Optional(&FormatItem::Compound(&[
- FormatItem::Literal(b":"),
- FormatItem::Component(Component::OffsetSecond(modifier::OffsetSecond::default())),
+const UTC_OFFSET_FORMAT: &[BorrowedFormatItem<'_>] = &[
+ BorrowedFormatItem::Component(Component::OffsetHour({
+ let mut m = modifier::OffsetHour::default();
+ m.sign_is_mandatory = true;
+ m
+ })),
+ BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound(&[
+ BorrowedFormatItem::Literal(b":"),
+ BorrowedFormatItem::Component(Component::OffsetMinute(modifier::OffsetMinute::default())),
+ BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound(&[
+ BorrowedFormatItem::Literal(b":"),
+ BorrowedFormatItem::Component(Component::OffsetSecond(
+ modifier::OffsetSecond::default(),
+ )),
])),
])),
];
@@ -479,7 +487,7 @@ impl Serialize for Month {
return self.to_string().serialize(serializer);
}
- (*self as u8).serialize(serializer)
+ u8::from(*self).serialize(serializer)
}
}
diff --git a/third_party/rust/time/src/serde/timestamp.rs b/third_party/rust/time/src/serde/timestamp.rs
deleted file mode 100644
index d86e6b9336..0000000000
--- a/third_party/rust/time/src/serde/timestamp.rs
+++ /dev/null
@@ -1,60 +0,0 @@
-//! Treat an [`OffsetDateTime`] as a [Unix timestamp] for the purposes of serde.
-//!
-//! Use this module in combination with serde's [`#[with]`][with] attribute.
-//!
-//! When deserializing, the offset is assumed to be UTC.
-//!
-//! [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
-//! [with]: https://serde.rs/field-attrs.html#with
-
-use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
-
-use crate::OffsetDateTime;
-
-/// Serialize an `OffsetDateTime` as its Unix timestamp
-pub fn serialize<S: Serializer>(
- datetime: &OffsetDateTime,
- serializer: S,
-) -> Result<S::Ok, S::Error> {
- datetime.unix_timestamp().serialize(serializer)
-}
-
-/// Deserialize an `OffsetDateTime` from its Unix timestamp
-pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result<OffsetDateTime, D::Error> {
- OffsetDateTime::from_unix_timestamp(<_>::deserialize(deserializer)?)
- .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err))
-}
-
-/// Treat an `Option<OffsetDateTime>` as a [Unix timestamp] for the purposes of
-/// serde.
-///
-/// Use this module in combination with serde's [`#[with]`][with] attribute.
-///
-/// When deserializing, the offset is assumed to be UTC.
-///
-/// [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
-/// [with]: https://serde.rs/field-attrs.html#with
-pub mod option {
- #[allow(clippy::wildcard_imports)]
- use super::*;
-
- /// Serialize an `Option<OffsetDateTime>` as its Unix timestamp
- pub fn serialize<S: Serializer>(
- option: &Option<OffsetDateTime>,
- serializer: S,
- ) -> Result<S::Ok, S::Error> {
- option
- .map(OffsetDateTime::unix_timestamp)
- .serialize(serializer)
- }
-
- /// Deserialize an `Option<OffsetDateTime>` from its Unix timestamp
- pub fn deserialize<'a, D: Deserializer<'a>>(
- deserializer: D,
- ) -> Result<Option<OffsetDateTime>, D::Error> {
- Option::deserialize(deserializer)?
- .map(OffsetDateTime::from_unix_timestamp)
- .transpose()
- .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err))
- }
-}
diff --git a/third_party/rust/time/src/serde/visitor.rs b/third_party/rust/time/src/serde/visitor.rs
index fcf718bb45..3a4311ecb2 100644
--- a/third_party/rust/time/src/serde/visitor.rs
+++ b/third_party/rust/time/src/serde/visitor.rs
@@ -181,7 +181,7 @@ impl<'a> de::Visitor<'a> for Visitor<UtcOffset> {
}
}
-impl<'a> de::Visitor<'a> for Visitor<Weekday> {
+impl de::Visitor<'_> for Visitor<Weekday> {
type Value = Weekday;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -218,7 +218,7 @@ impl<'a> de::Visitor<'a> for Visitor<Weekday> {
}
}
-impl<'a> de::Visitor<'a> for Visitor<Month> {
+impl de::Visitor<'_> for Visitor<Month> {
type Value = Month;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -269,7 +269,7 @@ impl<'a> de::Visitor<'a> for Visitor<Month> {
macro_rules! well_known {
($article:literal, $name:literal, $($ty:tt)+) => {
#[cfg(feature = "parsing")]
- impl<'a> de::Visitor<'a> for Visitor<$($ty)+> {
+ impl de::Visitor<'_> for Visitor<$($ty)+> {
type Value = OffsetDateTime;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
diff --git a/third_party/rust/time/src/sys/local_offset_at/unix.rs b/third_party/rust/time/src/sys/local_offset_at/unix.rs
index f4a8089328..eddd8e2a95 100644
--- a/third_party/rust/time/src/sys/local_offset_at/unix.rs
+++ b/third_party/rust/time/src/sys/local_offset_at/unix.rs
@@ -15,9 +15,6 @@ const OS_HAS_THREAD_SAFE_ENVIRONMENT: bool = match std::env::consts::OS.as_bytes
// https://github.com/NetBSD/src/blob/f45028636a44111bc4af44d460924958a4460844/lib/libc/stdlib/getenv.c
// https://github.com/NetBSD/src/blob/f45028636a44111bc4af44d460924958a4460844/lib/libc/stdlib/setenv.c
| b"netbsd"
- // https://github.com/apple-oss-distributions/Libc/blob/d526593760f0f79dfaeb8b96c3c8a42c791156ff/stdlib/FreeBSD/getenv.c
- // https://github.com/apple-oss-distributions/Libc/blob/d526593760f0f79dfaeb8b96c3c8a42c791156ff/stdlib/FreeBSD/setenv.c
- | b"macos"
=> true,
_ => false,
};
diff --git a/third_party/rust/time/src/sys/local_offset_at/wasm_js.rs b/third_party/rust/time/src/sys/local_offset_at/wasm_js.rs
index dfbe063a2a..a985e7c484 100644
--- a/third_party/rust/time/src/sys/local_offset_at/wasm_js.rs
+++ b/third_party/rust/time/src/sys/local_offset_at/wasm_js.rs
@@ -1,3 +1,5 @@
+use num_conv::prelude::*;
+
use crate::convert::*;
use crate::{OffsetDateTime, UtcOffset};
@@ -7,7 +9,8 @@ pub(super) fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> {
// The number of minutes returned by getTimezoneOffset() is positive if the local time zone
// is behind UTC, and negative if the local time zone is ahead of UTC. For example,
// for UTC+10, -600 will be returned.
- let timezone_offset = (js_date.get_timezone_offset() as i32) * -(Minute.per(Hour) as i32);
+ let timezone_offset =
+ (js_date.get_timezone_offset() as i32) * -Minute::per(Hour).cast_signed().extend::<i32>();
UtcOffset::from_whole_seconds(timezone_offset).ok()
}
diff --git a/third_party/rust/time/src/sys/local_offset_at/windows.rs b/third_party/rust/time/src/sys/local_offset_at/windows.rs
index a4d5882d67..d144292092 100644
--- a/third_party/rust/time/src/sys/local_offset_at/windows.rs
+++ b/third_party/rust/time/src/sys/local_offset_at/windows.rs
@@ -2,6 +2,8 @@
use core::mem::MaybeUninit;
+use num_conv::prelude::*;
+
use crate::convert::*;
use crate::{OffsetDateTime, UtcOffset};
@@ -57,21 +59,22 @@ fn systemtime_to_filetime(systime: &SystemTime) -> Option<FileTime> {
/// Convert a `FILETIME` to an `i64`, representing a number of seconds.
fn filetime_to_secs(filetime: &FileTime) -> i64 {
/// FILETIME represents 100-nanosecond intervals
- const FT_TO_SECS: i64 = Nanosecond.per(Second) as i64 / 100;
- ((filetime.dwHighDateTime as i64) << 32 | filetime.dwLowDateTime as i64) / FT_TO_SECS
+ const FT_TO_SECS: u64 = Nanosecond::per(Second) as u64 / 100;
+ ((filetime.dwHighDateTime.extend::<u64>() << 32 | filetime.dwLowDateTime.extend::<u64>())
+ / FT_TO_SECS) as i64
}
/// Convert an [`OffsetDateTime`] to a `SYSTEMTIME`.
fn offset_to_systemtime(datetime: OffsetDateTime) -> SystemTime {
let (_, month, day_of_month) = datetime.to_offset(UtcOffset::UTC).date().to_calendar_date();
SystemTime {
- wYear: datetime.year() as _,
- wMonth: month as _,
- wDay: day_of_month as _,
+ wYear: datetime.year().cast_unsigned().truncate(),
+ wMonth: u8::from(month).extend(),
+ wDay: day_of_month.extend(),
wDayOfWeek: 0, // ignored
- wHour: datetime.hour() as _,
- wMinute: datetime.minute() as _,
- wSecond: datetime.second() as _,
+ wHour: datetime.hour().extend(),
+ wMinute: datetime.minute().extend(),
+ wSecond: datetime.second().extend(),
wMilliseconds: datetime.millisecond(),
}
}
diff --git a/third_party/rust/time/src/tests.rs b/third_party/rust/time/src/tests.rs
index 030c473bed..4ccd87b180 100644
--- a/third_party/rust/time/src/tests.rs
+++ b/third_party/rust/time/src/tests.rs
@@ -25,9 +25,10 @@
//! This module should only be used when it is not possible to test the implementation in a
//! reasonable manner externally.
+use std::format;
use std::num::NonZeroU8;
-use crate::formatting::DigitCount;
+use crate::ext::DigitCount;
use crate::parsing::combinator::rfc::iso8601;
use crate::parsing::shim::Integer;
use crate::{duration, parsing};
@@ -71,14 +72,6 @@ fn digit_count() {
assert_eq!(1_000_000_000_u32.num_digits(), 10);
}
-#[test]
-fn default() {
- assert_eq!(
- duration::Padding::Optimize.clone(),
- duration::Padding::default()
- );
-}
-
#[test]
fn debug() {
let _ = format!("{:?}", duration::Padding::Optimize);
diff --git a/third_party/rust/time/src/time.rs b/third_party/rust/time/src/time.rs
index 87b465bb9a..2bd2fefe8e 100644
--- a/third_party/rust/time/src/time.rs
+++ b/third_party/rust/time/src/time.rs
@@ -1,14 +1,22 @@
//! The [`Time`] struct and its associated `impl`s.
+#[cfg(feature = "formatting")]
+use alloc::string::String;
use core::fmt;
use core::ops::{Add, Sub};
use core::time::Duration as StdDuration;
#[cfg(feature = "formatting")]
use std::io;
+use deranged::{RangedU32, RangedU8};
+use num_conv::prelude::*;
+use powerfmt::ext::FormatterExt;
+use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay};
+
use crate::convert::*;
#[cfg(feature = "formatting")]
use crate::formatting::Formattable;
+use crate::internal_macros::{cascade, ensure_ranged, impl_add_assign, impl_sub_assign};
#[cfg(feature = "parsing")]
use crate::parsing::Parsable;
use crate::util::DateAdjustment;
@@ -23,27 +31,118 @@ pub(crate) enum Padding {
Optimize,
}
+/// The type of the `hour` field of `Time`.
+type Hours = RangedU8<0, { Hour::per(Day) - 1 }>;
+/// The type of the `minute` field of `Time`.
+type Minutes = RangedU8<0, { Minute::per(Hour) - 1 }>;
+/// The type of the `second` field of `Time`.
+type Seconds = RangedU8<0, { Second::per(Minute) - 1 }>;
+/// The type of the `nanosecond` field of `Time`.
+type Nanoseconds = RangedU32<0, { Nanosecond::per(Second) - 1 }>;
+
/// The clock time within a given date. Nanosecond precision.
///
/// All minutes are assumed to have exactly 60 seconds; no attempt is made to handle leap seconds
/// (either positive or negative).
///
/// When comparing two `Time`s, they are assumed to be in the same calendar date.
-#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
+#[derive(Clone, Copy, Eq)]
+#[repr(C)]
pub struct Time {
+ // The order of this struct's fields matter!
+ // Do not change them.
+
+ // Little endian version
+ #[cfg(target_endian = "little")]
+ #[allow(clippy::missing_docs_in_private_items)]
+ nanosecond: Nanoseconds,
+ #[cfg(target_endian = "little")]
#[allow(clippy::missing_docs_in_private_items)]
- hour: u8,
+ second: Seconds,
+ #[cfg(target_endian = "little")]
#[allow(clippy::missing_docs_in_private_items)]
- minute: u8,
+ minute: Minutes,
+ #[cfg(target_endian = "little")]
#[allow(clippy::missing_docs_in_private_items)]
- second: u8,
+ hour: Hours,
+ #[cfg(target_endian = "little")]
#[allow(clippy::missing_docs_in_private_items)]
- nanosecond: u32,
+ padding: Padding,
+
+ // Big endian version
+ #[cfg(target_endian = "big")]
#[allow(clippy::missing_docs_in_private_items)]
padding: Padding,
+ #[cfg(target_endian = "big")]
+ #[allow(clippy::missing_docs_in_private_items)]
+ hour: Hours,
+ #[cfg(target_endian = "big")]
+ #[allow(clippy::missing_docs_in_private_items)]
+ minute: Minutes,
+ #[cfg(target_endian = "big")]
+ #[allow(clippy::missing_docs_in_private_items)]
+ second: Seconds,
+ #[cfg(target_endian = "big")]
+ #[allow(clippy::missing_docs_in_private_items)]
+ nanosecond: Nanoseconds,
+}
+
+impl core::hash::Hash for Time {
+ fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
+ self.as_u64().hash(state)
+ }
+}
+
+impl PartialEq for Time {
+ fn eq(&self, other: &Self) -> bool {
+ self.as_u64().eq(&other.as_u64())
+ }
+}
+
+impl PartialOrd for Time {
+ fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for Time {
+ fn cmp(&self, other: &Self) -> core::cmp::Ordering {
+ self.as_u64().cmp(&other.as_u64())
+ }
}
impl Time {
+ /// Provides an u64 based representation **of the correct endianness**
+ ///
+ /// This representation can be used to do comparisons equality testing or hashing.
+ const fn as_u64(self) -> u64 {
+ let nano_bytes = self.nanosecond.get().to_ne_bytes();
+
+ #[cfg(target_endian = "big")]
+ return u64::from_be_bytes([
+ self.padding as u8,
+ self.hour.get(),
+ self.minute.get(),
+ self.second.get(),
+ nano_bytes[0],
+ nano_bytes[1],
+ nano_bytes[2],
+ nano_bytes[3],
+ ]);
+
+ #[cfg(target_endian = "little")]
+ return u64::from_le_bytes([
+ nano_bytes[0],
+ nano_bytes[1],
+ nano_bytes[2],
+ nano_bytes[3],
+ self.second.get(),
+ self.minute.get(),
+ self.hour.get(),
+ self.padding as u8,
+ ]);
+ }
+
/// Create a `Time` that is exactly midnight.
///
/// ```rust
@@ -51,38 +150,44 @@ impl Time {
/// # use time_macros::time;
/// assert_eq!(Time::MIDNIGHT, time!(0:00));
/// ```
- pub const MIDNIGHT: Self = Self::__from_hms_nanos_unchecked(0, 0, 0, 0);
+ pub const MIDNIGHT: Self = Self::MIN;
/// The smallest value that can be represented by `Time`.
///
/// `00:00:00.0`
- pub(crate) const MIN: Self = Self::__from_hms_nanos_unchecked(0, 0, 0, 0);
+ pub(crate) const MIN: Self =
+ Self::from_hms_nanos_ranged(Hours::MIN, Minutes::MIN, Seconds::MIN, Nanoseconds::MIN);
/// The largest value that can be represented by `Time`.
///
/// `23:59:59.999_999_999`
- pub(crate) const MAX: Self = Self::__from_hms_nanos_unchecked(23, 59, 59, 999_999_999);
+ pub(crate) const MAX: Self =
+ Self::from_hms_nanos_ranged(Hours::MAX, Minutes::MAX, Seconds::MAX, Nanoseconds::MAX);
// region: constructors
/// Create a `Time` from its components.
+ ///
+ /// # Safety
+ ///
+ /// - `hours` must be in the range `0..=23`.
+ /// - `minutes` must be in the range `0..=59`.
+ /// - `seconds` must be in the range `0..=59`.
+ /// - `nanoseconds` must be in the range `0..=999_999_999`.
#[doc(hidden)]
- pub const fn __from_hms_nanos_unchecked(
+ pub const unsafe fn __from_hms_nanos_unchecked(
hour: u8,
minute: u8,
second: u8,
nanosecond: u32,
) -> Self {
- debug_assert!(hour < Hour.per(Day));
- debug_assert!(minute < Minute.per(Hour));
- debug_assert!(second < Second.per(Minute));
- debug_assert!(nanosecond < Nanosecond.per(Second));
-
- Self {
- hour,
- minute,
- second,
- nanosecond,
- padding: Padding::Optimize,
+ // Safety: The caller must uphold the safety invariants.
+ unsafe {
+ Self::from_hms_nanos_ranged(
+ Hours::new_unchecked(hour),
+ Minutes::new_unchecked(minute),
+ Seconds::new_unchecked(second),
+ Nanoseconds::new_unchecked(nanosecond),
+ )
}
}
@@ -100,10 +205,28 @@ impl Time {
/// assert!(Time::from_hms(0, 0, 60).is_err()); // 60 isn't a valid second.
/// ```
pub const fn from_hms(hour: u8, minute: u8, second: u8) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(hour in 0 => Hour.per(Day) - 1);
- ensure_value_in_range!(minute in 0 => Minute.per(Hour) - 1);
- ensure_value_in_range!(second in 0 => Second.per(Minute) - 1);
- Ok(Self::__from_hms_nanos_unchecked(hour, minute, second, 0))
+ Ok(Self::from_hms_nanos_ranged(
+ ensure_ranged!(Hours: hour),
+ ensure_ranged!(Minutes: minute),
+ ensure_ranged!(Seconds: second),
+ Nanoseconds::MIN,
+ ))
+ }
+
+ /// Create a `Time` from the hour, minute, second, and nanosecond.
+ pub(crate) const fn from_hms_nanos_ranged(
+ hour: Hours,
+ minute: Minutes,
+ second: Seconds,
+ nanosecond: Nanoseconds,
+ ) -> Self {
+ Self {
+ hour,
+ minute,
+ second,
+ nanosecond,
+ padding: Padding::Optimize,
+ }
}
/// Attempt to create a `Time` from the hour, minute, second, and millisecond.
@@ -126,15 +249,11 @@ impl Time {
second: u8,
millisecond: u16,
) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(hour in 0 => Hour.per(Day) - 1);
- ensure_value_in_range!(minute in 0 => Minute.per(Hour) - 1);
- ensure_value_in_range!(second in 0 => Second.per(Minute) - 1);
- ensure_value_in_range!(millisecond in 0 => Millisecond.per(Second) - 1);
- Ok(Self::__from_hms_nanos_unchecked(
- hour,
- minute,
- second,
- millisecond as u32 * Nanosecond.per(Millisecond),
+ Ok(Self::from_hms_nanos_ranged(
+ ensure_ranged!(Hours: hour),
+ ensure_ranged!(Minutes: minute),
+ ensure_ranged!(Seconds: second),
+ ensure_ranged!(Nanoseconds: millisecond as u32 * Nanosecond::per(Millisecond)),
))
}
@@ -158,15 +277,11 @@ impl Time {
second: u8,
microsecond: u32,
) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(hour in 0 => Hour.per(Day) - 1);
- ensure_value_in_range!(minute in 0 => Minute.per(Hour) - 1);
- ensure_value_in_range!(second in 0 => Second.per(Minute) - 1);
- ensure_value_in_range!(microsecond in 0 => Microsecond.per(Second) - 1);
- Ok(Self::__from_hms_nanos_unchecked(
- hour,
- minute,
- second,
- microsecond * Nanosecond.per(Microsecond) as u32,
+ Ok(Self::from_hms_nanos_ranged(
+ ensure_ranged!(Hours: hour),
+ ensure_ranged!(Minutes: minute),
+ ensure_ranged!(Seconds: second),
+ ensure_ranged!(Nanoseconds: microsecond * Nanosecond::per(Microsecond) as u32),
))
}
@@ -190,12 +305,11 @@ impl Time {
second: u8,
nanosecond: u32,
) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(hour in 0 => Hour.per(Day) - 1);
- ensure_value_in_range!(minute in 0 => Minute.per(Hour) - 1);
- ensure_value_in_range!(second in 0 => Second.per(Minute) - 1);
- ensure_value_in_range!(nanosecond in 0 => Nanosecond.per(Second) - 1);
- Ok(Self::__from_hms_nanos_unchecked(
- hour, minute, second, nanosecond,
+ Ok(Self::from_hms_nanos_ranged(
+ ensure_ranged!(Hours: hour),
+ ensure_ranged!(Minutes: minute),
+ ensure_ranged!(Seconds: second),
+ ensure_ranged!(Nanoseconds: nanosecond),
))
}
// endregion constructors
@@ -209,7 +323,7 @@ impl Time {
/// assert_eq!(time!(23:59:59).as_hms(), (23, 59, 59));
/// ```
pub const fn as_hms(self) -> (u8, u8, u8) {
- (self.hour, self.minute, self.second)
+ (self.hour.get(), self.minute.get(), self.second.get())
}
/// Get the clock hour, minute, second, and millisecond.
@@ -221,10 +335,10 @@ impl Time {
/// ```
pub const fn as_hms_milli(self) -> (u8, u8, u8, u16) {
(
- self.hour,
- self.minute,
- self.second,
- (self.nanosecond / Nanosecond.per(Millisecond)) as u16,
+ self.hour.get(),
+ self.minute.get(),
+ self.second.get(),
+ (self.nanosecond.get() / Nanosecond::per(Millisecond)) as u16,
)
}
@@ -240,10 +354,10 @@ impl Time {
/// ```
pub const fn as_hms_micro(self) -> (u8, u8, u8, u32) {
(
- self.hour,
- self.minute,
- self.second,
- self.nanosecond / Nanosecond.per(Microsecond) as u32,
+ self.hour.get(),
+ self.minute.get(),
+ self.second.get(),
+ self.nanosecond.get() / Nanosecond::per(Microsecond) as u32,
)
}
@@ -258,6 +372,17 @@ impl Time {
/// );
/// ```
pub const fn as_hms_nano(self) -> (u8, u8, u8, u32) {
+ (
+ self.hour.get(),
+ self.minute.get(),
+ self.second.get(),
+ self.nanosecond.get(),
+ )
+ }
+
+ /// Get the clock hour, minute, second, and nanosecond.
+ #[cfg(feature = "quickcheck")]
+ pub(crate) const fn as_hms_nano_ranged(self) -> (Hours, Minutes, Seconds, Nanoseconds) {
(self.hour, self.minute, self.second, self.nanosecond)
}
@@ -271,7 +396,7 @@ impl Time {
/// assert_eq!(time!(23:59:59).hour(), 23);
/// ```
pub const fn hour(self) -> u8 {
- self.hour
+ self.hour.get()
}
/// Get the minute within the hour.
@@ -284,7 +409,7 @@ impl Time {
/// assert_eq!(time!(23:59:59).minute(), 59);
/// ```
pub const fn minute(self) -> u8 {
- self.minute
+ self.minute.get()
}
/// Get the second within the minute.
@@ -297,7 +422,7 @@ impl Time {
/// assert_eq!(time!(23:59:59).second(), 59);
/// ```
pub const fn second(self) -> u8 {
- self.second
+ self.second.get()
}
/// Get the milliseconds within the second.
@@ -310,7 +435,7 @@ impl Time {
/// assert_eq!(time!(23:59:59.999).millisecond(), 999);
/// ```
pub const fn millisecond(self) -> u16 {
- (self.nanosecond / Nanosecond.per(Millisecond)) as _
+ (self.nanosecond.get() / Nanosecond::per(Millisecond)) as _
}
/// Get the microseconds within the second.
@@ -323,7 +448,7 @@ impl Time {
/// assert_eq!(time!(23:59:59.999_999).microsecond(), 999_999);
/// ```
pub const fn microsecond(self) -> u32 {
- self.nanosecond / Nanosecond.per(Microsecond) as u32
+ self.nanosecond.get() / Nanosecond::per(Microsecond) as u32
}
/// Get the nanoseconds within the second.
@@ -336,7 +461,7 @@ impl Time {
/// assert_eq!(time!(23:59:59.999_999_999).nanosecond(), 999_999_999);
/// ```
pub const fn nanosecond(self) -> u32 {
- self.nanosecond
+ self.nanosecond.get()
}
// endregion getters
@@ -344,116 +469,135 @@ impl Time {
/// Add the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow, returning whether
/// the date is different.
pub(crate) const fn adjusting_add(self, duration: Duration) -> (DateAdjustment, Self) {
- let mut nanoseconds = self.nanosecond as i32 + duration.subsec_nanoseconds();
+ let mut nanoseconds = self.nanosecond.get() as i32 + duration.subsec_nanoseconds();
let mut seconds =
- self.second as i8 + (duration.whole_seconds() % Second.per(Minute) as i64) as i8;
+ self.second.get() as i8 + (duration.whole_seconds() % Second::per(Minute) as i64) as i8;
let mut minutes =
- self.minute as i8 + (duration.whole_minutes() % Minute.per(Hour) as i64) as i8;
- let mut hours = self.hour as i8 + (duration.whole_hours() % Hour.per(Day) as i64) as i8;
+ self.minute.get() as i8 + (duration.whole_minutes() % Minute::per(Hour) as i64) as i8;
+ let mut hours =
+ self.hour.get() as i8 + (duration.whole_hours() % Hour::per(Day) as i64) as i8;
let mut date_adjustment = DateAdjustment::None;
- cascade!(nanoseconds in 0..Nanosecond.per(Second) as _ => seconds);
- cascade!(seconds in 0..Second.per(Minute) as _ => minutes);
- cascade!(minutes in 0..Minute.per(Hour) as _ => hours);
- if hours >= Hour.per(Day) as _ {
- hours -= Hour.per(Day) as i8;
+ cascade!(nanoseconds in 0..Nanosecond::per(Second) as _ => seconds);
+ cascade!(seconds in 0..Second::per(Minute) as _ => minutes);
+ cascade!(minutes in 0..Minute::per(Hour) as _ => hours);
+ if hours >= Hour::per(Day) as _ {
+ hours -= Hour::per(Day) as i8;
date_adjustment = DateAdjustment::Next;
} else if hours < 0 {
- hours += Hour.per(Day) as i8;
+ hours += Hour::per(Day) as i8;
date_adjustment = DateAdjustment::Previous;
}
(
date_adjustment,
- Self::__from_hms_nanos_unchecked(
- hours as _,
- minutes as _,
- seconds as _,
- nanoseconds as _,
- ),
+ // Safety: The cascades above ensure the values are in range.
+ unsafe {
+ Self::__from_hms_nanos_unchecked(
+ hours as _,
+ minutes as _,
+ seconds as _,
+ nanoseconds as _,
+ )
+ },
)
}
/// Subtract the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow, returning
/// whether the date is different.
pub(crate) const fn adjusting_sub(self, duration: Duration) -> (DateAdjustment, Self) {
- let mut nanoseconds = self.nanosecond as i32 - duration.subsec_nanoseconds();
+ let mut nanoseconds = self.nanosecond.get() as i32 - duration.subsec_nanoseconds();
let mut seconds =
- self.second as i8 - (duration.whole_seconds() % Second.per(Minute) as i64) as i8;
+ self.second.get() as i8 - (duration.whole_seconds() % Second::per(Minute) as i64) as i8;
let mut minutes =
- self.minute as i8 - (duration.whole_minutes() % Minute.per(Hour) as i64) as i8;
- let mut hours = self.hour as i8 - (duration.whole_hours() % Hour.per(Day) as i64) as i8;
+ self.minute.get() as i8 - (duration.whole_minutes() % Minute::per(Hour) as i64) as i8;
+ let mut hours =
+ self.hour.get() as i8 - (duration.whole_hours() % Hour::per(Day) as i64) as i8;
let mut date_adjustment = DateAdjustment::None;
- cascade!(nanoseconds in 0..Nanosecond.per(Second) as _ => seconds);
- cascade!(seconds in 0..Second.per(Minute) as _ => minutes);
- cascade!(minutes in 0..Minute.per(Hour) as _ => hours);
- if hours >= Hour.per(Day) as _ {
- hours -= Hour.per(Day) as i8;
+ cascade!(nanoseconds in 0..Nanosecond::per(Second) as _ => seconds);
+ cascade!(seconds in 0..Second::per(Minute) as _ => minutes);
+ cascade!(minutes in 0..Minute::per(Hour) as _ => hours);
+ if hours >= Hour::per(Day) as _ {
+ hours -= Hour::per(Day) as i8;
date_adjustment = DateAdjustment::Next;
} else if hours < 0 {
- hours += Hour.per(Day) as i8;
+ hours += Hour::per(Day) as i8;
date_adjustment = DateAdjustment::Previous;
}
(
date_adjustment,
- Self::__from_hms_nanos_unchecked(
- hours as _,
- minutes as _,
- seconds as _,
- nanoseconds as _,
- ),
+ // Safety: The cascades above ensure the values are in range.
+ unsafe {
+ Self::__from_hms_nanos_unchecked(
+ hours as _,
+ minutes as _,
+ seconds as _,
+ nanoseconds as _,
+ )
+ },
)
}
/// Add the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow,
/// returning whether the date is the previous date as the first element of the tuple.
pub(crate) const fn adjusting_add_std(self, duration: StdDuration) -> (bool, Self) {
- let mut nanosecond = self.nanosecond + duration.subsec_nanos();
- let mut second = self.second + (duration.as_secs() % Second.per(Minute) as u64) as u8;
- let mut minute = self.minute
- + ((duration.as_secs() / Second.per(Minute) as u64) % Minute.per(Hour) as u64) as u8;
- let mut hour = self.hour
- + ((duration.as_secs() / Second.per(Hour) as u64) % Hour.per(Day) as u64) as u8;
+ let mut nanosecond = self.nanosecond.get() + duration.subsec_nanos();
+ let mut second =
+ self.second.get() + (duration.as_secs() % Second::per(Minute) as u64) as u8;
+ let mut minute = self.minute.get()
+ + ((duration.as_secs() / Second::per(Minute) as u64) % Minute::per(Hour) as u64) as u8;
+ let mut hour = self.hour.get()
+ + ((duration.as_secs() / Second::per(Hour) as u64) % Hour::per(Day) as u64) as u8;
let mut is_next_day = false;
- cascade!(nanosecond in 0..Nanosecond.per(Second) => second);
- cascade!(second in 0..Second.per(Minute) => minute);
- cascade!(minute in 0..Minute.per(Hour) => hour);
- if hour >= Hour.per(Day) {
- hour -= Hour.per(Day);
+ cascade!(nanosecond in 0..Nanosecond::per(Second) => second);
+ cascade!(second in 0..Second::per(Minute) => minute);
+ cascade!(minute in 0..Minute::per(Hour) => hour);
+ if hour >= Hour::per(Day) {
+ hour -= Hour::per(Day);
is_next_day = true;
}
(
is_next_day,
- Self::__from_hms_nanos_unchecked(hour, minute, second, nanosecond),
+ // Safety: The cascades above ensure the values are in range.
+ unsafe { Self::__from_hms_nanos_unchecked(hour, minute, second, nanosecond) },
)
}
/// Subtract the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow,
/// returning whether the date is the previous date as the first element of the tuple.
pub(crate) const fn adjusting_sub_std(self, duration: StdDuration) -> (bool, Self) {
- let mut nanosecond = self.nanosecond as i32 - duration.subsec_nanos() as i32;
- let mut second = self.second as i8 - (duration.as_secs() % Second.per(Minute) as u64) as i8;
- let mut minute = self.minute as i8
- - ((duration.as_secs() / Second.per(Minute) as u64) % Minute.per(Hour) as u64) as i8;
- let mut hour = self.hour as i8
- - ((duration.as_secs() / Second.per(Hour) as u64) % Hour.per(Day) as u64) as i8;
+ let mut nanosecond = self.nanosecond.get() as i32 - duration.subsec_nanos() as i32;
+ let mut second =
+ self.second.get() as i8 - (duration.as_secs() % Second::per(Minute) as u64) as i8;
+ let mut minute = self.minute.get() as i8
+ - ((duration.as_secs() / Second::per(Minute) as u64) % Minute::per(Hour) as u64) as i8;
+ let mut hour = self.hour.get() as i8
+ - ((duration.as_secs() / Second::per(Hour) as u64) % Hour::per(Day) as u64) as i8;
let mut is_previous_day = false;
- cascade!(nanosecond in 0..Nanosecond.per(Second) as _ => second);
- cascade!(second in 0..Second.per(Minute) as _ => minute);
- cascade!(minute in 0..Minute.per(Hour) as _ => hour);
+ cascade!(nanosecond in 0..Nanosecond::per(Second) as _ => second);
+ cascade!(second in 0..Second::per(Minute) as _ => minute);
+ cascade!(minute in 0..Minute::per(Hour) as _ => hour);
if hour < 0 {
- hour += Hour.per(Day) as i8;
+ hour += Hour::per(Day) as i8;
is_previous_day = true;
}
(
is_previous_day,
- Self::__from_hms_nanos_unchecked(hour as _, minute as _, second as _, nanosecond as _),
+ // Safety: The cascades above ensure the values are in range.
+ unsafe {
+ Self::__from_hms_nanos_unchecked(
+ hour as _,
+ minute as _,
+ second as _,
+ nanosecond as _,
+ )
+ },
)
}
// endregion arithmetic helpers
@@ -470,14 +614,9 @@ impl Time {
/// assert!(time!(01:02:03.004_005_006).replace_hour(24).is_err()); // 24 isn't a valid hour
/// ```
#[must_use = "This method does not mutate the original `Time`."]
- pub const fn replace_hour(self, hour: u8) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(hour in 0 => Hour.per(Day) - 1);
- Ok(Self::__from_hms_nanos_unchecked(
- hour,
- self.minute,
- self.second,
- self.nanosecond,
- ))
+ pub const fn replace_hour(mut self, hour: u8) -> Result<Self, error::ComponentRange> {
+ self.hour = ensure_ranged!(Hours: hour);
+ Ok(self)
}
/// Replace the minutes within the hour.
@@ -491,14 +630,9 @@ impl Time {
/// assert!(time!(01:02:03.004_005_006).replace_minute(60).is_err()); // 60 isn't a valid minute
/// ```
#[must_use = "This method does not mutate the original `Time`."]
- pub const fn replace_minute(self, minute: u8) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(minute in 0 => Minute.per(Hour) - 1);
- Ok(Self::__from_hms_nanos_unchecked(
- self.hour,
- minute,
- self.second,
- self.nanosecond,
- ))
+ pub const fn replace_minute(mut self, minute: u8) -> Result<Self, error::ComponentRange> {
+ self.minute = ensure_ranged!(Minutes: minute);
+ Ok(self)
}
/// Replace the seconds within the minute.
@@ -512,14 +646,9 @@ impl Time {
/// assert!(time!(01:02:03.004_005_006).replace_second(60).is_err()); // 60 isn't a valid second
/// ```
#[must_use = "This method does not mutate the original `Time`."]
- pub const fn replace_second(self, second: u8) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(second in 0 => Second.per(Minute) - 1);
- Ok(Self::__from_hms_nanos_unchecked(
- self.hour,
- self.minute,
- second,
- self.nanosecond,
- ))
+ pub const fn replace_second(mut self, second: u8) -> Result<Self, error::ComponentRange> {
+ self.second = ensure_ranged!(Seconds: second);
+ Ok(self)
}
/// Replace the milliseconds within the second.
@@ -534,16 +663,12 @@ impl Time {
/// ```
#[must_use = "This method does not mutate the original `Time`."]
pub const fn replace_millisecond(
- self,
+ mut self,
millisecond: u16,
) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(millisecond in 0 => Millisecond.per(Second) - 1);
- Ok(Self::__from_hms_nanos_unchecked(
- self.hour,
- self.minute,
- self.second,
- millisecond as u32 * Nanosecond.per(Millisecond),
- ))
+ self.nanosecond =
+ ensure_ranged!(Nanoseconds: millisecond as u32 * Nanosecond::per(Millisecond));
+ Ok(self)
}
/// Replace the microseconds within the second.
@@ -558,16 +683,12 @@ impl Time {
/// ```
#[must_use = "This method does not mutate the original `Time`."]
pub const fn replace_microsecond(
- self,
+ mut self,
microsecond: u32,
) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(microsecond in 0 => Microsecond.per(Second) - 1);
- Ok(Self::__from_hms_nanos_unchecked(
- self.hour,
- self.minute,
- self.second,
- microsecond * Nanosecond.per(Microsecond) as u32,
- ))
+ self.nanosecond =
+ ensure_ranged!(Nanoseconds: microsecond * Nanosecond::per(Microsecond) as u32);
+ Ok(self)
}
/// Replace the nanoseconds within the second.
@@ -581,14 +702,12 @@ impl Time {
/// assert!(time!(01:02:03.004_005_006).replace_nanosecond(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid nanosecond
/// ```
#[must_use = "This method does not mutate the original `Time`."]
- pub const fn replace_nanosecond(self, nanosecond: u32) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(nanosecond in 0 => Nanosecond.per(Second) - 1);
- Ok(Self::__from_hms_nanos_unchecked(
- self.hour,
- self.minute,
- self.second,
- nanosecond,
- ))
+ pub const fn replace_nanosecond(
+ mut self,
+ nanosecond: u32,
+ ) -> Result<Self, error::ComponentRange> {
+ self.nanosecond = ensure_ranged!(Nanoseconds: nanosecond);
+ Ok(self)
}
// endregion replacement
}
@@ -601,7 +720,7 @@ impl Time {
self,
output: &mut impl io::Write,
format: &(impl Formattable + ?Sized),
- ) -> Result<usize, crate::error::Format> {
+ ) -> Result<usize, error::Format> {
format.format_into(output, None, Some(self), None)
}
@@ -614,10 +733,7 @@ impl Time {
/// assert_eq!(time!(12:00).format(&format)?, "12:00:00");
/// # Ok::<_, time::Error>(())
/// ```
- pub fn format(
- self,
- format: &(impl Formattable + ?Sized),
- ) -> Result<String, crate::error::Format> {
+ pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
format.format(None, Some(self), None)
}
}
@@ -642,9 +758,24 @@ impl Time {
}
}
-impl fmt::Display for Time {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let (value, width) = match self.nanosecond() {
+mod private {
+ #[non_exhaustive]
+ #[derive(Debug, Clone, Copy)]
+ pub struct TimeMetadata {
+ /// How many characters wide the formatted subsecond is.
+ pub(super) subsecond_width: u8,
+ /// The value to use when formatting the subsecond. Leading zeroes will be added as
+ /// necessary.
+ pub(super) subsecond_value: u32,
+ }
+}
+use private::TimeMetadata;
+
+impl SmartDisplay for Time {
+ type Metadata = TimeMetadata;
+
+ fn metadata(&self, _: FormatterOptions) -> Metadata<Self> {
+ let (subsecond_value, subsecond_width) = match self.nanosecond() {
nanos if nanos % 10 != 0 => (nanos, 9),
nanos if (nanos / 10) % 10 != 0 => (nanos / 10, 8),
nanos if (nanos / 100) % 10 != 0 => (nanos / 100, 7),
@@ -655,14 +786,50 @@ impl fmt::Display for Time {
nanos if (nanos / 10_000_000) % 10 != 0 => (nanos / 10_000_000, 2),
nanos => (nanos / 100_000_000, 1),
};
- write!(
- f,
- "{}:{:02}:{:02}.{value:0width$}",
- self.hour, self.minute, self.second,
+
+ let formatted_width = smart_display::padded_width_of!(
+ self.hour.get(),
+ ":",
+ self.minute.get() => width(2) fill('0'),
+ ":",
+ self.second.get() => width(2) fill('0'),
+ ".",
+ ) + subsecond_width;
+
+ Metadata::new(
+ formatted_width,
+ self,
+ TimeMetadata {
+ subsecond_width: subsecond_width.truncate(),
+ subsecond_value,
+ },
+ )
+ }
+
+ fn fmt_with_metadata(
+ &self,
+ f: &mut fmt::Formatter<'_>,
+ metadata: Metadata<Self>,
+ ) -> fmt::Result {
+ let subsecond_width = metadata.subsecond_width.extend();
+ let subsecond_value = metadata.subsecond_value;
+
+ f.pad_with_width(
+ metadata.unpadded_width(),
+ format_args!(
+ "{}:{:02}:{:02}.{subsecond_value:0subsecond_width$}",
+ self.hour, self.minute, self.second
+ ),
)
}
}
+impl fmt::Display for Time {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ SmartDisplay::fmt(self, f)
+ }
+}
+
impl fmt::Debug for Time {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
@@ -754,24 +921,32 @@ impl Sub for Time {
/// assert_eq!(time!(0:00) - time!(23:00), (-23).hours());
/// ```
fn sub(self, rhs: Self) -> Self::Output {
- let hour_diff = (self.hour as i8) - (rhs.hour as i8);
- let minute_diff = (self.minute as i8) - (rhs.minute as i8);
- let second_diff = (self.second as i8) - (rhs.second as i8);
- let nanosecond_diff = (self.nanosecond as i32) - (rhs.nanosecond as i32);
+ let hour_diff = self.hour.get().cast_signed() - rhs.hour.get().cast_signed();
+ let minute_diff = self.minute.get().cast_signed() - rhs.minute.get().cast_signed();
+ let second_diff = self.second.get().cast_signed() - rhs.second.get().cast_signed();
+ let nanosecond_diff =
+ self.nanosecond.get().cast_signed() - rhs.nanosecond.get().cast_signed();
- let seconds = hour_diff as i64 * Second.per(Hour) as i64
- + minute_diff as i64 * Second.per(Minute) as i64
- + second_diff as i64;
+ let seconds = hour_diff.extend::<i64>() * Second::per(Hour).cast_signed().extend::<i64>()
+ + minute_diff.extend::<i64>() * Second::per(Minute).cast_signed().extend::<i64>()
+ + second_diff.extend::<i64>();
let (seconds, nanoseconds) = if seconds > 0 && nanosecond_diff < 0 {
- (seconds - 1, nanosecond_diff + Nanosecond.per(Second) as i32)
+ (
+ seconds - 1,
+ nanosecond_diff + Nanosecond::per(Second).cast_signed(),
+ )
} else if seconds < 0 && nanosecond_diff > 0 {
- (seconds + 1, nanosecond_diff - Nanosecond.per(Second) as i32)
+ (
+ seconds + 1,
+ nanosecond_diff - Nanosecond::per(Second).cast_signed(),
+ )
} else {
(seconds, nanosecond_diff)
};
- Duration::new_unchecked(seconds, nanoseconds)
+ // Safety: `nanoseconds` is in range due to the overflow handling.
+ unsafe { Duration::new_unchecked(seconds, nanoseconds) }
}
}
// endregion trait impls
diff --git a/third_party/rust/time/src/utc_offset.rs b/third_party/rust/time/src/utc_offset.rs
index af7fd1bf79..7cf24d141c 100644
--- a/third_party/rust/time/src/utc_offset.rs
+++ b/third_party/rust/time/src/utc_offset.rs
@@ -1,14 +1,21 @@
//! The [`UtcOffset`] struct and its associated `impl`s.
+#[cfg(feature = "formatting")]
+use alloc::string::String;
use core::fmt;
use core::ops::Neg;
#[cfg(feature = "formatting")]
use std::io;
+use deranged::{RangedI32, RangedI8};
+use powerfmt::ext::FormatterExt;
+use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay};
+
use crate::convert::*;
use crate::error;
#[cfg(feature = "formatting")]
use crate::formatting::Formattable;
+use crate::internal_macros::ensure_ranged;
#[cfg(feature = "parsing")]
use crate::parsing::Parsable;
#[cfg(feature = "local-offset")]
@@ -16,19 +23,39 @@ use crate::sys::local_offset_at;
#[cfg(feature = "local-offset")]
use crate::OffsetDateTime;
+/// The type of the `hours` field of `UtcOffset`.
+type Hours = RangedI8<-25, 25>;
+/// The type of the `minutes` field of `UtcOffset`.
+type Minutes = RangedI8<{ -(Minute::per(Hour) as i8 - 1) }, { Minute::per(Hour) as i8 - 1 }>;
+/// The type of the `seconds` field of `UtcOffset`.
+type Seconds = RangedI8<{ -(Second::per(Minute) as i8 - 1) }, { Second::per(Minute) as i8 - 1 }>;
+/// The type capable of storing the range of whole seconds that a `UtcOffset` can encompass.
+type WholeSeconds = RangedI32<
+ {
+ Hours::MIN.get() as i32 * Second::per(Hour) as i32
+ + Minutes::MIN.get() as i32 * Second::per(Minute) as i32
+ + Seconds::MIN.get() as i32
+ },
+ {
+ Hours::MAX.get() as i32 * Second::per(Hour) as i32
+ + Minutes::MAX.get() as i32 * Second::per(Minute) as i32
+ + Seconds::MAX.get() as i32
+ },
+>;
+
/// An offset from UTC.
///
-/// This struct can store values up to ±23:59:59. If you need support outside this range, please
+/// This struct can store values up to ±25:59:59. If you need support outside this range, please
/// file an issue with your use case.
// All three components _must_ have the same sign.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct UtcOffset {
#[allow(clippy::missing_docs_in_private_items)]
- hours: i8,
+ hours: Hours,
#[allow(clippy::missing_docs_in_private_items)]
- minutes: i8,
+ minutes: Minutes,
#[allow(clippy::missing_docs_in_private_items)]
- seconds: i8,
+ seconds: Seconds,
}
impl UtcOffset {
@@ -39,34 +66,30 @@ impl UtcOffset {
/// # use time_macros::offset;
/// assert_eq!(UtcOffset::UTC, offset!(UTC));
/// ```
- pub const UTC: Self = Self::__from_hms_unchecked(0, 0, 0);
+ pub const UTC: Self = Self::from_whole_seconds_ranged(WholeSeconds::new_static::<0>());
// region: constructors
/// Create a `UtcOffset` representing an offset of the hours, minutes, and seconds provided, the
/// validity of which must be guaranteed by the caller. All three parameters must have the same
/// sign.
+ ///
+ /// # Safety
+ ///
+ /// - Hours must be in the range `-25..=25`.
+ /// - Minutes must be in the range `-59..=59`.
+ /// - Seconds must be in the range `-59..=59`.
+ ///
+ /// While the signs of the parameters are required to match to avoid bugs, this is not a safety
+ /// invariant.
#[doc(hidden)]
- pub const fn __from_hms_unchecked(hours: i8, minutes: i8, seconds: i8) -> Self {
- if hours < 0 {
- debug_assert!(minutes <= 0);
- debug_assert!(seconds <= 0);
- } else if hours > 0 {
- debug_assert!(minutes >= 0);
- debug_assert!(seconds >= 0);
- }
- if minutes < 0 {
- debug_assert!(seconds <= 0);
- } else if minutes > 0 {
- debug_assert!(seconds >= 0);
- }
- debug_assert!(hours.unsigned_abs() < 24);
- debug_assert!(minutes.unsigned_abs() < Minute.per(Hour));
- debug_assert!(seconds.unsigned_abs() < Second.per(Minute));
-
- Self {
- hours,
- minutes,
- seconds,
+ pub const unsafe fn __from_hms_unchecked(hours: i8, minutes: i8, seconds: i8) -> Self {
+ // Safety: The caller must uphold the safety invariants.
+ unsafe {
+ Self::from_hms_ranged_unchecked(
+ Hours::new_unchecked(hours),
+ Minutes::new_unchecked(minutes),
+ Seconds::new_unchecked(seconds),
+ )
}
}
@@ -84,29 +107,71 @@ impl UtcOffset {
/// ```
pub const fn from_hms(
hours: i8,
- mut minutes: i8,
- mut seconds: i8,
+ minutes: i8,
+ seconds: i8,
) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(hours in -23 => 23);
- ensure_value_in_range!(
- minutes in -(Minute.per(Hour) as i8 - 1) => Minute.per(Hour) as i8 - 1
- );
- ensure_value_in_range!(
- seconds in -(Second.per(Minute) as i8 - 1) => Second.per(Minute) as i8 - 1
- );
+ Ok(Self::from_hms_ranged(
+ ensure_ranged!(Hours: hours),
+ ensure_ranged!(Minutes: minutes),
+ ensure_ranged!(Seconds: seconds),
+ ))
+ }
+
+ /// Create a `UtcOffset` representing an offset of the hours, minutes, and seconds provided. All
+ /// three parameters must have the same sign.
+ ///
+ /// While the signs of the parameters are required to match, this is not a safety invariant.
+ pub(crate) const fn from_hms_ranged_unchecked(
+ hours: Hours,
+ minutes: Minutes,
+ seconds: Seconds,
+ ) -> Self {
+ if hours.get() < 0 {
+ debug_assert!(minutes.get() <= 0);
+ debug_assert!(seconds.get() <= 0);
+ } else if hours.get() > 0 {
+ debug_assert!(minutes.get() >= 0);
+ debug_assert!(seconds.get() >= 0);
+ }
+ if minutes.get() < 0 {
+ debug_assert!(seconds.get() <= 0);
+ } else if minutes.get() > 0 {
+ debug_assert!(seconds.get() >= 0);
+ }
- if (hours > 0 && minutes < 0) || (hours < 0 && minutes > 0) {
- minutes *= -1;
+ Self {
+ hours,
+ minutes,
+ seconds,
+ }
+ }
+
+ /// Create a `UtcOffset` representing an offset by the number of hours, minutes, and seconds
+ /// provided.
+ ///
+ /// The sign of all three components should match. If they do not, all smaller components will
+ /// have their signs flipped.
+ pub(crate) const fn from_hms_ranged(
+ hours: Hours,
+ mut minutes: Minutes,
+ mut seconds: Seconds,
+ ) -> Self {
+ if (hours.get() > 0 && minutes.get() < 0) || (hours.get() < 0 && minutes.get() > 0) {
+ minutes = minutes.neg();
}
- if (hours > 0 && seconds < 0)
- || (hours < 0 && seconds > 0)
- || (minutes > 0 && seconds < 0)
- || (minutes < 0 && seconds > 0)
+ if (hours.get() > 0 && seconds.get() < 0)
+ || (hours.get() < 0 && seconds.get() > 0)
+ || (minutes.get() > 0 && seconds.get() < 0)
+ || (minutes.get() < 0 && seconds.get() > 0)
{
- seconds *= -1;
+ seconds = seconds.neg();
}
- Ok(Self::__from_hms_unchecked(hours, minutes, seconds))
+ Self {
+ hours,
+ minutes,
+ seconds,
+ }
}
/// Create a `UtcOffset` representing an offset by the number of seconds provided.
@@ -117,16 +182,32 @@ impl UtcOffset {
/// # Ok::<_, time::Error>(())
/// ```
pub const fn from_whole_seconds(seconds: i32) -> Result<Self, error::ComponentRange> {
- ensure_value_in_range!(
- seconds in -24 * Second.per(Hour) as i32 - 1 => 24 * Second.per(Hour) as i32 - 1
- );
-
- Ok(Self::__from_hms_unchecked(
- (seconds / Second.per(Hour) as i32) as _,
- ((seconds % Second.per(Hour) as i32) / Minute.per(Hour) as i32) as _,
- (seconds % Second.per(Minute) as i32) as _,
+ Ok(Self::from_whole_seconds_ranged(
+ ensure_ranged!(WholeSeconds: seconds),
))
}
+
+ /// Create a `UtcOffset` representing an offset by the number of seconds provided.
+ // ignore because the function is crate-private
+ /// ```rust,ignore
+ /// # use time::UtcOffset;
+ /// # use deranged::RangedI32;
+ /// assert_eq!(
+ /// UtcOffset::from_whole_seconds_ranged(RangedI32::new_static::<3_723>()).as_hms(),
+ /// (1, 2, 3)
+ /// );
+ /// # Ok::<_, time::Error>(())
+ /// ```
+ pub(crate) const fn from_whole_seconds_ranged(seconds: WholeSeconds) -> Self {
+ // Safety: The type of `seconds` guarantees that all values are in range.
+ unsafe {
+ Self::__from_hms_unchecked(
+ (seconds.get() / Second::per(Hour) as i32) as _,
+ ((seconds.get() % Second::per(Hour) as i32) / Minute::per(Hour) as i32) as _,
+ (seconds.get() % Second::per(Minute) as i32) as _,
+ )
+ }
+ }
// endregion constructors
// region: getters
@@ -139,6 +220,13 @@ impl UtcOffset {
/// assert_eq!(offset!(-1:02:03).as_hms(), (-1, -2, -3));
/// ```
pub const fn as_hms(self) -> (i8, i8, i8) {
+ (self.hours.get(), self.minutes.get(), self.seconds.get())
+ }
+
+ /// Obtain the UTC offset as its hours, minutes, and seconds. The sign of all three components
+ /// will always match. A positive value indicates an offset to the east; a negative to the west.
+ #[cfg(feature = "quickcheck")]
+ pub(crate) const fn as_hms_ranged(self) -> (Hours, Minutes, Seconds) {
(self.hours, self.minutes, self.seconds)
}
@@ -151,7 +239,7 @@ impl UtcOffset {
/// assert_eq!(offset!(-1:02:03).whole_hours(), -1);
/// ```
pub const fn whole_hours(self) -> i8 {
- self.hours
+ self.hours.get()
}
/// Obtain the number of whole minutes the offset is from UTC. A positive value indicates an
@@ -163,7 +251,7 @@ impl UtcOffset {
/// assert_eq!(offset!(-1:02:03).whole_minutes(), -62);
/// ```
pub const fn whole_minutes(self) -> i16 {
- self.hours as i16 * Minute.per(Hour) as i16 + self.minutes as i16
+ self.hours.get() as i16 * Minute::per(Hour) as i16 + self.minutes.get() as i16
}
/// Obtain the number of minutes past the hour the offset is from UTC. A positive value
@@ -175,7 +263,7 @@ impl UtcOffset {
/// assert_eq!(offset!(-1:02:03).minutes_past_hour(), -2);
/// ```
pub const fn minutes_past_hour(self) -> i8 {
- self.minutes
+ self.minutes.get()
}
/// Obtain the number of whole seconds the offset is from UTC. A positive value indicates an
@@ -189,9 +277,9 @@ impl UtcOffset {
// This may be useful for anyone manually implementing arithmetic, as it
// would let them construct a `Duration` directly.
pub const fn whole_seconds(self) -> i32 {
- self.hours as i32 * Second.per(Hour) as i32
- + self.minutes as i32 * Second.per(Minute) as i32
- + self.seconds as i32
+ self.hours.get() as i32 * Second::per(Hour) as i32
+ + self.minutes.get() as i32 * Second::per(Minute) as i32
+ + self.seconds.get() as i32
}
/// Obtain the number of seconds past the minute the offset is from UTC. A positive value
@@ -203,7 +291,7 @@ impl UtcOffset {
/// assert_eq!(offset!(-1:02:03).seconds_past_minute(), -3);
/// ```
pub const fn seconds_past_minute(self) -> i8 {
- self.seconds
+ self.seconds.get()
}
// endregion getters
@@ -218,7 +306,7 @@ impl UtcOffset {
/// assert!(offset!(UTC).is_utc());
/// ```
pub const fn is_utc(self) -> bool {
- self.hours == 0 && self.minutes == 0 && self.seconds == 0
+ self.hours.get() == 0 && self.minutes.get() == 0 && self.seconds.get() == 0
}
/// Check if the offset is positive, or east of UTC.
@@ -230,7 +318,7 @@ impl UtcOffset {
/// assert!(!offset!(UTC).is_positive());
/// ```
pub const fn is_positive(self) -> bool {
- self.hours > 0 || self.minutes > 0 || self.seconds > 0
+ self.hours.get() > 0 || self.minutes.get() > 0 || self.seconds.get() > 0
}
/// Check if the offset is negative, or west of UTC.
@@ -242,7 +330,7 @@ impl UtcOffset {
/// assert!(!offset!(UTC).is_negative());
/// ```
pub const fn is_negative(self) -> bool {
- self.hours < 0 || self.minutes < 0 || self.seconds < 0
+ self.hours.get() < 0 || self.minutes.get() < 0 || self.seconds.get() < 0
}
// endregion is_{sign}
@@ -326,16 +414,50 @@ impl UtcOffset {
}
}
+mod private {
+ #[non_exhaustive]
+ #[derive(Debug, Clone, Copy)]
+ pub struct UtcOffsetMetadata;
+}
+use private::UtcOffsetMetadata;
+
+impl SmartDisplay for UtcOffset {
+ type Metadata = UtcOffsetMetadata;
+
+ fn metadata(&self, _: FormatterOptions) -> Metadata<Self> {
+ let sign = if self.is_negative() { '-' } else { '+' };
+ let width = smart_display::padded_width_of!(
+ sign,
+ self.hours.abs() => width(2),
+ ":",
+ self.minutes.abs() => width(2),
+ ":",
+ self.seconds.abs() => width(2),
+ );
+ Metadata::new(width, self, UtcOffsetMetadata)
+ }
+
+ fn fmt_with_metadata(
+ &self,
+ f: &mut fmt::Formatter<'_>,
+ metadata: Metadata<Self>,
+ ) -> fmt::Result {
+ f.pad_with_width(
+ metadata.unpadded_width(),
+ format_args!(
+ "{}{:02}:{:02}:{:02}",
+ if self.is_negative() { '-' } else { '+' },
+ self.hours.abs(),
+ self.minutes.abs(),
+ self.seconds.abs(),
+ ),
+ )
+ }
+}
+
impl fmt::Display for UtcOffset {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(
- f,
- "{}{:02}:{:02}:{:02}",
- if self.is_negative() { '-' } else { '+' },
- self.hours.abs(),
- self.minutes.abs(),
- self.seconds.abs()
- )
+ SmartDisplay::fmt(self, f)
}
}
@@ -350,6 +472,6 @@ impl Neg for UtcOffset {
type Output = Self;
fn neg(self) -> Self::Output {
- Self::__from_hms_unchecked(-self.hours, -self.minutes, -self.seconds)
+ Self::from_hms_ranged(self.hours.neg(), self.minutes.neg(), self.seconds.neg())
}
}
diff --git a/third_party/rust/time/src/util.rs b/third_party/rust/time/src/util.rs
index 857f5f20b0..9bb9b0404d 100644
--- a/third_party/rust/time/src/util.rs
+++ b/third_party/rust/time/src/util.rs
@@ -85,13 +85,13 @@ pub mod local_offset {
/// - [`UtcOffset::local_offset_at`](crate::UtcOffset::local_offset_at)
/// - [`UtcOffset::current_local_offset`](crate::UtcOffset::current_local_offset)
pub unsafe fn set_soundness(soundness: Soundness) {
- LOCAL_OFFSET_IS_SOUND.store(soundness == Soundness::Sound, Ordering::SeqCst);
+ LOCAL_OFFSET_IS_SOUND.store(soundness == Soundness::Sound, Ordering::Release);
}
/// Obtains the soundness of obtaining the local UTC offset. If it is [`Soundness::Unsound`],
/// it is allowed to invoke undefined behavior when obtaining the local UTC offset.
pub fn get_soundness() -> Soundness {
- match LOCAL_OFFSET_IS_SOUND.load(Ordering::SeqCst) {
+ match LOCAL_OFFSET_IS_SOUND.load(Ordering::Acquire) {
false => Soundness::Unsound,
true => Soundness::Sound,
}
diff --git a/third_party/rust/time/src/weekday.rs b/third_party/rust/time/src/weekday.rs
index 761107e694..543ecb22cc 100644
--- a/third_party/rust/time/src/weekday.rs
+++ b/third_party/rust/time/src/weekday.rs
@@ -1,10 +1,11 @@
//! Days of the week.
-use core::fmt::{self, Display};
+use core::fmt;
use core::str::FromStr;
-use Weekday::*;
+use powerfmt::smart_display::{FormatterOptions, Metadata, SmartDisplay};
+use self::Weekday::*;
use crate::error;
/// Days of the week.
@@ -13,19 +14,19 @@ use crate::error;
/// Friday), this type does not implement `PartialOrd` or `Ord`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Weekday {
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
Monday,
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
Tuesday,
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
Wednesday,
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
Thursday,
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
Friday,
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
Saturday,
- #[allow(clippy::missing_docs_in_private_items)]
+ #[allow(missing_docs)]
Sunday,
}
@@ -160,9 +161,30 @@ impl Weekday {
}
}
-impl Display for Weekday {
+mod private {
+ #[non_exhaustive]
+ #[derive(Debug, Clone, Copy)]
+ pub struct WeekdayMetadata;
+}
+use private::WeekdayMetadata;
+
+impl SmartDisplay for Weekday {
+ type Metadata = WeekdayMetadata;
+
+ fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> {
+ match self {
+ Monday => Metadata::new(6, self, WeekdayMetadata),
+ Tuesday => Metadata::new(7, self, WeekdayMetadata),
+ Wednesday => Metadata::new(9, self, WeekdayMetadata),
+ Thursday => Metadata::new(8, self, WeekdayMetadata),
+ Friday => Metadata::new(6, self, WeekdayMetadata),
+ Saturday => Metadata::new(8, self, WeekdayMetadata),
+ Sunday => Metadata::new(6, self, WeekdayMetadata),
+ }
+ }
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str(match self {
+ f.pad(match self {
Monday => "Monday",
Tuesday => "Tuesday",
Wednesday => "Wednesday",
@@ -174,6 +196,12 @@ impl Display for Weekday {
}
}
+impl fmt::Display for Weekday {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ SmartDisplay::fmt(self, f)
+ }
+}
+
impl FromStr for Weekday {
type Err = error::InvalidVariant;