diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 498ebad32..277060db0 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -8,7 +8,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - rust: [1.40.0, stable] + rust: [1.34.0, 1.36.0, stable] os: [ubuntu-latest, windows-latest, macOS-latest] steps: @@ -23,30 +23,33 @@ jobs: toolchain: ${{ matrix.rust }} override: true - - name: Run `cargo check --no-default-features` + # deprecated + - name: Run `cargo check --features alloc` uses: actions-rs/cargo@v1 with: command: check args: --no-default-features + if: matrix.rust != '1.34.0' # alloc is unstable in 1.34 - - name: Run `cargo check --no-default-features --features serde` + # std, serde, deprecated + - name: Run `cargo check --features serde` uses: actions-rs/cargo@v1 with: command: check args: --no-default-features --features serde - - name: Run `cargo check --all-features` + # std, deprecated + - name: Run `cargo check` uses: actions-rs/cargo@v1 with: command: check - args: --all-features test: name: Test suite runs-on: ${{ matrix.os }} strategy: matrix: - rust: [1.40.0, stable] + rust: [1.34.0, 1.36.0, stable] # 1.36 is when alloc was stabilized os: [ubuntu-latest, windows-latest, macOS-latest] steps: @@ -61,17 +64,17 @@ jobs: toolchain: ${{ matrix.rust }} override: true - - name: Run `cargo test --no-default-features` + - name: Run `cargo test --features alloc` uses: actions-rs/cargo@v1 with: command: test - args: --no-default-features + args: --features alloc + if: matrix.rust != '1.34.0' # alloc is unstable in 1.34 - - name: Run `cargo test --all-features` + - name: Run `cargo test` uses: actions-rs/cargo@v1 with: command: test - args: --all-features fmt: name: Formatting @@ -117,8 +120,8 @@ jobs: - name: Install clippy run: rustup component add clippy - - name: Run `cargo clippy --all-features` + - name: Run `cargo clippy --features serde,deprecated` uses: actions-rs/clippy-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} - args: --all-features + args: --features serde,deprecated diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 7f60df5c6..8ce0de618 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -21,11 +21,11 @@ jobs: toolchain: nightly # rust-lang/rust#43466, rust-lang/rust#43781 override: true - - name: Run `cargo doc --all-features` + - name: Run `cargo doc --features serde,deprecated` uses: actions-rs/cargo@v1 with: command: doc - args: --all-features + args: --features serde,deprecated - name: Build & publish documentation uses: JamesIves/github-pages-deploy-action@releases/v2 diff --git a/Cargo.toml b/Cargo.toml index 46c5030ed..29d3b73ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,18 +11,19 @@ license = "MIT OR Apache-2.0" # TODO Add GitHub Actions badge once rust-lang/crates.io#1838 is merged. description = "Date and time library. Fully interoperable with the standard library. Mostly compatible with #![no_std]." +[package.metadata.docs.rs] +all-features = true + [features] -default = ["std", "deprecated"] +default = ["deprecated"] deprecated = [] -std = [] +alloc = ["serde/alloc"] panicking-api = [] [dependencies] -serde = { version = "1", optional = true, default-features = false, features = ["derive", "alloc"] } +serde = { version = "1", optional = true, default-features = false, features = ["derive"] } time-macros = { version = "0.1", path = "time-macros" } - -[package.metadata.docs.rs] -all-features = true +rustversion = "1" [workspace] members = [ diff --git a/README.md b/README.md index 473a81a72..82832ace2 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Matrix](https://img.shields.io/badge/chat-Matrix/Riot-blue)](https://riot.im/app/#/room/!AAFrFkLHvtsXtMYRho:matrix.org) ![license](https://img.shields.io/badge/license-MIT%20or%20Apache--2-brightgreen) ![version](https://img.shields.io/crates/v/time) -![rustc 1.40.0](https://img.shields.io/badge/rustc-1.40.0-blue) +![rustc 1.34.0](https://img.shields.io/badge/rustc-1.34.0-blue) [Documentation (master)](https://time-rs.github.io/time/time/index.html)
diff --git a/src/date.rs b/src/date.rs index a3ca0bcfa..3969c7fb1 100644 --- a/src/date.rs +++ b/src/date.rs @@ -1,8 +1,10 @@ -#[cfg(not(feature = "std"))] -use crate::no_std_prelude::*; +#[cfg(feature = "alloc")] +use crate::alloc_prelude::*; use crate::{ format::parse::{parse, ParseError, ParseResult, ParsedItems}, - internals, ComponentRangeError, DeferredFormat, Duration, PrimitiveDateTime, Time, + internals, + shim::*, + ComponentRangeError, DeferredFormat, Duration, PrimitiveDateTime, Time, Weekday::{self, Friday, Monday, Saturday, Sunday, Thursday, Tuesday, Wednesday}, }; use core::{ @@ -272,8 +274,8 @@ impl Date { /// assert!(Date::today().year() >= 2019); /// ``` #[inline(always)] - #[cfg(feature = "std")] - #[cfg_attr(doc, doc(cfg(feature = "std")))] + #[cfg(not(feature = "alloc"))] + #[cfg_attr(doc, doc(cfg(not(feature = "alloc"))))] pub fn today() -> Self { PrimitiveDateTime::now().date() } @@ -524,7 +526,7 @@ impl Date { match (day as i32 + (13 * (month as i32 + 1)) / 5 + adjusted_year + adjusted_year / 4 - adjusted_year / 100 + adjusted_year / 400) - .rem_euclid(7) + .rem_euclid_shim(7) { 0 => Saturday, 1 => Sunday, @@ -635,15 +637,20 @@ impl Date { let f = julian_day + J + (((4 * julian_day + B) / 146_097) * 3) / 4 + C; let e = R * f + V; - let g = e.rem_euclid(P) / R; + let g = e.rem_euclid_shim(P) / R; let h = U * g + W; - let day = h.rem_euclid(S) / U + 1; - let month = (h / S + M).rem_euclid(N) + 1; + let day = h.rem_euclid_shim(S) / U + 1; + let month = (h / S + M).rem_euclid_shim(N) + 1; let year = (e / P) - Y + (N + M - month) / N; // TODO Seek out a formal proof that this always results in a valid value. #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] - internals::Date::from_ymd_unchecked(year as i32, month as u8, day as u8) + Date::try_from_ymd(year as i32, month as u8, day as u8).unwrap_or_else(|e| { + unreachable!( + "Internal error. Please file an issue on the time repository.\n\n{}", + e + ); + }) } } @@ -946,20 +953,17 @@ impl Date { } } - // Verification for all components is done at parse time. match items { - items!(year, month, day) => Ok(internals::Date::from_ymd_unchecked( - year, - month.get(), - day.get(), - )), + items!(year, month, day) => { + Date::try_from_ymd(year, month.get(), day.get()).map_err(Into::into) + } items!(year, ordinal_day) => { - Ok(internals::Date::from_yo_unchecked(year, ordinal_day.get())) + Date::try_from_yo(year, ordinal_day.get()).map_err(Into::into) + } + items!(week_based_year, iso_week, weekday) => { + Date::try_from_iso_ywd(week_based_year, iso_week.get(), weekday).map_err(Into::into) } - items!(week_based_year, iso_week, weekday) => Ok( - internals::Date::from_iso_ywd_unchecked(week_based_year, iso_week.get(), weekday), - ), - items!(year, sunday_week, weekday) => Ok(internals::Date::from_yo_unchecked( + items!(year, sunday_week, weekday) => Date::try_from_yo( year, #[allow(clippy::cast_sign_loss)] { @@ -967,8 +971,9 @@ impl Date { - adjustment(year) + 1) as u16 }, - )), - items!(year, monday_week, weekday) => Ok(internals::Date::from_yo_unchecked( + ) + .map_err(Into::into), + items!(year, monday_week, weekday) => Date::try_from_yo( year, #[allow(clippy::cast_sign_loss)] { @@ -976,7 +981,8 @@ impl Date { - adjustment(year) + 1) as u16 }, - )), + ) + .map_err(Into::into), _ => Err(ParseError::InsufficientInformation), } } @@ -1079,7 +1085,6 @@ impl Ord for Date { mod test { use super::*; use crate::{date, prelude::*, time}; - use alloc::collections::btree_set::BTreeSet; macro_rules! julian { ($julian:literal) => { @@ -1089,17 +1094,12 @@ mod test { #[test] fn weeks_in_year_exhaustive() { - let mut years_with_53 = BTreeSet::new(); - for year in [ + let years_with_53 = &[ 4, 9, 15, 20, 26, 32, 37, 43, 48, 54, 60, 65, 71, 76, 82, 88, 93, 99, 105, 111, 116, 122, 128, 133, 139, 144, 150, 156, 161, 167, 172, 178, 184, 189, 195, 201, 207, 212, 218, 224, 229, 235, 240, 246, 252, 257, 263, 268, 274, 280, 285, 291, 296, 303, 308, 314, 320, 325, 331, 336, 342, 348, 353, 359, 364, 370, 376, 381, 387, 392, 398, - ] - .iter() - { - years_with_53.insert(year); - } + ]; for year in 0..400 { assert_eq!( @@ -1536,423 +1536,399 @@ mod test { #[test] #[allow(clippy::zero_prefixed_literal)] fn test_parse_monday_based_week() { - macro_rules! assert_dwy { - ($weekday:ident $week:literal $year:literal => $ordinal:literal) => { - assert_eq!( - Date::parse( - concat!( - stringify!($weekday), - " ", - stringify!($week), - " ", - stringify!($year) - ), - "%a %W %Y" - ), - Ok(date!($year - $ordinal)) - ); + macro_rules! parse { + ($s:literal) => { + Date::parse($s, "%a %W %Y").unwrap() }; } // A - assert_dwy!(Sun 00 2023 => 001); - assert_dwy!(Mon 01 2023 => 002); - assert_dwy!(Tue 01 2023 => 003); - assert_dwy!(Wed 01 2023 => 004); - assert_dwy!(Thu 01 2023 => 005); - assert_dwy!(Fri 01 2023 => 006); - assert_dwy!(Sat 01 2023 => 007); + assert_eq!(parse!("Sun 00 2023"), date!(2023-001)); + assert_eq!(parse!("Mon 01 2023"), date!(2023-002)); + assert_eq!(parse!("Tue 01 2023"), date!(2023-003)); + assert_eq!(parse!("Wed 01 2023"), date!(2023-004)); + assert_eq!(parse!("Thu 01 2023"), date!(2023-005)); + assert_eq!(parse!("Fri 01 2023"), date!(2023-006)); + assert_eq!(parse!("Sat 01 2023"), date!(2023-007)); // B - assert_dwy!(Sat 00 2022 => 001); - assert_dwy!(Sun 00 2022 => 002); - assert_dwy!(Mon 01 2022 => 003); - assert_dwy!(Tue 01 2022 => 004); - assert_dwy!(Wed 01 2022 => 005); - assert_dwy!(Thu 01 2022 => 006); - assert_dwy!(Fri 01 2022 => 007); + assert_eq!(parse!("Sat 00 2022"), date!(2022-001)); + assert_eq!(parse!("Sun 00 2022"), date!(2022-002)); + assert_eq!(parse!("Mon 01 2022"), date!(2022-003)); + assert_eq!(parse!("Tue 01 2022"), date!(2022-004)); + assert_eq!(parse!("Wed 01 2022"), date!(2022-005)); + assert_eq!(parse!("Thu 01 2022"), date!(2022-006)); + assert_eq!(parse!("Fri 01 2022"), date!(2022-007)); // C - assert_dwy!(Fri 00 2021 => 001); - assert_dwy!(Sat 00 2021 => 002); - assert_dwy!(Sun 00 2021 => 003); - assert_dwy!(Mon 01 2021 => 004); - assert_dwy!(Tue 01 2021 => 005); - assert_dwy!(Wed 01 2021 => 006); - assert_dwy!(Thu 01 2021 => 007); + assert_eq!(parse!("Fri 00 2021"), date!(2021-001)); + assert_eq!(parse!("Sat 00 2021"), date!(2021-002)); + assert_eq!(parse!("Sun 00 2021"), date!(2021-003)); + assert_eq!(parse!("Mon 01 2021"), date!(2021-004)); + assert_eq!(parse!("Tue 01 2021"), date!(2021-005)); + assert_eq!(parse!("Wed 01 2021"), date!(2021-006)); + assert_eq!(parse!("Thu 01 2021"), date!(2021-007)); // D - assert_dwy!(Thu 00 2026 => 001); - assert_dwy!(Fri 00 2026 => 002); - assert_dwy!(Sat 00 2026 => 003); - assert_dwy!(Sun 00 2026 => 004); - assert_dwy!(Mon 01 2026 => 005); - assert_dwy!(Tue 01 2026 => 006); - assert_dwy!(Wed 01 2026 => 007); + assert_eq!(parse!("Thu 00 2026"), date!(2026-001)); + assert_eq!(parse!("Fri 00 2026"), date!(2026-002)); + assert_eq!(parse!("Sat 00 2026"), date!(2026-003)); + assert_eq!(parse!("Sun 00 2026"), date!(2026-004)); + assert_eq!(parse!("Mon 01 2026"), date!(2026-005)); + assert_eq!(parse!("Tue 01 2026"), date!(2026-006)); + assert_eq!(parse!("Wed 01 2026"), date!(2026-007)); // E - assert_dwy!(Wed 00 2025 => 001); - assert_dwy!(Thu 00 2025 => 002); - assert_dwy!(Fri 00 2025 => 003); - assert_dwy!(Sat 00 2025 => 004); - assert_dwy!(Sun 00 2025 => 005); - assert_dwy!(Mon 01 2025 => 006); - assert_dwy!(Tue 01 2025 => 007); + assert_eq!(parse!("Wed 00 2025"), date!(2025-001)); + assert_eq!(parse!("Thu 00 2025"), date!(2025-002)); + assert_eq!(parse!("Fri 00 2025"), date!(2025-003)); + assert_eq!(parse!("Sat 00 2025"), date!(2025-004)); + assert_eq!(parse!("Sun 00 2025"), date!(2025-005)); + assert_eq!(parse!("Mon 01 2025"), date!(2025-006)); + assert_eq!(parse!("Tue 01 2025"), date!(2025-007)); // F - assert_dwy!(Tue 00 2019 => 001); - assert_dwy!(Wed 00 2019 => 002); - assert_dwy!(Thu 00 2019 => 003); - assert_dwy!(Fri 00 2019 => 004); - assert_dwy!(Sat 00 2019 => 005); - assert_dwy!(Sun 00 2019 => 006); - assert_dwy!(Mon 01 2019 => 007); + assert_eq!(parse!("Tue 00 2019"), date!(2019-001)); + assert_eq!(parse!("Wed 00 2019"), date!(2019-002)); + assert_eq!(parse!("Thu 00 2019"), date!(2019-003)); + assert_eq!(parse!("Fri 00 2019"), date!(2019-004)); + assert_eq!(parse!("Sat 00 2019"), date!(2019-005)); + assert_eq!(parse!("Sun 00 2019"), date!(2019-006)); + assert_eq!(parse!("Mon 01 2019"), date!(2019-007)); // G - assert_dwy!(Mon 01 2018 => 001); - assert_dwy!(Tue 01 2018 => 002); - assert_dwy!(Wed 01 2018 => 003); - assert_dwy!(Thu 01 2018 => 004); - assert_dwy!(Fri 01 2018 => 005); - assert_dwy!(Sat 01 2018 => 006); - assert_dwy!(Sun 01 2018 => 007); + assert_eq!(parse!("Mon 01 2018"), date!(2018-001)); + assert_eq!(parse!("Tue 01 2018"), date!(2018-002)); + assert_eq!(parse!("Wed 01 2018"), date!(2018-003)); + assert_eq!(parse!("Thu 01 2018"), date!(2018-004)); + assert_eq!(parse!("Fri 01 2018"), date!(2018-005)); + assert_eq!(parse!("Sat 01 2018"), date!(2018-006)); + assert_eq!(parse!("Sun 01 2018"), date!(2018-007)); // AG - assert_dwy!(Sun 00 2012 => 001); - assert_dwy!(Mon 01 2012 => 002); - assert_dwy!(Tue 01 2012 => 003); - assert_dwy!(Wed 01 2012 => 004); - assert_dwy!(Thu 01 2012 => 005); - assert_dwy!(Fri 01 2012 => 006); - assert_dwy!(Sat 01 2012 => 007); - assert_dwy!(Tue 09 2012 => 059); - assert_dwy!(Wed 09 2012 => 060); - assert_dwy!(Thu 09 2012 => 061); - assert_dwy!(Fri 09 2012 => 062); - assert_dwy!(Sat 09 2012 => 063); - assert_dwy!(Sun 09 2012 => 064); - assert_dwy!(Mon 10 2012 => 065); - assert_dwy!(Tue 10 2012 => 066); - assert_dwy!(Wed 10 2012 => 067); + assert_eq!(parse!("Sun 00 2012"), date!(2012-001)); + assert_eq!(parse!("Mon 01 2012"), date!(2012-002)); + assert_eq!(parse!("Tue 01 2012"), date!(2012-003)); + assert_eq!(parse!("Wed 01 2012"), date!(2012-004)); + assert_eq!(parse!("Thu 01 2012"), date!(2012-005)); + assert_eq!(parse!("Fri 01 2012"), date!(2012-006)); + assert_eq!(parse!("Sat 01 2012"), date!(2012-007)); + assert_eq!(parse!("Tue 09 2012"), date!(2012-059)); + assert_eq!(parse!("Wed 09 2012"), date!(2012-060)); + assert_eq!(parse!("Thu 09 2012"), date!(2012-061)); + assert_eq!(parse!("Fri 09 2012"), date!(2012-062)); + assert_eq!(parse!("Sat 09 2012"), date!(2012-063)); + assert_eq!(parse!("Sun 09 2012"), date!(2012-064)); + assert_eq!(parse!("Mon 10 2012"), date!(2012-065)); + assert_eq!(parse!("Tue 10 2012"), date!(2012-066)); + assert_eq!(parse!("Wed 10 2012"), date!(2012-067)); // BA - assert_dwy!(Sat 00 2028 => 001); - assert_dwy!(Sun 00 2028 => 002); - assert_dwy!(Mon 01 2028 => 003); - assert_dwy!(Tue 01 2028 => 004); - assert_dwy!(Wed 01 2028 => 005); - assert_dwy!(Thu 01 2028 => 006); - assert_dwy!(Fri 01 2028 => 007); - assert_dwy!(Mon 09 2028 => 059); - assert_dwy!(Tue 09 2028 => 060); - assert_dwy!(Wed 09 2028 => 061); - assert_dwy!(Thu 09 2028 => 062); - assert_dwy!(Fri 09 2028 => 063); - assert_dwy!(Sat 09 2028 => 064); - assert_dwy!(Sun 09 2028 => 065); - assert_dwy!(Mon 10 2028 => 066); - assert_dwy!(Tue 10 2028 => 067); + assert_eq!(parse!("Sat 00 2028"), date!(2028-001)); + assert_eq!(parse!("Sun 00 2028"), date!(2028-002)); + assert_eq!(parse!("Mon 01 2028"), date!(2028-003)); + assert_eq!(parse!("Tue 01 2028"), date!(2028-004)); + assert_eq!(parse!("Wed 01 2028"), date!(2028-005)); + assert_eq!(parse!("Thu 01 2028"), date!(2028-006)); + assert_eq!(parse!("Fri 01 2028"), date!(2028-007)); + assert_eq!(parse!("Mon 09 2028"), date!(2028-059)); + assert_eq!(parse!("Tue 09 2028"), date!(2028-060)); + assert_eq!(parse!("Wed 09 2028"), date!(2028-061)); + assert_eq!(parse!("Thu 09 2028"), date!(2028-062)); + assert_eq!(parse!("Fri 09 2028"), date!(2028-063)); + assert_eq!(parse!("Sat 09 2028"), date!(2028-064)); + assert_eq!(parse!("Sun 09 2028"), date!(2028-065)); + assert_eq!(parse!("Mon 10 2028"), date!(2028-066)); + assert_eq!(parse!("Tue 10 2028"), date!(2028-067)); // CB - assert_dwy!(Fri 00 2016 => 001); - assert_dwy!(Sat 00 2016 => 002); - assert_dwy!(Sun 00 2016 => 003); - assert_dwy!(Mon 01 2016 => 004); - assert_dwy!(Tue 01 2016 => 005); - assert_dwy!(Wed 01 2016 => 006); - assert_dwy!(Thu 01 2016 => 007); - assert_dwy!(Sun 08 2016 => 059); - assert_dwy!(Mon 09 2016 => 060); - assert_dwy!(Tue 09 2016 => 061); - assert_dwy!(Wed 09 2016 => 062); - assert_dwy!(Thu 09 2016 => 063); - assert_dwy!(Fri 09 2016 => 064); - assert_dwy!(Sat 09 2016 => 065); - assert_dwy!(Sun 09 2016 => 066); - assert_dwy!(Mon 10 2016 => 067); + assert_eq!(parse!("Fri 00 2016"), date!(2016-001)); + assert_eq!(parse!("Sat 00 2016"), date!(2016-002)); + assert_eq!(parse!("Sun 00 2016"), date!(2016-003)); + assert_eq!(parse!("Mon 01 2016"), date!(2016-004)); + assert_eq!(parse!("Tue 01 2016"), date!(2016-005)); + assert_eq!(parse!("Wed 01 2016"), date!(2016-006)); + assert_eq!(parse!("Thu 01 2016"), date!(2016-007)); + assert_eq!(parse!("Sun 08 2016"), date!(2016-059)); + assert_eq!(parse!("Mon 09 2016"), date!(2016-060)); + assert_eq!(parse!("Tue 09 2016"), date!(2016-061)); + assert_eq!(parse!("Wed 09 2016"), date!(2016-062)); + assert_eq!(parse!("Thu 09 2016"), date!(2016-063)); + assert_eq!(parse!("Fri 09 2016"), date!(2016-064)); + assert_eq!(parse!("Sat 09 2016"), date!(2016-065)); + assert_eq!(parse!("Sun 09 2016"), date!(2016-066)); + assert_eq!(parse!("Mon 10 2016"), date!(2016-067)); // DC - assert_dwy!(Thu 00 2032 => 001); - assert_dwy!(Fri 00 2032 => 002); - assert_dwy!(Sat 00 2032 => 003); - assert_dwy!(Sun 00 2032 => 004); - assert_dwy!(Mon 01 2032 => 005); - assert_dwy!(Tue 01 2032 => 006); - assert_dwy!(Wed 01 2032 => 007); - assert_dwy!(Sat 08 2032 => 059); - assert_dwy!(Sun 08 2032 => 060); - assert_dwy!(Mon 09 2032 => 061); - assert_dwy!(Tue 09 2032 => 062); - assert_dwy!(Wed 09 2032 => 063); - assert_dwy!(Thu 09 2032 => 064); - assert_dwy!(Fri 09 2032 => 065); - assert_dwy!(Sat 09 2032 => 066); - assert_dwy!(Sun 09 2032 => 067); + assert_eq!(parse!("Thu 00 2032"), date!(2032-001)); + assert_eq!(parse!("Fri 00 2032"), date!(2032-002)); + assert_eq!(parse!("Sat 00 2032"), date!(2032-003)); + assert_eq!(parse!("Sun 00 2032"), date!(2032-004)); + assert_eq!(parse!("Mon 01 2032"), date!(2032-005)); + assert_eq!(parse!("Tue 01 2032"), date!(2032-006)); + assert_eq!(parse!("Wed 01 2032"), date!(2032-007)); + assert_eq!(parse!("Sat 08 2032"), date!(2032-059)); + assert_eq!(parse!("Sun 08 2032"), date!(2032-060)); + assert_eq!(parse!("Mon 09 2032"), date!(2032-061)); + assert_eq!(parse!("Tue 09 2032"), date!(2032-062)); + assert_eq!(parse!("Wed 09 2032"), date!(2032-063)); + assert_eq!(parse!("Thu 09 2032"), date!(2032-064)); + assert_eq!(parse!("Fri 09 2032"), date!(2032-065)); + assert_eq!(parse!("Sat 09 2032"), date!(2032-066)); + assert_eq!(parse!("Sun 09 2032"), date!(2032-067)); // ED - assert_dwy!(Wed 00 2020 => 001); - assert_dwy!(Thu 00 2020 => 002); - assert_dwy!(Fri 00 2020 => 003); - assert_dwy!(Sat 00 2020 => 004); - assert_dwy!(Sun 00 2020 => 005); - assert_dwy!(Mon 01 2020 => 006); - assert_dwy!(Tue 01 2020 => 007); - assert_dwy!(Fri 08 2020 => 059); - assert_dwy!(Sat 08 2020 => 060); - assert_dwy!(Sun 08 2020 => 061); - assert_dwy!(Mon 09 2020 => 062); - assert_dwy!(Tue 09 2020 => 063); - assert_dwy!(Wed 09 2020 => 064); - assert_dwy!(Thu 09 2020 => 065); - assert_dwy!(Fri 09 2020 => 066); - assert_dwy!(Sat 09 2020 => 067); + assert_eq!(parse!("Wed 00 2020"), date!(2020-001)); + assert_eq!(parse!("Thu 00 2020"), date!(2020-002)); + assert_eq!(parse!("Fri 00 2020"), date!(2020-003)); + assert_eq!(parse!("Sat 00 2020"), date!(2020-004)); + assert_eq!(parse!("Sun 00 2020"), date!(2020-005)); + assert_eq!(parse!("Mon 01 2020"), date!(2020-006)); + assert_eq!(parse!("Tue 01 2020"), date!(2020-007)); + assert_eq!(parse!("Fri 08 2020"), date!(2020-059)); + assert_eq!(parse!("Sat 08 2020"), date!(2020-060)); + assert_eq!(parse!("Sun 08 2020"), date!(2020-061)); + assert_eq!(parse!("Mon 09 2020"), date!(2020-062)); + assert_eq!(parse!("Tue 09 2020"), date!(2020-063)); + assert_eq!(parse!("Wed 09 2020"), date!(2020-064)); + assert_eq!(parse!("Thu 09 2020"), date!(2020-065)); + assert_eq!(parse!("Fri 09 2020"), date!(2020-066)); + assert_eq!(parse!("Sat 09 2020"), date!(2020-067)); // FE - assert_dwy!(Tue 00 2036 => 001); - assert_dwy!(Wed 00 2036 => 002); - assert_dwy!(Thu 00 2036 => 003); - assert_dwy!(Fri 00 2036 => 004); - assert_dwy!(Sat 00 2036 => 005); - assert_dwy!(Sun 00 2036 => 006); - assert_dwy!(Mon 01 2036 => 007); - assert_dwy!(Thu 08 2036 => 059); - assert_dwy!(Fri 08 2036 => 060); - assert_dwy!(Sat 08 2036 => 061); - assert_dwy!(Sun 08 2036 => 062); - assert_dwy!(Mon 09 2036 => 063); - assert_dwy!(Tue 09 2036 => 064); - assert_dwy!(Wed 09 2036 => 065); - assert_dwy!(Thu 09 2036 => 066); - assert_dwy!(Fri 09 2036 => 067); + assert_eq!(parse!("Tue 00 2036"), date!(2036-001)); + assert_eq!(parse!("Wed 00 2036"), date!(2036-002)); + assert_eq!(parse!("Thu 00 2036"), date!(2036-003)); + assert_eq!(parse!("Fri 00 2036"), date!(2036-004)); + assert_eq!(parse!("Sat 00 2036"), date!(2036-005)); + assert_eq!(parse!("Sun 00 2036"), date!(2036-006)); + assert_eq!(parse!("Mon 01 2036"), date!(2036-007)); + assert_eq!(parse!("Thu 08 2036"), date!(2036-059)); + assert_eq!(parse!("Fri 08 2036"), date!(2036-060)); + assert_eq!(parse!("Sat 08 2036"), date!(2036-061)); + assert_eq!(parse!("Sun 08 2036"), date!(2036-062)); + assert_eq!(parse!("Mon 09 2036"), date!(2036-063)); + assert_eq!(parse!("Tue 09 2036"), date!(2036-064)); + assert_eq!(parse!("Wed 09 2036"), date!(2036-065)); + assert_eq!(parse!("Thu 09 2036"), date!(2036-066)); + assert_eq!(parse!("Fri 09 2036"), date!(2036-067)); // GF - assert_dwy!(Mon 01 2024 => 001); - assert_dwy!(Tue 01 2024 => 002); - assert_dwy!(Wed 01 2024 => 003); - assert_dwy!(Thu 01 2024 => 004); - assert_dwy!(Fri 01 2024 => 005); - assert_dwy!(Sat 01 2024 => 006); - assert_dwy!(Sun 01 2024 => 007); - assert_dwy!(Wed 09 2024 => 059); - assert_dwy!(Thu 09 2024 => 060); - assert_dwy!(Fri 09 2024 => 061); - assert_dwy!(Sat 09 2024 => 062); - assert_dwy!(Sun 09 2024 => 063); - assert_dwy!(Mon 10 2024 => 064); - assert_dwy!(Tue 10 2024 => 065); - assert_dwy!(Wed 10 2024 => 066); - assert_dwy!(Thu 10 2024 => 067); + assert_eq!(parse!("Mon 01 2024"), date!(2024-001)); + assert_eq!(parse!("Tue 01 2024"), date!(2024-002)); + assert_eq!(parse!("Wed 01 2024"), date!(2024-003)); + assert_eq!(parse!("Thu 01 2024"), date!(2024-004)); + assert_eq!(parse!("Fri 01 2024"), date!(2024-005)); + assert_eq!(parse!("Sat 01 2024"), date!(2024-006)); + assert_eq!(parse!("Sun 01 2024"), date!(2024-007)); + assert_eq!(parse!("Wed 09 2024"), date!(2024-059)); + assert_eq!(parse!("Thu 09 2024"), date!(2024-060)); + assert_eq!(parse!("Fri 09 2024"), date!(2024-061)); + assert_eq!(parse!("Sat 09 2024"), date!(2024-062)); + assert_eq!(parse!("Sun 09 2024"), date!(2024-063)); + assert_eq!(parse!("Mon 10 2024"), date!(2024-064)); + assert_eq!(parse!("Tue 10 2024"), date!(2024-065)); + assert_eq!(parse!("Wed 10 2024"), date!(2024-066)); + assert_eq!(parse!("Thu 10 2024"), date!(2024-067)); } #[test] #[allow(clippy::zero_prefixed_literal)] fn test_parse_sunday_based_week() { - macro_rules! assert_dwy { - ($weekday:ident $week:literal $year:literal => $ordinal:literal) => { - assert_eq!( - Date::parse( - concat!( - stringify!($weekday), - " ", - stringify!($week), - " ", - stringify!($year) - ), - "%a %U %Y" - ), - Ok(date!($year - $ordinal)) - ); + macro_rules! parse { + ($s:literal) => { + Date::parse($s, "%a %U %Y").unwrap() }; } // A - assert_dwy!(Sun 01 2018 => 001); - assert_dwy!(Mon 01 2018 => 002); - assert_dwy!(Tue 01 2018 => 003); - assert_dwy!(Wed 01 2018 => 004); - assert_dwy!(Thu 01 2018 => 005); - assert_dwy!(Fri 01 2018 => 006); - assert_dwy!(Sat 01 2018 => 007); + assert_eq!(parse!("Sun 01 2018"), date!(2018-001)); + assert_eq!(parse!("Mon 01 2018"), date!(2018-002)); + assert_eq!(parse!("Tue 01 2018"), date!(2018-003)); + assert_eq!(parse!("Wed 01 2018"), date!(2018-004)); + assert_eq!(parse!("Thu 01 2018"), date!(2018-005)); + assert_eq!(parse!("Fri 01 2018"), date!(2018-006)); + assert_eq!(parse!("Sat 01 2018"), date!(2018-007)); // B - assert_dwy!(Sat 00 2023 => 001); - assert_dwy!(Sun 01 2023 => 002); - assert_dwy!(Mon 01 2023 => 003); - assert_dwy!(Tue 01 2023 => 004); - assert_dwy!(Wed 01 2023 => 005); - assert_dwy!(Thu 01 2023 => 006); - assert_dwy!(Fri 01 2023 => 007); + assert_eq!(parse!("Sat 00 2023"), date!(2023-001)); + assert_eq!(parse!("Sun 01 2023"), date!(2023-002)); + assert_eq!(parse!("Mon 01 2023"), date!(2023-003)); + assert_eq!(parse!("Tue 01 2023"), date!(2023-004)); + assert_eq!(parse!("Wed 01 2023"), date!(2023-005)); + assert_eq!(parse!("Thu 01 2023"), date!(2023-006)); + assert_eq!(parse!("Fri 01 2023"), date!(2023-007)); // C - assert_dwy!(Fri 00 2022 => 001); - assert_dwy!(Sat 00 2022 => 002); - assert_dwy!(Sun 01 2022 => 003); - assert_dwy!(Mon 01 2022 => 004); - assert_dwy!(Tue 01 2022 => 005); - assert_dwy!(Wed 01 2022 => 006); - assert_dwy!(Thu 01 2022 => 007); + assert_eq!(parse!("Fri 00 2022"), date!(2022-001)); + assert_eq!(parse!("Sat 00 2022"), date!(2022-002)); + assert_eq!(parse!("Sun 01 2022"), date!(2022-003)); + assert_eq!(parse!("Mon 01 2022"), date!(2022-004)); + assert_eq!(parse!("Tue 01 2022"), date!(2022-005)); + assert_eq!(parse!("Wed 01 2022"), date!(2022-006)); + assert_eq!(parse!("Thu 01 2022"), date!(2022-007)); // D - assert_dwy!(Thu 00 2021 => 001); - assert_dwy!(Fri 00 2021 => 002); - assert_dwy!(Sat 00 2021 => 003); - assert_dwy!(Sun 01 2021 => 004); - assert_dwy!(Mon 01 2021 => 005); - assert_dwy!(Tue 01 2021 => 006); - assert_dwy!(Wed 01 2021 => 007); + assert_eq!(parse!("Thu 00 2021"), date!(2021-001)); + assert_eq!(parse!("Fri 00 2021"), date!(2021-002)); + assert_eq!(parse!("Sat 00 2021"), date!(2021-003)); + assert_eq!(parse!("Sun 01 2021"), date!(2021-004)); + assert_eq!(parse!("Mon 01 2021"), date!(2021-005)); + assert_eq!(parse!("Tue 01 2021"), date!(2021-006)); + assert_eq!(parse!("Wed 01 2021"), date!(2021-007)); // E - assert_dwy!(Wed 00 2026 => 001); - assert_dwy!(Thu 00 2026 => 002); - assert_dwy!(Fri 00 2026 => 003); - assert_dwy!(Sat 00 2026 => 004); - assert_dwy!(Sun 01 2026 => 005); - assert_dwy!(Mon 01 2026 => 006); - assert_dwy!(Tue 01 2026 => 007); + assert_eq!(parse!("Wed 00 2026"), date!(2026-001)); + assert_eq!(parse!("Thu 00 2026"), date!(2026-002)); + assert_eq!(parse!("Fri 00 2026"), date!(2026-003)); + assert_eq!(parse!("Sat 00 2026"), date!(2026-004)); + assert_eq!(parse!("Sun 01 2026"), date!(2026-005)); + assert_eq!(parse!("Mon 01 2026"), date!(2026-006)); + assert_eq!(parse!("Tue 01 2026"), date!(2026-007)); // F - assert_dwy!(Tue 00 2025 => 001); - assert_dwy!(Wed 00 2025 => 002); - assert_dwy!(Thu 00 2025 => 003); - assert_dwy!(Fri 00 2025 => 004); - assert_dwy!(Sat 00 2025 => 005); - assert_dwy!(Sun 01 2025 => 006); - assert_dwy!(Mon 01 2025 => 007); + assert_eq!(parse!("Tue 00 2025"), date!(2025-001)); + assert_eq!(parse!("Wed 00 2025"), date!(2025-002)); + assert_eq!(parse!("Thu 00 2025"), date!(2025-003)); + assert_eq!(parse!("Fri 00 2025"), date!(2025-004)); + assert_eq!(parse!("Sat 00 2025"), date!(2025-005)); + assert_eq!(parse!("Sun 01 2025"), date!(2025-006)); + assert_eq!(parse!("Mon 01 2025"), date!(2025-007)); // G - assert_dwy!(Mon 00 2019 => 001); - assert_dwy!(Tue 00 2019 => 002); - assert_dwy!(Wed 00 2019 => 003); - assert_dwy!(Thu 00 2019 => 004); - assert_dwy!(Fri 00 2019 => 005); - assert_dwy!(Sat 00 2019 => 006); - assert_dwy!(Sun 01 2019 => 007); + assert_eq!(parse!("Mon 00 2019"), date!(2019-001)); + assert_eq!(parse!("Tue 00 2019"), date!(2019-002)); + assert_eq!(parse!("Wed 00 2019"), date!(2019-003)); + assert_eq!(parse!("Thu 00 2019"), date!(2019-004)); + assert_eq!(parse!("Fri 00 2019"), date!(2019-005)); + assert_eq!(parse!("Sat 00 2019"), date!(2019-006)); + assert_eq!(parse!("Sun 01 2019"), date!(2019-007)); // AG - assert_dwy!(Sun 01 2024 => 001); - assert_dwy!(Mon 01 2024 => 002); - assert_dwy!(Tue 01 2024 => 003); - assert_dwy!(Wed 01 2024 => 004); - assert_dwy!(Thu 01 2024 => 005); - assert_dwy!(Fri 01 2024 => 006); - assert_dwy!(Sat 01 2024 => 007); - assert_dwy!(Tue 09 2024 => 059); - assert_dwy!(Wed 09 2024 => 060); - assert_dwy!(Thu 09 2024 => 061); - assert_dwy!(Fri 09 2024 => 062); - assert_dwy!(Sat 09 2024 => 063); - assert_dwy!(Sun 10 2024 => 064); - assert_dwy!(Mon 10 2024 => 065); - assert_dwy!(Tue 10 2024 => 066); - assert_dwy!(Wed 10 2024 => 067); + assert_eq!(parse!("Sun 01 2024"), date!(2024-001)); + assert_eq!(parse!("Mon 01 2024"), date!(2024-002)); + assert_eq!(parse!("Tue 01 2024"), date!(2024-003)); + assert_eq!(parse!("Wed 01 2024"), date!(2024-004)); + assert_eq!(parse!("Thu 01 2024"), date!(2024-005)); + assert_eq!(parse!("Fri 01 2024"), date!(2024-006)); + assert_eq!(parse!("Sat 01 2024"), date!(2024-007)); + assert_eq!(parse!("Tue 09 2024"), date!(2024-059)); + assert_eq!(parse!("Wed 09 2024"), date!(2024-060)); + assert_eq!(parse!("Thu 09 2024"), date!(2024-061)); + assert_eq!(parse!("Fri 09 2024"), date!(2024-062)); + assert_eq!(parse!("Sat 09 2024"), date!(2024-063)); + assert_eq!(parse!("Sun 10 2024"), date!(2024-064)); + assert_eq!(parse!("Mon 10 2024"), date!(2024-065)); + assert_eq!(parse!("Tue 10 2024"), date!(2024-066)); + assert_eq!(parse!("Wed 10 2024"), date!(2024-067)); // BA - assert_dwy!(Sat 00 2012 => 001); - assert_dwy!(Sun 01 2012 => 002); - assert_dwy!(Mon 01 2012 => 003); - assert_dwy!(Tue 01 2012 => 004); - assert_dwy!(Wed 01 2012 => 005); - assert_dwy!(Thu 01 2012 => 006); - assert_dwy!(Fri 01 2012 => 007); - assert_dwy!(Mon 09 2012 => 059); - assert_dwy!(Tue 09 2012 => 060); - assert_dwy!(Wed 09 2012 => 061); - assert_dwy!(Thu 09 2012 => 062); - assert_dwy!(Fri 09 2012 => 063); - assert_dwy!(Sat 09 2012 => 064); - assert_dwy!(Sun 10 2012 => 065); - assert_dwy!(Mon 10 2012 => 066); - assert_dwy!(Tue 10 2012 => 067); + assert_eq!(parse!("Sat 00 2012"), date!(2012-001)); + assert_eq!(parse!("Sun 01 2012"), date!(2012-002)); + assert_eq!(parse!("Mon 01 2012"), date!(2012-003)); + assert_eq!(parse!("Tue 01 2012"), date!(2012-004)); + assert_eq!(parse!("Wed 01 2012"), date!(2012-005)); + assert_eq!(parse!("Thu 01 2012"), date!(2012-006)); + assert_eq!(parse!("Fri 01 2012"), date!(2012-007)); + assert_eq!(parse!("Mon 09 2012"), date!(2012-059)); + assert_eq!(parse!("Tue 09 2012"), date!(2012-060)); + assert_eq!(parse!("Wed 09 2012"), date!(2012-061)); + assert_eq!(parse!("Thu 09 2012"), date!(2012-062)); + assert_eq!(parse!("Fri 09 2012"), date!(2012-063)); + assert_eq!(parse!("Sat 09 2012"), date!(2012-064)); + assert_eq!(parse!("Sun 10 2012"), date!(2012-065)); + assert_eq!(parse!("Mon 10 2012"), date!(2012-066)); + assert_eq!(parse!("Tue 10 2012"), date!(2012-067)); // CB - assert_dwy!(Fri 00 2028 => 001); - assert_dwy!(Sat 00 2028 => 002); - assert_dwy!(Sun 01 2028 => 003); - assert_dwy!(Mon 01 2028 => 004); - assert_dwy!(Tue 01 2028 => 005); - assert_dwy!(Wed 01 2028 => 006); - assert_dwy!(Thu 01 2028 => 007); - assert_dwy!(Sun 09 2028 => 059); - assert_dwy!(Mon 09 2028 => 060); - assert_dwy!(Tue 09 2028 => 061); - assert_dwy!(Wed 09 2028 => 062); - assert_dwy!(Thu 09 2028 => 063); - assert_dwy!(Fri 09 2028 => 064); - assert_dwy!(Sat 09 2028 => 065); - assert_dwy!(Sun 10 2028 => 066); - assert_dwy!(Mon 10 2028 => 067); + assert_eq!(parse!("Fri 00 2028"), date!(2028-001)); + assert_eq!(parse!("Sat 00 2028"), date!(2028-002)); + assert_eq!(parse!("Sun 01 2028"), date!(2028-003)); + assert_eq!(parse!("Mon 01 2028"), date!(2028-004)); + assert_eq!(parse!("Tue 01 2028"), date!(2028-005)); + assert_eq!(parse!("Wed 01 2028"), date!(2028-006)); + assert_eq!(parse!("Thu 01 2028"), date!(2028-007)); + assert_eq!(parse!("Sun 09 2028"), date!(2028-059)); + assert_eq!(parse!("Mon 09 2028"), date!(2028-060)); + assert_eq!(parse!("Tue 09 2028"), date!(2028-061)); + assert_eq!(parse!("Wed 09 2028"), date!(2028-062)); + assert_eq!(parse!("Thu 09 2028"), date!(2028-063)); + assert_eq!(parse!("Fri 09 2028"), date!(2028-064)); + assert_eq!(parse!("Sat 09 2028"), date!(2028-065)); + assert_eq!(parse!("Sun 10 2028"), date!(2028-066)); + assert_eq!(parse!("Mon 10 2028"), date!(2028-067)); // DC - assert_dwy!(Thu 00 2016 => 001); - assert_dwy!(Fri 00 2016 => 002); - assert_dwy!(Sat 00 2016 => 003); - assert_dwy!(Sun 01 2016 => 004); - assert_dwy!(Mon 01 2016 => 005); - assert_dwy!(Tue 01 2016 => 006); - assert_dwy!(Wed 01 2016 => 007); - assert_dwy!(Sat 08 2016 => 059); - assert_dwy!(Sun 09 2016 => 060); - assert_dwy!(Mon 09 2016 => 061); - assert_dwy!(Tue 09 2016 => 062); - assert_dwy!(Wed 09 2016 => 063); - assert_dwy!(Thu 09 2016 => 064); - assert_dwy!(Fri 09 2016 => 065); - assert_dwy!(Sat 09 2016 => 066); - assert_dwy!(Sun 10 2016 => 067); + assert_eq!(parse!("Thu 00 2016"), date!(2016-001)); + assert_eq!(parse!("Fri 00 2016"), date!(2016-002)); + assert_eq!(parse!("Sat 00 2016"), date!(2016-003)); + assert_eq!(parse!("Sun 01 2016"), date!(2016-004)); + assert_eq!(parse!("Mon 01 2016"), date!(2016-005)); + assert_eq!(parse!("Tue 01 2016"), date!(2016-006)); + assert_eq!(parse!("Wed 01 2016"), date!(2016-007)); + assert_eq!(parse!("Sat 08 2016"), date!(2016-059)); + assert_eq!(parse!("Sun 09 2016"), date!(2016-060)); + assert_eq!(parse!("Mon 09 2016"), date!(2016-061)); + assert_eq!(parse!("Tue 09 2016"), date!(2016-062)); + assert_eq!(parse!("Wed 09 2016"), date!(2016-063)); + assert_eq!(parse!("Thu 09 2016"), date!(2016-064)); + assert_eq!(parse!("Fri 09 2016"), date!(2016-065)); + assert_eq!(parse!("Sat 09 2016"), date!(2016-066)); + assert_eq!(parse!("Sun 10 2016"), date!(2016-067)); // ED - assert_dwy!(Wed 00 2032 => 001); - assert_dwy!(Thu 00 2032 => 002); - assert_dwy!(Fri 00 2032 => 003); - assert_dwy!(Sat 00 2032 => 004); - assert_dwy!(Sun 01 2032 => 005); - assert_dwy!(Mon 01 2032 => 006); - assert_dwy!(Tue 01 2032 => 007); - assert_dwy!(Fri 08 2032 => 059); - assert_dwy!(Sat 08 2032 => 060); - assert_dwy!(Sun 09 2032 => 061); - assert_dwy!(Mon 09 2032 => 062); - assert_dwy!(Tue 09 2032 => 063); - assert_dwy!(Wed 09 2032 => 064); - assert_dwy!(Thu 09 2032 => 065); - assert_dwy!(Fri 09 2032 => 066); - assert_dwy!(Sat 09 2032 => 067); + assert_eq!(parse!("Wed 00 2032"), date!(2032-001)); + assert_eq!(parse!("Thu 00 2032"), date!(2032-002)); + assert_eq!(parse!("Fri 00 2032"), date!(2032-003)); + assert_eq!(parse!("Sat 00 2032"), date!(2032-004)); + assert_eq!(parse!("Sun 01 2032"), date!(2032-005)); + assert_eq!(parse!("Mon 01 2032"), date!(2032-006)); + assert_eq!(parse!("Tue 01 2032"), date!(2032-007)); + assert_eq!(parse!("Fri 08 2032"), date!(2032-059)); + assert_eq!(parse!("Sat 08 2032"), date!(2032-060)); + assert_eq!(parse!("Sun 09 2032"), date!(2032-061)); + assert_eq!(parse!("Mon 09 2032"), date!(2032-062)); + assert_eq!(parse!("Tue 09 2032"), date!(2032-063)); + assert_eq!(parse!("Wed 09 2032"), date!(2032-064)); + assert_eq!(parse!("Thu 09 2032"), date!(2032-065)); + assert_eq!(parse!("Fri 09 2032"), date!(2032-066)); + assert_eq!(parse!("Sat 09 2032"), date!(2032-067)); // FE - assert_dwy!(Tue 00 2020 => 001); - assert_dwy!(Wed 00 2020 => 002); - assert_dwy!(Thu 00 2020 => 003); - assert_dwy!(Fri 00 2020 => 004); - assert_dwy!(Sat 00 2020 => 005); - assert_dwy!(Sun 01 2020 => 006); - assert_dwy!(Mon 01 2020 => 007); - assert_dwy!(Thu 08 2020 => 059); - assert_dwy!(Fri 08 2020 => 060); - assert_dwy!(Sat 08 2020 => 061); - assert_dwy!(Sun 09 2020 => 062); - assert_dwy!(Mon 09 2020 => 063); - assert_dwy!(Tue 09 2020 => 064); - assert_dwy!(Wed 09 2020 => 065); - assert_dwy!(Thu 09 2020 => 066); - assert_dwy!(Fri 09 2020 => 067); + assert_eq!(parse!("Tue 00 2020"), date!(2020-001)); + assert_eq!(parse!("Wed 00 2020"), date!(2020-002)); + assert_eq!(parse!("Thu 00 2020"), date!(2020-003)); + assert_eq!(parse!("Fri 00 2020"), date!(2020-004)); + assert_eq!(parse!("Sat 00 2020"), date!(2020-005)); + assert_eq!(parse!("Sun 01 2020"), date!(2020-006)); + assert_eq!(parse!("Mon 01 2020"), date!(2020-007)); + assert_eq!(parse!("Thu 08 2020"), date!(2020-059)); + assert_eq!(parse!("Fri 08 2020"), date!(2020-060)); + assert_eq!(parse!("Sat 08 2020"), date!(2020-061)); + assert_eq!(parse!("Sun 09 2020"), date!(2020-062)); + assert_eq!(parse!("Mon 09 2020"), date!(2020-063)); + assert_eq!(parse!("Tue 09 2020"), date!(2020-064)); + assert_eq!(parse!("Wed 09 2020"), date!(2020-065)); + assert_eq!(parse!("Thu 09 2020"), date!(2020-066)); + assert_eq!(parse!("Fri 09 2020"), date!(2020-067)); // GF - assert_dwy!(Mon 00 2036 => 001); - assert_dwy!(Tue 00 2036 => 002); - assert_dwy!(Wed 00 2036 => 003); - assert_dwy!(Thu 00 2036 => 004); - assert_dwy!(Fri 00 2036 => 005); - assert_dwy!(Sat 00 2036 => 006); - assert_dwy!(Sun 01 2036 => 007); - assert_dwy!(Wed 08 2036 => 059); - assert_dwy!(Thu 08 2036 => 060); - assert_dwy!(Fri 08 2036 => 061); - assert_dwy!(Sat 08 2036 => 062); - assert_dwy!(Sun 09 2036 => 063); - assert_dwy!(Mon 09 2036 => 064); - assert_dwy!(Tue 09 2036 => 065); - assert_dwy!(Wed 09 2036 => 066); - assert_dwy!(Thu 09 2036 => 067); + assert_eq!(parse!("Mon 00 2036"), date!(2036-001)); + assert_eq!(parse!("Tue 00 2036"), date!(2036-002)); + assert_eq!(parse!("Wed 00 2036"), date!(2036-003)); + assert_eq!(parse!("Thu 00 2036"), date!(2036-004)); + assert_eq!(parse!("Fri 00 2036"), date!(2036-005)); + assert_eq!(parse!("Sat 00 2036"), date!(2036-006)); + assert_eq!(parse!("Sun 01 2036"), date!(2036-007)); + assert_eq!(parse!("Wed 08 2036"), date!(2036-059)); + assert_eq!(parse!("Thu 08 2036"), date!(2036-060)); + assert_eq!(parse!("Fri 08 2036"), date!(2036-061)); + assert_eq!(parse!("Sat 08 2036"), date!(2036-062)); + assert_eq!(parse!("Sun 09 2036"), date!(2036-063)); + assert_eq!(parse!("Mon 09 2036"), date!(2036-064)); + assert_eq!(parse!("Tue 09 2036"), date!(2036-065)); + assert_eq!(parse!("Wed 09 2036"), date!(2036-066)); + assert_eq!(parse!("Thu 09 2036"), date!(2036-067)); } #[test] @@ -2028,7 +2004,7 @@ mod test { } #[test] - fn as_wo() { + fn as_yo() { assert_eq!(date!(2019-01-01).as_yo(), (2019, 1)); } diff --git a/src/duration.rs b/src/duration.rs index ec569ca7a..bb3a4d507 100644 --- a/src/duration.rs +++ b/src/duration.rs @@ -1,4 +1,6 @@ -#[cfg(feature = "std")] +#[allow(unused_imports)] +use crate::shim::*; +#[cfg(not(feature = "alloc"))] use crate::Instant; use crate::{ ConversionRangeError, @@ -235,7 +237,8 @@ impl Duration { /// assert_eq!(Duration::seconds(-1).abs(), Duration::seconds(1)); /// ``` #[inline(always)] - pub const fn abs(self) -> Self { + #[rustversion::attr(since(1.39), const)] + pub fn abs(self) -> Self { Self { seconds: self.seconds.abs(), nanoseconds: self.nanoseconds.abs(), @@ -244,7 +247,7 @@ impl Duration { /// Convert the existing `Duration` to a `std::time::Duration` and its sign. #[inline(always)] - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] pub(crate) fn sign_abs_std(self) -> (Sign, StdDuration) { ( self.sign(), @@ -746,8 +749,8 @@ impl Duration { /// 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. #[inline(always)] - #[cfg(feature = "std")] - #[cfg_attr(doc, doc(cfg(feature = "std")))] + #[cfg(not(feature = "alloc"))] + #[cfg_attr(doc, doc(cfg(not(feature = "alloc"))))] pub fn time_fn(f: impl FnOnce() -> T) -> (Self, T) { let start = Instant::now(); let return_value = f(); @@ -849,7 +852,7 @@ impl Duration { } #[inline(always)] - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] #[deprecated(since = "0.2.0", note = "Use the `time_fn` function")] pub fn span(f: F) -> Self { Self::time_fn(f).0 @@ -885,11 +888,11 @@ impl TryFrom for Duration { original .as_secs() .try_into() - .map_err(|_| ConversionRangeError)?, + .map_err(|_| ConversionRangeError::new())?, original .subsec_nanos() .try_into() - .map_err(|_| ConversionRangeError)?, + .map_err(|_| ConversionRangeError::new())?, )) } } @@ -903,11 +906,11 @@ impl TryFrom for StdDuration { duration .seconds .try_into() - .map_err(|_| ConversionRangeError)?, + .map_err(|_| ConversionRangeError::new())?, duration .nanoseconds .try_into() - .map_err(|_| ConversionRangeError)?, + .map_err(|_| ConversionRangeError::new())?, )) } } @@ -1556,7 +1559,7 @@ mod test { } #[test] - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] fn time_fn() { let (time, value) = Duration::time_fn(|| { std::thread::sleep(100.std_milliseconds()); @@ -1699,7 +1702,7 @@ mod test { duration -= 500.milliseconds(); assert_eq!(duration, 1.seconds()); - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] { let mut duration = 1.std_seconds(); assert_panics!(duration -= 2.seconds()); diff --git a/src/error.rs b/src/error.rs index 819f44608..2c411e740 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,7 +1,9 @@ +#[cfg(feature = "alloc")] +use crate::alloc_prelude::*; use crate::format::ParseError; -#[cfg(not(feature = "std"))] -use crate::no_std_prelude::*; use core::fmt; +#[cfg(not(feature = "alloc"))] +use std::boxed::Box; /// A unified error type for anything returned by a method in the time crate. /// @@ -10,7 +12,11 @@ use core::fmt; // Boxing the `ComponentRangeError` reduces the size of `Error` from 72 bytes to // 16. #[allow(clippy::missing_docs_in_private_items)] // variants only -#[non_exhaustive] +#[rustversion::attr(since(1.40), non_exhaustive)] +#[rustversion::attr( + before(1.40), + doc("This enum is non-exhaustive. Additional variants may be added at any time.") +)] #[derive(Debug, Clone, PartialEq, Eq)] pub enum Error { ConversionRange(ConversionRangeError), @@ -22,21 +28,36 @@ impl fmt::Display for Error { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::ConversionRange(e) => e.fmt(f), - Self::ComponentRange(e) => e.fmt(f), - Self::Parse(e) => e.fmt(f), + Error::ConversionRange(e) => e.fmt(f), + Error::ComponentRange(e) => e.fmt(f), + Error::Parse(e) => e.fmt(f), } } } -#[cfg(feature = "std")] +#[cfg(not(feature = "alloc"))] impl std::error::Error for Error {} /// An error type indicating that a conversion failed because the target type /// could not store the initial value. -#[non_exhaustive] +#[allow(clippy::missing_docs_in_private_items)] +#[rustversion::attr(since(1.40), non_exhaustive)] +#[rustversion::attr( + before(1.40), + doc("This struct is non-exhaustive. Additional variants may be added at any time.") +)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct ConversionRangeError; +pub struct ConversionRangeError { + #[allow(clippy::missing_docs_in_private_items)] + nonexhaustive: (), +} + +impl ConversionRangeError { + #[allow(clippy::missing_docs_in_private_items)] + pub(crate) const fn new() -> Self { + Self { nonexhaustive: () } + } +} impl fmt::Display for ConversionRangeError { #[inline(always)] @@ -45,13 +66,13 @@ impl fmt::Display for ConversionRangeError { } } -#[cfg(feature = "std")] +#[cfg(not(feature = "alloc"))] impl std::error::Error for ConversionRangeError {} impl From for Error { #[inline(always)] fn from(original: ConversionRangeError) -> Self { - Self::ConversionRange(original) + Error::ConversionRange(original) } } @@ -59,9 +80,12 @@ impl From for Error { /// range, causing a failure. // i64 is the narrowest type fitting all use cases. This eliminates the need // for a type parameter. -#[allow(missing_copy_implementations)] // Non-copy fields may be added. -#[non_exhaustive] -#[derive(Debug, Clone, PartialEq, Eq)] +#[rustversion::attr(since(1.40), non_exhaustive)] +#[rustversion::attr( + before(1.40), + doc("This struct is non-exhaustive. Additional fields may be added at any time.") +)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ComponentRangeError { /// Name of the component. pub(crate) name: &'static str, @@ -99,16 +123,16 @@ impl fmt::Display for ComponentRangeError { impl From for Error { #[inline(always)] fn from(original: ComponentRangeError) -> Self { - Self::ComponentRange(Box::new(original)) + Error::ComponentRange(Box::new(original)) } } -#[cfg(feature = "std")] +#[cfg(not(feature = "alloc"))] impl std::error::Error for ComponentRangeError {} impl From for Error { #[inline(always)] fn from(original: ParseError) -> Self { - Self::Parse(original) + Error::Parse(original) } } diff --git a/src/format/date.rs b/src/format/date.rs index 83042ae8b..222e57a97 100644 --- a/src/format/date.rs +++ b/src/format/date.rs @@ -9,9 +9,9 @@ use super::{ }, Padding, ParseError, ParseResult, ParsedItems, }; -#[cfg(not(feature = "std"))] -use crate::no_std_prelude::*; -use crate::{Date, Sign, Weekday}; +#[cfg(feature = "alloc")] +use crate::alloc_prelude::*; +use crate::{shim::*, Date, Sign, Weekday}; use core::{ fmt::{self, Formatter}, num::{NonZeroU16, NonZeroU8}, @@ -141,7 +141,7 @@ pub(crate) fn parse_C(items: &mut ParsedItems, s: &mut &str, padding: Padding) - items.year = (try_consume_digits::(s, (2 - padding_length)..=(3 - padding_length)) .ok_or(ParseError::InvalidYear)? * 100 - + items.year.unwrap_or(0).rem_euclid(100)) + + items.year.unwrap_or(0).rem_euclid_shim(100)) .into(); Ok(()) @@ -166,7 +166,12 @@ pub(crate) fn parse_d(items: &mut ParsedItems, s: &mut &str, padding: Padding) - /// Week-based year, last two digits (`00`-`99`) #[inline(always)] pub(crate) fn fmt_g(f: &mut Formatter<'_>, date: Date, padding: Padding) -> fmt::Result { - pad!(f, padding(Zero), 2, date.iso_year_week().0.rem_euclid(100)) + pad!( + f, + padding(Zero), + 2, + date.iso_year_week().0.rem_euclid_shim(100) + ) } /// Week-based year, last two digits (`00`-`99`) @@ -222,10 +227,10 @@ pub(crate) fn fmt_j(f: &mut Formatter<'_>, date: Date, padding: Padding) -> fmt: /// Day of the year, zero-padded to width 3 (`001`-`366`) #[inline(always)] pub(crate) fn parse_j(items: &mut ParsedItems, s: &mut &str, padding: Padding) -> ParseResult<()> { - items.ordinal_day = - try_consume_exact_digits::(s, 3, padding.default_to(Padding::Zero)) - .ok_or(ParseError::InvalidDayOfYear)? - .into(); + items.ordinal_day = NonZeroU16::new( + try_consume_exact_digits_in_range(s, 3, 1..=366, padding.default_to(Padding::Zero)) + .ok_or(ParseError::InvalidDayOfYear)?, + ); Ok(()) } @@ -239,9 +244,10 @@ pub(crate) fn fmt_m(f: &mut Formatter<'_>, date: Date, padding: Padding) -> fmt: /// Month of the year, zero-padded (`01`-`12`) #[inline(always)] pub(crate) fn parse_m(items: &mut ParsedItems, s: &mut &str, padding: Padding) -> ParseResult<()> { - items.month = try_consume_exact_digits::(s, 2, padding.default_to(Padding::Zero)) - .ok_or(ParseError::InvalidMonth)? - .into(); + items.month = NonZeroU8::new( + try_consume_exact_digits_in_range(s, 2, 1..=12, padding.default_to(Padding::Zero)) + .ok_or(ParseError::InvalidMonth)?, + ); Ok(()) } @@ -343,7 +349,7 @@ pub(crate) fn parse_W(items: &mut ParsedItems, s: &mut &str, padding: Padding) - /// Last two digits of year (`00`-`99`) #[inline(always)] pub(crate) fn fmt_y(f: &mut Formatter<'_>, date: Date, padding: Padding) -> fmt::Result { - pad!(f, padding(Zero), 2, date.year().rem_euclid(100)) + pad!(f, padding(Zero), 2, date.year().rem_euclid_shim(100)) } /// Last two digits of year (`00`-`99`) diff --git a/src/format/mod.rs b/src/format/mod.rs index 120189183..d4e66bc50 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -37,8 +37,8 @@ pub(crate) mod parse; pub(crate) mod parse_items; pub(crate) mod time; -#[cfg(not(feature = "std"))] -use crate::no_std_prelude::*; +#[cfg(feature = "alloc")] +use crate::alloc_prelude::*; use crate::{Date, Time, UtcOffset}; use core::fmt::{self, Display, Formatter}; #[allow(unreachable_pub)] // rust-lang/rust#64762 @@ -71,7 +71,7 @@ impl Padding { #[inline(always)] pub(crate) fn default_to(self, value: Self) -> Self { match self { - Self::Default => value, + Padding::Default => value, _ => self, } } diff --git a/src/format/parse.rs b/src/format/parse.rs index cbd19dcd9..a490fca57 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -1,22 +1,31 @@ //! Parsing for various types. use super::{parse_fmt_string, FormatItem, Padding, Specifier}; -use crate::{UtcOffset, Weekday}; +use crate::{shim::*, ComponentRangeError, UtcOffset, Weekday}; use core::{ fmt::{self, Display, Formatter}, num::{NonZeroU16, NonZeroU8}, ops::{Bound, RangeBounds}, str::FromStr, }; -#[cfg(feature = "std")] +#[cfg(not(feature = "alloc"))] use std::error::Error; +#[cfg(feature = "alloc")] +use alloc::boxed::Box; +#[cfg(not(feature = "alloc"))] +use std::boxed::Box; + /// Helper type to avoid repeating the error type. pub(crate) type ParseResult = Result; /// An error ocurred while parsing. -#[non_exhaustive] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[rustversion::attr(since(1.40), non_exhaustive)] +#[rustversion::attr( + before(1.40), + doc("This enum is non-exhaustive. Additional variants may be added at any time.") +)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ParseError { /// The second present was not valid. InvalidSecond, @@ -55,6 +64,15 @@ pub enum ParseError { UnexpectedEndOfString, /// There was not enough information provided to create the requested type. InsufficientInformation, + /// A component was out of range. + ComponentOutOfRange(Box), +} + +impl From for ParseError { + #[inline(always)] + fn from(error: ComponentRangeError) -> Self { + ParseError::ComponentOutOfRange(Box::new(error)) + } } impl Display for ParseError { @@ -82,11 +100,12 @@ impl Display for ParseError { InsufficientInformation => { f.write_str("insufficient information provided to create the requested type") } + ComponentOutOfRange(e) => write!(f, "{}", e), } } } -#[cfg(feature = "std")] +#[cfg(not(feature = "alloc"))] impl Error for ParseError {} /// A value representing a time that is either "AM" or "PM". @@ -252,7 +271,7 @@ pub(crate) fn try_consume_digits_in_range( num_digits: impl RangeBounds, range: impl RangeBounds, ) -> Option { - try_consume_digits(s, num_digits).filter(|value| range.contains(value)) + try_consume_digits(s, num_digits).filter(|value| range_contains(&range, value)) } /// Attempt to consume an exact number of digits. @@ -291,13 +310,13 @@ pub(crate) fn try_consume_exact_digits( /// Attempt to consume an exact number of digits. Returns `None` if the value is /// not within the allowed range. #[inline] -pub(crate) fn try_consume_exact_digits_in_range( +pub(crate) fn try_consume_exact_digits_in_range>( s: &mut &str, num_digits: usize, - range: impl RangeBounds, + range: U, padding: Padding, ) -> Option { - try_consume_exact_digits(s, num_digits, padding).filter(|value| range.contains(value)) + try_consume_exact_digits(s, num_digits, padding).filter(|value| range_contains(&range, value)) } /// Consume all leading padding up to the number of characters. diff --git a/src/format/parse_items.rs b/src/format/parse_items.rs index 94c283331..3386b7d2d 100644 --- a/src/format/parse_items.rs +++ b/src/format/parse_items.rs @@ -1,8 +1,8 @@ //! Parse formats used in the `format` and `parse` methods. +#[cfg(feature = "alloc")] +use crate::alloc_prelude::*; use crate::format::{FormatItem, Padding, Specifier}; -#[cfg(not(feature = "std"))] -use crate::no_std_prelude::*; /// Parse the formatting string. Panics if not valid. #[inline(always)] diff --git a/src/format/time.rs b/src/format/time.rs index b55fbe589..6449b0135 100644 --- a/src/format/time.rs +++ b/src/format/time.rs @@ -10,6 +10,7 @@ use crate::{ }, Padding, ParseError, ParseResult, ParsedItems, }, + shim::*, Time, }; use core::{ @@ -40,7 +41,7 @@ pub(crate) fn fmt_I(f: &mut Formatter<'_>, time: Time, padding: Padding) -> fmt: f, padding(Zero), 2, - (time.hour() as i8 - 1).rem_euclid(12) + 1 + (time.hour() as i8 - 1).rem_euclid_shim(12) + 1 ) } diff --git a/src/instant.rs b/src/instant.rs index 29edb57b9..583ddae9d 100644 --- a/src/instant.rs +++ b/src/instant.rs @@ -30,7 +30,7 @@ use std::time::Instant as StdInstant; /// /// This implementation allows for operations with signed [`Duration`]s, but is /// otherwise identical to [`std::time::Instant`]. -#[cfg_attr(doc, doc(cfg(feature = "std")))] +#[cfg_attr(doc, doc(cfg(not(feature = "alloc"))))] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Instant { /// Inner representation, using `std::time::Instant`. diff --git a/src/lib.rs b/src/lib.rs index 92a22b4cc..adf664dfe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,18 +1,20 @@ //! Simple time handling. //! -//! ![rustc 1.40.0](https://img.shields.io/badge/rustc-1.40.0-blue) +//! ![rustc 1.34.0](https://img.shields.io/badge/rustc-1.34.0-blue) //! //! # Feature flags in Cargo //! -//! ## `std` +//! ## `alloc` //! //! Currently, all structs except `Instant` can be used with `#![no_std]`. As //! support for the standard library is enabled by default, you muse use -//! `default_features = false` in your `Cargo.toml` to enable this. +//! the `alloc` feature to enable `#![no_std]` support. As time relies on an +//! allocator for some functionality, a global allocator must be present. This +//! inherently requires a greater minimum supported Rust version of 1.36.0. //! //! ```toml //! [dependencies] -//! time = { version = "0.2", default-features = false } +//! time = { version = "0.2", features = ["alloc"] } //! ``` //! //! Of the structs that are usable, some methods may only be enabled due a @@ -24,16 +26,9 @@ //! To enable it, use the `serde` feature. This is not enabled by default. It //! _is_ compatible with `#![no_std]`, so long as an allocator is present. //! -//! With the standard library: -//! ```toml -//! [dependencies] -//! time = { version = "0.2", features = ["serde"] } -//! ``` -//! -//! With `#![no_std]` support: //! ```toml //! [dependencies] -//! time = { version = "0.2", default-features = false, features = ["serde"] } +//! time = { version = "0.2", features = ["alloc", "serde"] } //! ``` //! //! ## `deprecated` @@ -41,12 +36,11 @@ //! Using the `deprecated` feature allows using deprecated v0.1 methods. Enabled //! by default. //! -//! With the standard library, the normal `time = 0.2` will work as expected. +//! To _disable_ this feature: //! -//! With `#![no_std]` support: //! ```toml //! [dependencies] -//! time = { version = "0.2", default-features = false, features = ["deprecated"] } +//! time = { version = "0.2", default-features = false } //! ``` //! //! ## `panicking-api` @@ -58,6 +52,9 @@ //! //! Library authors should avoid using this feature. //! +//! This feature will be removed in a future release, as there are provided +//! macros to perform the equivalent calculations at compile-time. +//! //! ```toml //! [dependencies] //! time = { version = "0.2", features = ["panicking-api"] } @@ -119,7 +116,7 @@ //! | `0` | Pad with zeros | `%0d` => `05` | #![cfg_attr(doc, feature(doc_cfg))] -#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(feature = "alloc", no_std)] #![forbid(unsafe_code)] #![deny( anonymous_parameters, @@ -157,7 +154,7 @@ clippy::cast_possible_wrap, clippy::cast_lossless, clippy::module_name_repetitions, - clippy::must_use_candidate // rust-lang/rust-clippy#4779 + clippy::must_use_candidate )] #![cfg_attr(test, allow(clippy::cognitive_complexity, clippy::too_many_lines))] #![doc(html_favicon_url = "https://avatars0.githubusercontent.com/u/55999857")] @@ -168,6 +165,17 @@ // Unfortunately, this also means we can't have a `time` mod. extern crate self as time; +#[rustversion::before(1.34.0)] +compile_error!("The time crate has a minimum supported rust version of 1.34.0."); + +#[cfg(feature = "alloc")] +#[rustversion::before(1.36.0)] +compile_error!( + "Using the time crate without the standard library enabled requires a global allocator. This \ + was stabilized in Rust 1.36.0. You can either upgrade or enable the standard library." +); + +#[cfg(feature = "alloc")] #[macro_use] extern crate alloc; @@ -191,26 +199,32 @@ macro_rules! format_conditional { #[cfg_attr(doc, doc(cfg(feature = "panicking-api")))] macro_rules! assert_value_in_range { ($value:ident in $start:expr => $end:expr) => { - if !($start..=$end).contains(&$value) { - panic!( - concat!(stringify!($value), " must be in the range {}..={} (was {})"), - $start, - $end, - $value, - ); + #[allow(unused_comparisons)] + { + if $value < $start || $value > $end { + panic!( + concat!(stringify!($value), " must be in the range {}..={} (was {})"), + $start, + $end, + $value, + ); + } } }; ($value:ident in $start:expr => $end:expr, given $($conditional:ident),+ $(,)?) => { - if !($start..=$end).contains(&$value) { - panic!( - concat!(stringify!($value), " must be in the range {}..={} given{} (was {})"), - $start, - $end, - &format_conditional!($($conditional),+), - $value, - ); - }; + #[allow(unused_comparisons)] + { + if $value < $start || $value > $end { + panic!( + concat!(stringify!($value), " must be in the range {}..={} given{} (was {})"), + $start, + $end, + &format_conditional!($($conditional),+), + $value, + ); + }; + } }; } @@ -218,31 +232,37 @@ macro_rules! assert_value_in_range { /// Returns `None` if the value is not in range. macro_rules! ensure_value_in_range { ($value:ident in $start:expr => $end:expr) => { - if !($start..=$end).contains(&$value) { - return Err(ComponentRangeError { - name: stringify!($value), - minimum: i64::from($start), - maximum: i64::from($end), - value: i64::from($value), - given: Vec::new(), - }); + #[allow(unused_comparisons)] + { + if $value < $start || $value > $end { + return Err(ComponentRangeError { + name: stringify!($value), + minimum: i64::from($start), + maximum: i64::from($end), + value: i64::from($value), + given: Vec::new(), + }); + } } }; ($value:ident in $start:expr => $end:expr, given $($conditional:ident),+ $(,)?) => { - if !($start..=$end).contains(&$value) { - return Err(ComponentRangeError { - name: stringify!($value), - minimum: i64::from($start), - maximum: i64::from($end), - value: i64::from($value), - given: vec![$((stringify!($conditional), i64::from($conditional))),+], - }); - }; + #[allow(unused_comparisons)] + { + if $value < $start || $value > $end { + return Err(ComponentRangeError { + name: stringify!($value), + minimum: i64::from($start), + maximum: i64::from($end), + value: i64::from($value), + given: vec![$((stringify!($conditional), i64::from($conditional))),+], + }); + }; + } }; } -#[cfg(all(test, feature = "std"))] +#[cfg(all(test, not(feature = "alloc")))] macro_rules! assert_panics { ($e:expr $(, $message:literal)?) => { #[allow(box_pointers)] @@ -267,7 +287,7 @@ mod duration; mod error; mod format; /// The `Instant` struct and its associated `impl`s. -#[cfg(feature = "std")] +#[cfg(not(feature = "alloc"))] mod instant; pub mod internals; /// A collection of traits extending built-in numerical types. @@ -277,7 +297,10 @@ mod offset_date_time; /// The `PrimitiveDateTime` struct and its associated `impl`s. mod primitive_date_time; #[cfg(feature = "serde")] +#[allow(missing_copy_implementations, missing_debug_implementations)] mod serde; +/// Shims to provide functionality on older versions of rustc. +mod shim; /// The `Sign` struct and its associated `impl`s. mod sign; /// The `Time` struct and its associated `impl`s. @@ -293,7 +316,7 @@ pub use error::{ComponentRangeError, ConversionRangeError, Error}; pub(crate) use format::DeferredFormat; use format::ParseResult; pub use format::{validate_format_string, ParseError}; -#[cfg(feature = "std")] +#[cfg(not(feature = "alloc"))] pub use instant::Instant; pub use numerical_traits::{NumericalDuration, NumericalStdDuration, NumericalStdDurationShort}; pub use offset_date_time::OffsetDateTime; @@ -415,8 +438,8 @@ pub mod prelude { /// A stable alternative to [`alloc::v1::prelude`](https://doc.rust-lang.org/stable/alloc/prelude/v1/index.html). /// Useful anywhere `#![no_std]` is allowed. -#[cfg(not(feature = "std"))] -mod no_std_prelude { +#[cfg(feature = "alloc")] +mod alloc_prelude { #![allow(unused_imports)] pub(crate) use alloc::{ borrow::ToOwned, @@ -426,6 +449,7 @@ mod no_std_prelude { }; } +#[allow(clippy::missing_docs_in_private_items)] mod private { use super::*; @@ -474,19 +498,19 @@ pub fn parse(s: &str, format: &str) -> ParseResult { // For some back-compatibility, we're also implementing some deprecated types // and methods. They will be removed completely in 0.3. -#[cfg(all(feature = "std", feature = "deprecated"))] +#[cfg(all(not(feature = "alloc"), feature = "deprecated"))] #[cfg_attr(tarpaulin, skip)] #[allow(clippy::missing_docs_in_private_items)] #[deprecated(since = "0.2.0", note = "Use `Instant`")] pub type PreciseTime = Instant; -#[cfg(all(feature = "std", feature = "deprecated"))] +#[cfg(all(not(feature = "alloc"), feature = "deprecated"))] #[cfg_attr(tarpaulin, skip)] #[allow(clippy::missing_docs_in_private_items)] #[deprecated(since = "0.2.0", note = "Use `Instant`")] pub type SteadyTime = Instant; -#[cfg(all(feature = "std", feature = "deprecated"))] +#[cfg(all(not(feature = "alloc"), feature = "deprecated"))] #[cfg_attr(tarpaulin, skip)] #[allow(clippy::missing_docs_in_private_items)] #[deprecated( @@ -503,7 +527,7 @@ pub fn precise_time_ns() -> u64 { .expect("You really shouldn't be using this in the year 2554...") } -#[cfg(all(feature = "std", feature = "deprecated"))] +#[cfg(all(not(feature = "alloc"), feature = "deprecated"))] #[cfg_attr(tarpaulin, skip)] #[allow(clippy::missing_docs_in_private_items)] #[deprecated( diff --git a/src/offset_date_time.rs b/src/offset_date_time.rs index 3451b751b..207ca8084 100644 --- a/src/offset_date_time.rs +++ b/src/offset_date_time.rs @@ -1,5 +1,5 @@ -#[cfg(not(feature = "std"))] -use crate::no_std_prelude::*; +#[cfg(feature = "alloc")] +use crate::alloc_prelude::*; use crate::{ format::parse::{parse, ParseResult, ParsedItems}, offset, Date, DeferredFormat, Duration, PrimitiveDateTime, Time, UtcOffset, Weekday, @@ -40,8 +40,8 @@ impl OffsetDateTime { /// assert_eq!(OffsetDateTime::now().offset(), offset!(UTC)); /// ``` #[inline(always)] - #[cfg(feature = "std")] - #[cfg_attr(doc, doc(cfg(feature = "std")))] + #[cfg(not(feature = "alloc"))] + #[cfg_attr(doc, doc(cfg(not(feature = "alloc"))))] pub fn now() -> Self { PrimitiveDateTime::now().using_offset(offset!(UTC)) } @@ -777,7 +777,7 @@ mod test { use crate::{date, prelude::*, time}; #[test] - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] fn now() { assert!(OffsetDateTime::now().year() >= 2019); assert_eq!(OffsetDateTime::now().offset(), offset!(UTC)); @@ -1204,7 +1204,7 @@ mod test { } #[test] - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] fn hash() { use std::{collections::hash_map::DefaultHasher, hash::Hash}; diff --git a/src/primitive_date_time.rs b/src/primitive_date_time.rs index 032a9cfd5..4b843b28a 100644 --- a/src/primitive_date_time.rs +++ b/src/primitive_date_time.rs @@ -1,19 +1,19 @@ -#[cfg(not(feature = "std"))] -use crate::no_std_prelude::*; -#[cfg(feature = "std")] +#[cfg(feature = "alloc")] +use crate::alloc_prelude::*; +#[cfg(not(feature = "alloc"))] use crate::Sign; use crate::{ format::parse::{parse, ParseResult, ParsedItems}, time, Date, DeferredFormat, Duration, OffsetDateTime, Time, UtcOffset, Weekday, }; -#[cfg(feature = "std")] +#[cfg(not(feature = "alloc"))] use core::convert::{From, TryFrom}; use core::{ cmp::Ordering, ops::{Add, AddAssign, Sub, SubAssign}, time::Duration as StdDuration, }; -#[cfg(feature = "std")] +#[cfg(not(feature = "alloc"))] use std::time::SystemTime; /// Combined date and time. @@ -55,8 +55,8 @@ impl PrimitiveDateTime { /// assert!(PrimitiveDateTime::now().year() >= 2019); /// ``` #[inline(always)] - #[cfg(feature = "std")] - #[cfg_attr(doc, doc(cfg(feature = "std")))] + #[cfg(not(feature = "alloc"))] + #[cfg_attr(doc, doc(cfg(not(feature = "alloc"))))] pub fn now() -> Self { SystemTime::now().into() } @@ -481,7 +481,7 @@ impl Add for PrimitiveDateTime { } } -#[cfg(feature = "std")] +#[cfg(not(feature = "alloc"))] impl Add for SystemTime { type Output = Self; @@ -528,7 +528,7 @@ impl AddAssign for PrimitiveDateTime { } } -#[cfg(feature = "std")] +#[cfg(not(feature = "alloc"))] impl AddAssign for SystemTime { #[inline(always)] fn add_assign(&mut self, duration: Duration) { @@ -564,7 +564,7 @@ impl Sub for PrimitiveDateTime { } } -#[cfg(feature = "std")] +#[cfg(not(feature = "alloc"))] impl Sub for SystemTime { type Output = Self; @@ -588,7 +588,7 @@ impl SubAssign for PrimitiveDateTime { } } -#[cfg(feature = "std")] +#[cfg(not(feature = "alloc"))] impl SubAssign for SystemTime { #[inline(always)] fn sub_assign(&mut self, duration: Duration) { @@ -605,7 +605,7 @@ impl Sub for PrimitiveDateTime { } } -#[cfg(feature = "std")] +#[cfg(not(feature = "alloc"))] impl Sub for PrimitiveDateTime { type Output = Duration; @@ -615,7 +615,7 @@ impl Sub for PrimitiveDateTime { } } -#[cfg(feature = "std")] +#[cfg(not(feature = "alloc"))] impl Sub for SystemTime { type Output = Duration; @@ -632,7 +632,7 @@ impl PartialOrd for PrimitiveDateTime { } } -#[cfg(feature = "std")] +#[cfg(not(feature = "alloc"))] impl PartialEq for PrimitiveDateTime { #[inline(always)] fn eq(&self, rhs: &SystemTime) -> bool { @@ -640,7 +640,7 @@ impl PartialEq for PrimitiveDateTime { } } -#[cfg(feature = "std")] +#[cfg(not(feature = "alloc"))] impl PartialEq for SystemTime { #[inline(always)] fn eq(&self, rhs: &PrimitiveDateTime) -> bool { @@ -648,7 +648,7 @@ impl PartialEq for SystemTime { } } -#[cfg(feature = "std")] +#[cfg(not(feature = "alloc"))] impl PartialOrd for PrimitiveDateTime { #[inline(always)] fn partial_cmp(&self, other: &SystemTime) -> Option { @@ -656,7 +656,7 @@ impl PartialOrd for PrimitiveDateTime { } } -#[cfg(feature = "std")] +#[cfg(not(feature = "alloc"))] impl PartialOrd for SystemTime { #[inline(always)] fn partial_cmp(&self, other: &PrimitiveDateTime) -> Option { @@ -683,7 +683,7 @@ impl Ord for PrimitiveDateTime { } } -#[cfg(feature = "std")] +#[cfg(not(feature = "alloc"))] impl From for PrimitiveDateTime { // There is definitely some way to have this conversion be infallible, but // it won't be an issue for over 500 years. @@ -700,7 +700,7 @@ impl From for PrimitiveDateTime { } } -#[cfg(feature = "std")] +#[cfg(not(feature = "alloc"))] #[allow(clippy::fallible_impl_from)] impl From for SystemTime { #[inline] @@ -731,7 +731,7 @@ mod test { } #[test] - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] fn now() { assert!(PrimitiveDateTime::now().year() >= 2019); } @@ -956,7 +956,7 @@ mod test { } #[test] - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] fn std_add_duration() { assert_eq!( SystemTime::from(date!(2019-01-01).midnight()) + 5.days(), @@ -1027,7 +1027,7 @@ mod test { } #[test] - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] fn std_add_assign_duration() { let mut ny19 = SystemTime::from(date!(2019-01-01).midnight()); ny19 += 5.days(); @@ -1087,7 +1087,7 @@ mod test { } #[test] - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] fn std_sub_duration() { assert_eq!( SystemTime::from(date!(2019-01-06).midnight()) - 5.days(), @@ -1142,7 +1142,7 @@ mod test { } #[test] - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] fn std_sub_assign_duration() { let mut ny19 = SystemTime::from(date!(2019-01-06).midnight()); ny19 -= 5.days(); @@ -1182,7 +1182,7 @@ mod test { } #[test] - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] fn std_sub_datetime() { assert_eq!( SystemTime::from(date!(2019-01-02).midnight()) - date!(2019-01-01).midnight(), @@ -1203,7 +1203,7 @@ mod test { } #[test] - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] fn sub_std() { assert_eq!( date!(2019-01-02).midnight() - SystemTime::from(date!(2019-01-01).midnight()), @@ -1319,7 +1319,7 @@ mod test { } #[test] - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] fn eq_std() { let now_datetime = PrimitiveDateTime::now(); let now_systemtime = SystemTime::from(now_datetime); @@ -1327,16 +1327,16 @@ mod test { } #[test] - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] fn std_eq() { - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] let now_datetime = PrimitiveDateTime::now(); let now_systemtime = SystemTime::from(now_datetime); assert_eq!(now_datetime, now_systemtime); } #[test] - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] fn ord_std() { assert_eq!( date!(2019-01-01).midnight(), @@ -1383,7 +1383,7 @@ mod test { } #[test] - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] fn std_ord() { assert_eq!( SystemTime::from(date!(2019-01-01).midnight()), @@ -1430,7 +1430,7 @@ mod test { } #[test] - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] fn from_std() { assert_eq!( PrimitiveDateTime::from(SystemTime::UNIX_EPOCH), @@ -1439,7 +1439,7 @@ mod test { } #[test] - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] fn to_std() { assert_eq!( SystemTime::from(PrimitiveDateTime::unix_epoch()), diff --git a/src/serde/sign.rs b/src/serde/sign.rs index 2e056efd7..0f0eb6ea2 100644 --- a/src/serde/sign.rs +++ b/src/serde/sign.rs @@ -23,9 +23,9 @@ impl TryFrom for crate::Sign { #[inline] fn try_from(original: Sign) -> Result { match original { - Sign(1) => Ok(Self::Positive), - Sign(-1) => Ok(Self::Negative), - Sign(0) => Ok(Self::Zero), + Sign(1) => Ok(crate::Sign::Positive), + Sign(-1) => Ok(crate::Sign::Negative), + Sign(0) => Ok(crate::Sign::Zero), _ => Err("invalid value"), } } diff --git a/src/serde/weekday.rs b/src/serde/weekday.rs index 9b45a1fc2..82c14dea5 100644 --- a/src/serde/weekday.rs +++ b/src/serde/weekday.rs @@ -17,13 +17,13 @@ impl TryFrom for crate::Weekday { #[inline] fn try_from(original: Weekday) -> Result { match original { - Weekday(1) => Ok(Self::Monday), - Weekday(2) => Ok(Self::Tuesday), - Weekday(3) => Ok(Self::Wednesday), - Weekday(4) => Ok(Self::Thursday), - Weekday(5) => Ok(Self::Friday), - Weekday(6) => Ok(Self::Saturday), - Weekday(7) => Ok(Self::Sunday), + Weekday(1) => Ok(crate::Weekday::Monday), + Weekday(2) => Ok(crate::Weekday::Tuesday), + Weekday(3) => Ok(crate::Weekday::Wednesday), + Weekday(4) => Ok(crate::Weekday::Thursday), + Weekday(5) => Ok(crate::Weekday::Friday), + Weekday(6) => Ok(crate::Weekday::Saturday), + Weekday(7) => Ok(crate::Weekday::Sunday), _ => Err("invalid value"), } } diff --git a/src/shim.rs b/src/shim.rs new file mode 100644 index 000000000..74488110d --- /dev/null +++ b/src/shim.rs @@ -0,0 +1,81 @@ +#![allow(clippy::missing_docs_in_private_items)] + +use core::{ + ops::{ + Bound::{Excluded, Included, Unbounded}, + RangeBounds, + }, + time::Duration, +}; + +/// Check if a range contains the given value. Equivalent to +/// `range.contains(&item)`, but works on older compilers. +pub(crate) fn range_contains(range: &impl RangeBounds, item: &U) -> bool +where + T: PartialOrd, + U: ?Sized + PartialOrd, +{ + (match range.start_bound() { + Included(start) => start <= item, + Excluded(start) => start < item, + Unbounded => true, + }) && (match range.end_bound() { + Included(end) => item <= end, + Excluded(end) => item < end, + Unbounded => true, + }) +} + +pub(crate) trait EuclidShim { + /// Get the Euclidean remainder. + fn rem_euclid_shim(self, rhs: Self) -> Self; +} + +macro_rules! impl_euclid_shim_signed { + ($($type:ty),* $(,)?) => { + $( + impl EuclidShim for $type { + #[inline] + fn rem_euclid_shim(self, rhs: Self) -> Self { + let r = self % rhs; + if r < 0 { + if rhs < 0 { + r - rhs + } else { + r + rhs + } + } else { + r + } + } + } + )* + }; +} +impl_euclid_shim_signed![i8, i16, i32, i64, i128, isize]; + +macro_rules! impl_euclid_shim_unsigned { + ($($type:ty),* $(,)?) => { + $( + impl EuclidShim for $type { + #[inline] + fn rem_euclid_shim(self, rhs: Self) -> Self { + self % rhs + } + } + )* + }; +} +impl_euclid_shim_unsigned![u8, u16, u32, u64, u128, usize]; + +pub(crate) trait DurationShim { + /// Get the number of seconds in a `Duration` as a 64 bit float. + fn as_secs_f64(&self) -> f64; +} +impl DurationShim for Duration { + #[inline] + #[allow(clippy::cast_precision_loss)] + fn as_secs_f64(&self) -> f64 { + (self.as_secs() as f64) + (self.as_nanos() as f64) / (1_000_000_000.) + } +} diff --git a/src/time_mod.rs b/src/time_mod.rs index 032f4113f..09105e8e3 100644 --- a/src/time_mod.rs +++ b/src/time_mod.rs @@ -1,9 +1,10 @@ -#[cfg(not(feature = "std"))] -use crate::no_std_prelude::*; -#[cfg(feature = "std")] +#[cfg(feature = "alloc")] +use crate::alloc_prelude::*; +#[cfg(not(feature = "alloc"))] use crate::PrimitiveDateTime; use crate::{ format::{parse, parse::AmPm, ParseError, ParseResult, ParsedItems}, + shim::*, ComponentRangeError, DeferredFormat, Duration, }; use core::{ @@ -401,8 +402,8 @@ impl Time { /// println!("{:?}", Time::now()); /// ``` #[inline(always)] - #[cfg(feature = "std")] - #[cfg_attr(doc, doc(cfg(feature = "std")))] + #[cfg(not(feature = "alloc"))] + #[cfg_attr(doc, doc(cfg(not(feature = "alloc"))))] pub fn now() -> Self { PrimitiveDateTime::now().time() } @@ -588,25 +589,21 @@ impl Time { } match items { - items!(hour_24, minute, second) => Ok(Self::try_from_hms(hour_24, minute, second) - .expect("components are checked when parsing")), + items!(hour_24, minute, second) => { + Self::try_from_hms(hour_24, minute, second).map_err(Into::into) + } items!(hour_12, minute, second, am_pm) => { - Ok( - Self::try_from_hms(hour_12_to_24(hour_12, am_pm), minute, second) - .expect("components are checked when parsing"), - ) + Self::try_from_hms(hour_12_to_24(hour_12, am_pm), minute, second) + .map_err(Into::into) } - items!(hour_24, minute) => Ok(Self::try_from_hms(hour_24, minute, 0) - .expect("components are checked when parsing")), + items!(hour_24, minute) => Self::try_from_hms(hour_24, minute, 0).map_err(Into::into), items!(hour_12, minute, am_pm) => { - Ok(Self::try_from_hms(hour_12_to_24(hour_12, am_pm), minute, 0) - .expect("components are checked when parsing")) + Self::try_from_hms(hour_12_to_24(hour_12, am_pm), minute, 0).map_err(Into::into) } - items!(hour_24) => { - Ok(Self::try_from_hms(hour_24, 0, 0).expect("components are checked when parsing")) + items!(hour_24) => Self::try_from_hms(hour_24, 0, 0).map_err(Into::into), + items!(hour_12, am_pm) => { + Self::try_from_hms(hour_12_to_24(hour_12, am_pm), 0, 0).map_err(Into::into) } - items!(hour_12, am_pm) => Ok(Self::try_from_hms(hour_12_to_24(hour_12, am_pm), 0, 0) - .expect("components are checked when parsing")), _ => Err(ParseError::InsufficientInformation), } } @@ -636,7 +633,7 @@ impl Add for Time { self.nanoseconds_since_midnight() + duration .whole_nanoseconds() - .rem_euclid(NANOS_PER_DAY as i128) as u64, + .rem_euclid_shim(NANOS_PER_DAY as i128) as u64, ) } } @@ -890,7 +887,7 @@ mod test { assert_eq!(time.second(), 3); assert_eq!(time.nanosecond(), 0); - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] { assert_panics!(Time::from_hms(24, 0, 0), "24 isn't a valid hour"); assert_panics!(Time::from_hms(0, 60, 0), "60 isn't a valid minute"); @@ -922,7 +919,7 @@ mod test { assert_eq!(time.millisecond(), 4); assert_eq!(time.nanosecond(), 4_000_000); - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] { assert_panics!(Time::from_hms_milli(24, 0, 0, 0), "24 isn't a valid hour"); assert_panics!(Time::from_hms_milli(0, 60, 0, 0), "60 isn't a valid minute"); @@ -960,7 +957,7 @@ mod test { assert_eq!(time.microsecond(), 4); assert_eq!(time.nanosecond(), 4_000); - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] { assert_panics!(Time::from_hms_micro(24, 0, 0, 0), "24 isn't a valid hour"); assert_panics!(Time::from_hms_micro(0, 60, 0, 0), "60 isn't a valid minute"); @@ -997,7 +994,7 @@ mod test { assert_eq!(time.second(), 3); assert_eq!(time.nanosecond(), 4); - #[cfg(feature = "std")] + #[cfg(not(feature = "alloc"))] { assert_panics!(Time::from_hms_nano(24, 0, 0, 0), "24 isn't a valid hour."); assert_panics!(Time::from_hms_nano(0, 60, 0, 0), "60 isn't a valid minute."); diff --git a/src/utc_offset.rs b/src/utc_offset.rs index a4c79b7f5..aba1b0f46 100644 --- a/src/utc_offset.rs +++ b/src/utc_offset.rs @@ -1,5 +1,5 @@ -#[cfg(not(feature = "std"))] -use crate::no_std_prelude::*; +#[cfg(feature = "alloc")] +use crate::alloc_prelude::*; use crate::{ format::{parse, ParseError, ParseResult, ParsedItems}, DeferredFormat, Duration, diff --git a/time-macros-impl/src/date.rs b/time-macros-impl/src/date.rs index d7421a932..cc0bb8945 100644 --- a/time-macros-impl/src/date.rs +++ b/time-macros-impl/src/date.rs @@ -36,9 +36,11 @@ impl Parse for Date { let (year, ordinal) = if input.peek(Ident) { let week = { let week = input.parse::()?; - match week.to_string() { - s if s.starts_with("W") => LitInt::new(&s[1..], week.span()), - _ => return error!(week.span(), "expected week value to start with `W`"), + let week_str = week.to_string(); + if week_str.starts_with("W") { + LitInt::new(&week_str[1..], week.span()) + } else { + return error!(week.span(), "expected week value to start with `W`"); } }; input.parse::()?; @@ -69,7 +71,7 @@ impl Parse for Date { // TODO Replace use `LitInt` extension methods when dtolnay/syn#748 is // resolved. - if !(-100_000..=100_000).contains(&year) { + if year < -100_000 || year > 100_000 { return error!(year_span, "value must be in the range -100_000..=100_000"); } diff --git a/time-macros-impl/src/ext.rs b/time-macros-impl/src/ext.rs index 2e62a41a5..f5aa790ca 100644 --- a/time-macros-impl/src/ext.rs +++ b/time-macros-impl/src/ext.rs @@ -1,3 +1,4 @@ +use crate::shim::*; use proc_macro2::Span; use std::{ fmt::{Debug, Display}, @@ -26,7 +27,7 @@ impl LitIntExtension for LitInt { } fn ensure_in_range(&self, range: impl RangeBounds + Debug) -> Result<()> { - if range.contains(&self.value()?) { + if range_contains(&range, &self.value()?) { Ok(()) } else { error!(self.span(), "value must be in range {:?}", range) diff --git a/time-macros-impl/src/lib.rs b/time-macros-impl/src/lib.rs index 71cee0162..0fd1b4589 100644 --- a/time-macros-impl/src/lib.rs +++ b/time-macros-impl/src/lib.rs @@ -32,7 +32,8 @@ clippy::cast_possible_wrap, clippy::cast_lossless, clippy::module_name_repetitions, - clippy::must_use_candidate + clippy::must_use_candidate, + clippy::use_self, // Some things aren't allowed in older compilers. )] extern crate proc_macro; @@ -64,6 +65,7 @@ mod kw { mod date; mod ext; mod offset; +mod shim; mod time; mod time_crate; diff --git a/time-macros-impl/src/shim.rs b/time-macros-impl/src/shim.rs new file mode 100644 index 000000000..74488110d --- /dev/null +++ b/time-macros-impl/src/shim.rs @@ -0,0 +1,81 @@ +#![allow(clippy::missing_docs_in_private_items)] + +use core::{ + ops::{ + Bound::{Excluded, Included, Unbounded}, + RangeBounds, + }, + time::Duration, +}; + +/// Check if a range contains the given value. Equivalent to +/// `range.contains(&item)`, but works on older compilers. +pub(crate) fn range_contains(range: &impl RangeBounds, item: &U) -> bool +where + T: PartialOrd, + U: ?Sized + PartialOrd, +{ + (match range.start_bound() { + Included(start) => start <= item, + Excluded(start) => start < item, + Unbounded => true, + }) && (match range.end_bound() { + Included(end) => item <= end, + Excluded(end) => item < end, + Unbounded => true, + }) +} + +pub(crate) trait EuclidShim { + /// Get the Euclidean remainder. + fn rem_euclid_shim(self, rhs: Self) -> Self; +} + +macro_rules! impl_euclid_shim_signed { + ($($type:ty),* $(,)?) => { + $( + impl EuclidShim for $type { + #[inline] + fn rem_euclid_shim(self, rhs: Self) -> Self { + let r = self % rhs; + if r < 0 { + if rhs < 0 { + r - rhs + } else { + r + rhs + } + } else { + r + } + } + } + )* + }; +} +impl_euclid_shim_signed![i8, i16, i32, i64, i128, isize]; + +macro_rules! impl_euclid_shim_unsigned { + ($($type:ty),* $(,)?) => { + $( + impl EuclidShim for $type { + #[inline] + fn rem_euclid_shim(self, rhs: Self) -> Self { + self % rhs + } + } + )* + }; +} +impl_euclid_shim_unsigned![u8, u16, u32, u64, u128, usize]; + +pub(crate) trait DurationShim { + /// Get the number of seconds in a `Duration` as a 64 bit float. + fn as_secs_f64(&self) -> f64; +} +impl DurationShim for Duration { + #[inline] + #[allow(clippy::cast_precision_loss)] + fn as_secs_f64(&self) -> f64 { + (self.as_secs() as f64) + (self.as_nanos() as f64) / (1_000_000_000.) + } +} diff --git a/time-macros-impl/src/time.rs b/time-macros-impl/src/time.rs index 0f7aa2ed6..b03f21d25 100644 --- a/time-macros-impl/src/time.rs +++ b/time-macros-impl/src/time.rs @@ -17,16 +17,16 @@ impl Parse for AmPm { use crate::kw::{am, pm, AM, PM}; if input.peek(am) { input.parse::()?; - Ok(Self::Am) + Ok(AmPm::Am) } else if input.peek(AM) { input.parse::()?; - Ok(Self::Am) + Ok(AmPm::Am) } else if input.peek(pm) { input.parse::()?; - Ok(Self::Pm) + Ok(AmPm::Pm) } else if input.peek(PM) { input.parse::()?; - Ok(Self::Pm) + Ok(AmPm::Pm) } else { error!("expected am or pm") } diff --git a/time-macros-impl/src/time_crate/date.rs b/time-macros-impl/src/time_crate/date.rs index 1e033afd0..ac088915c 100644 --- a/time-macros-impl/src/time_crate/date.rs +++ b/time-macros-impl/src/time_crate/date.rs @@ -1,4 +1,5 @@ use super::Weekday::{self, *}; +use crate::shim::*; fn is_leap_year(year: i32) -> bool { (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)) @@ -89,7 +90,7 @@ impl Date { match (day as i32 + (13 * (month as i32 + 1)) / 5 + adjusted_year + adjusted_year / 4 - adjusted_year / 100 + adjusted_year / 400) - .rem_euclid(7) + .rem_euclid_shim(7) { 0 => Saturday, 1 => Sunday,