diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 20db931e1a..59ebb41504 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,7 +37,7 @@ jobs: - run: cargo test --doc --all-features --color=always -- --color=always # later this may be able to be included with the below - # kept seperate for now as the following don't compile on 1.38.0 + # kept seperate for now as the following don't compile on 1.47.0 # * rkyv # * criterion rust_msrv: @@ -49,7 +49,7 @@ jobs: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.38.0 + toolchain: 1.47.0 - uses: Swatinem/rust-cache@v1 # run --lib and --doc to avoid the long running integration tests which are run elsewhere - run: cargo test --lib --features unstable-locales,wasmbind,clock,serde,winapi --color=always -- --color=always diff --git a/Cargo.toml b/Cargo.toml index c308d60a98..17af396392 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,6 @@ __doctest = [] const-validation = [] [dependencies] -num-integer = { version = "0.1.36", default-features = false } serde = { version = "1.0.99", default-features = false, optional = true } pure-rust-locales = { version = "0.5.2", optional = true } criterion = { version = "0.4.0", optional = true } diff --git a/ci/github.sh b/ci/github.sh index a52b5f8efa..cfe8eedf32 100755 --- a/ci/github.sh +++ b/ci/github.sh @@ -29,7 +29,7 @@ meaningful in the github actions feature matrix UI. runv cargo --version - if [[ ${RUST_VERSION:-} != 1.38.0 ]]; then + if [[ ${RUST_VERSION:-} != 1.47.0 ]]; then if [[ ${WASM:-} == yes_wasm ]]; then test_wasm elif [[ ${WASM:-} == wasm_simple ]]; then @@ -51,7 +51,7 @@ meaningful in the github actions feature matrix UI. else test_regular UTC0 fi - elif [[ ${RUST_VERSION:-} == 1.38.0 ]]; then + elif [[ ${RUST_VERSION:-} == 1.47.0 ]]; then test_132 else echo "ERROR: didn't run any tests" diff --git a/clippy.toml b/clippy.toml index 749c3b58a5..7846a3e097 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1 +1 @@ -msrv = "1.38" +msrv = "1.47" diff --git a/src/format/mod.rs b/src/format/mod.rs index cd83c1a3bf..56870b86a6 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -356,6 +356,7 @@ impl ParseError { /// The category of parse error #[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)] +#[allow(clippy::manual_non_exhaustive)] pub enum ParseErrorKind { /// Given field is out of permitted range. OutOfRange, @@ -488,8 +489,8 @@ fn format_inner<'a>( ) }; + use crate::naive::internals::{const_div_floor_i64, const_mod_floor_i64}; use core::fmt::Write; - use num_integer::{div_floor, mod_floor}; match *item { Item::Literal(s) | Item::Space(s) => result.push_str(s), @@ -508,22 +509,25 @@ fn format_inner<'a>( let (width, v) = match *spec { Year => (4, date.map(|d| i64::from(d.year()))), - YearDiv100 => (2, date.map(|d| div_floor(i64::from(d.year()), 100))), - YearMod100 => (2, date.map(|d| mod_floor(i64::from(d.year()), 100))), + YearDiv100 => (2, date.map(|d| const_div_floor_i64(i64::from(d.year()), 100))), + YearMod100 => (2, date.map(|d| const_mod_floor_i64(i64::from(d.year()), 100))), IsoYear => ( 4, - date.map(|d| (d.iso_week().map(|w| i64::from(w.year())))) - .ok_or_else(|| fmt::Error)?, + date.map(|d| (d.iso_week().map(|w| i64::from(w.year())))).ok_or(fmt::Error)?, ), IsoYearDiv100 => ( 2, - date.map(|d| (d.iso_week().map(|w| div_floor(i64::from(w.year()), 100)))) - .ok_or_else(|| fmt::Error)?, + date.map(|d| { + (d.iso_week().map(|w| const_div_floor_i64(i64::from(w.year()), 100))) + }) + .ok_or(fmt::Error)?, ), IsoYearMod100 => ( 2, - date.map(|d| (d.iso_week().map(|w| mod_floor(i64::from(w.year()), 100)))) - .ok_or_else(|| fmt::Error)?, + date.map(|d| { + (d.iso_week().map(|w| const_mod_floor_i64(i64::from(w.year()), 100))) + }) + .ok_or(fmt::Error)?, ), Month => (2, date.map(|d| i64::from(d.month()))), Day => (2, date.map(|d| i64::from(d.day()))), @@ -531,8 +535,7 @@ fn format_inner<'a>( WeekFromMon => (2, date.map(|d| i64::from(week_from_mon(d)))), IsoWeek => ( 2, - date.map(|d| (d.iso_week().map(|w| i64::from(w.week())))) - .ok_or_else(|| fmt::Error)?, + date.map(|d| (d.iso_week().map(|w| i64::from(w.week())))).ok_or(fmt::Error)?, ), NumDaysFromSun => (1, date.map(|d| i64::from(d.weekday().num_days_from_sunday()))), WeekdayFromMon => (1, date.map(|d| i64::from(d.weekday().number_from_monday()))), diff --git a/src/format/parsed.rs b/src/format/parsed.rs index bf07b6abf4..31923732ad 100644 --- a/src/format/parsed.rs +++ b/src/format/parsed.rs @@ -6,8 +6,6 @@ use core::convert::TryFrom; -use num_integer::div_rem; - use super::{ParseResult, IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE}; use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime}; use crate::offset::{FixedOffset, LocalResult, Offset, TimeZone}; @@ -21,6 +19,7 @@ use crate::{DateTime, Datelike, TimeDelta, Timelike, Weekday}; /// - `to_*` methods try to make a concrete date and time value out of set fields. /// It fully checks any remaining out-of-range conditions and inconsistent/impossible fields. #[derive(Clone, PartialEq, Eq, Debug, Default, Hash)] +#[allow(clippy::manual_non_exhaustive)] pub struct Parsed { /// Year. /// @@ -299,7 +298,6 @@ impl Parsed { q: Option, r: Option, ) -> ParseResult> { - dbg!(y, q, r); match (y, q, r) { // if there is no further information, simply return the given full year. // this is a common case, so let's avoid division here. @@ -313,7 +311,7 @@ impl Parsed { if y < 0 { return Err(OUT_OF_RANGE); } - let (q_, r_) = div_rem(y, 100); + let (q_, r_) = (y / 100, y % 100); if q.unwrap_or(q_) == q_ && r.unwrap_or(r_) == r_ { Ok(Some(y)) } else { @@ -345,7 +343,6 @@ impl Parsed { q: Option, r: Option, ) -> ParseResult> { - dbg!(y, q, r); match (y, q, r) { // if there is no further information, simply return the given full year. // this is a common case, so let's avoid division here. @@ -359,7 +356,7 @@ impl Parsed { if y < 0 { return Err(OUT_OF_RANGE); } - let (q_, r_) = div_rem(y, 100); + let (q_, r_) = (y / 100, y % 100); if q.unwrap_or(q_) == q_ && r.unwrap_or(r_) == r_ { Ok(Some(y)) } else { @@ -391,13 +388,11 @@ impl Parsed { let given_isoyear = resolve_year_isoweek(self.isoyear, self.isoyear_div_100, self.isoyear_mod_100)?; - dbg!(given_year, given_isoyear); - // verify the normal year-month-day date. let verify_ymd = |date: NaiveDate| { let year = date.year(); let (year_div_100, year_mod_100) = if year >= 0 { - let (q, r) = div_rem(year, 100); + let (q, r) = (year / 100, year % 100); (Some(q), Some(r)) } else { (None, None) // they should be empty to be consistent @@ -414,22 +409,20 @@ impl Parsed { // verify the ISO week date. let verify_isoweekdate = |date: NaiveDate| { let week = date.iso_week().ok_or(OUT_OF_RANGE)?; - dbg!(week); let isoyear = week.year(); let isoweek = week.week(); let weekday = date.weekday(); - dbg!(isoyear, isoweek, weekday); let (isoyear_div_100, isoyear_mod_100) = if isoyear >= 0 { - let (q, r) = div_rem(isoyear, 100); + let (q, r) = (isoyear / 100, isoyear % 100); (Some(q), Some(r)) } else { (None, None) // they should be empty to be consistent }; Ok(self.isoyear.unwrap_or(isoyear) == isoyear - && dbg!(self.isoyear_div_100.or(isoyear_div_100)) == isoyear_div_100 - && dbg!(self.isoyear_mod_100.or(isoyear_mod_100)) == isoyear_mod_100 - && dbg!(self.isoweek.unwrap_or(isoweek)) == isoweek - && dbg!(self.weekday.unwrap_or(weekday)) == weekday) + && self.isoyear_div_100.or(isoyear_div_100) == isoyear_div_100 + && self.isoyear_mod_100.or(isoyear_mod_100) == isoyear_mod_100 + && self.isoweek.unwrap_or(isoweek) == isoweek + && self.weekday.unwrap_or(weekday) == weekday) }; // verify the ordinal and other (non-ISO) week dates. @@ -448,23 +441,15 @@ impl Parsed { // it is consistent with other given fields. let (verified, parsed_date) = match (given_year, given_isoyear, self) { (Some(year), _, &Parsed { month: Some(month), day: Some(day), .. }) => { - dbg!(year, month, day); // year, month, day let date = NaiveDate::from_ymd_opt(year, month, day).ok_or(OUT_OF_RANGE)?; (verify_isoweekdate(date)? && verify_ordinal(date), date) } (Some(year), _, &Parsed { ordinal: Some(ordinal), .. }) => { - dbg!(year, ordinal); // year, day of the year let date = NaiveDate::from_yo_opt(year, ordinal).ok_or(OUT_OF_RANGE)?; - dbg!(date); - ( - dbg!(verify_ymd(date)) - && dbg!(verify_isoweekdate(date))? - && dbg!(verify_ordinal(date)), - date, - ) + (verify_ymd(date) && verify_isoweekdate(date)? && verify_ordinal(date), date) } ( @@ -472,7 +457,6 @@ impl Parsed { _, &Parsed { week_from_sun: Some(week_from_sun), weekday: Some(weekday), .. }, ) => { - dbg!(year, week_from_sun, weekday); // year, week (starting at 1st Sunday), day of the week let newyear = NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE)?; let firstweek = match newyear.weekday() { @@ -507,7 +491,6 @@ impl Parsed { _, &Parsed { week_from_mon: Some(week_from_mon), weekday: Some(weekday), .. }, ) => { - dbg!(year, week_from_mon, weekday); // year, week (starting at 1st Monday), day of the week let newyear = NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE)?; let firstweek = match newyear.weekday() { @@ -538,7 +521,6 @@ impl Parsed { } (_, Some(isoyear), &Parsed { isoweek: Some(isoweek), weekday: Some(weekday), .. }) => { - dbg!(isoyear, isoweek, weekday); // ISO year, week, day of the week let date = NaiveDate::from_isoywd_opt(isoyear, isoweek, weekday); let date = date.ok_or(OUT_OF_RANGE)?; diff --git a/src/month.rs b/src/month.rs index 4ffc7cd6e7..a620591673 100644 --- a/src/month.rs +++ b/src/month.rs @@ -171,6 +171,26 @@ impl Month { } } + /// a + #[cfg(feature = "const-validation")] + pub const fn try_from_u8_validated(val: u8) -> Self { + match val { + 1 => Month::January, + 2 => Month::February, + 3 => Month::March, + 4 => Month::April, + 5 => Month::May, + 6 => Month::June, + 7 => Month::July, + 8 => Month::August, + 9 => Month::September, + 10 => Month::October, + 11 => Month::November, + 12 => Month::December, + _ => panic!("Invalid month value"), + } + } + /// a pub const fn try_from_u16(val: u16) -> Option { match val { diff --git a/src/naive/date.rs b/src/naive/date.rs index 026cf41b7c..1ab46dc1f7 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -20,14 +20,36 @@ use crate::month::Months; use crate::naive::{IsoWeek, NaiveDateTime, NaiveTime}; use crate::{Datelike, Month, TimeDelta, Weekday}; - -use crate::try_opt; use super::internals::DateImpl; -use super::isoweek; +use crate::{try_from_u64_to_i64, try_opt}; #[cfg(test)] use super::internals; +// forces the cosnt validation +#[cfg(feature = "const-validation")] +#[macro_export] +/// a +macro_rules! ymd { + ($y:expr, $m:expr, $d:expr) => {{ + const _: NaiveDate = NaiveDate::from_ymd_validated($y, $m, $d); + + NaiveDate::from_ymd_validated($y, $m, $d) + }}; +} + +// forces the cosnt validation +#[cfg(feature = "const-validation")] +#[macro_export] +/// a +macro_rules! yo { + ($y:expr, $o:expr) => {{ + const _: NaiveDate = NaiveDate::from_yo_validated($y, $o); + + NaiveDate::from_yo_validated($y, $o) + }}; +} + // MAX_YEAR-12-31 minus 0000-01-01 // = ((MAX_YEAR+1)-01-01 minus 0001-01-01) + (0001-01-01 minus 0000-01-01) - 1 day // = ((MAX_YEAR+1)-01-01 minus 0001-01-01) + 365 days @@ -272,13 +294,17 @@ impl NaiveDate { /// assert!(from_ymd_opt(-400000, 1, 1).is_none()); /// ``` pub const fn from_ymd_opt(year: i16, month: u8, day: u8) -> Option { - Some(NaiveDate { inner: try_opt!(DateImpl::from_ymd(year, try_opt!(Month::try_from_u8(month)), day)) }) + Some(NaiveDate { + inner: try_opt!(DateImpl::from_ymd(year, try_opt!(Month::try_from_u8(month)), day)), + }) } #[cfg(feature = "const-validation")] - /// - pub const fn from_ymd_validated(year: i16, month: Month, day: u8) -> NaiveDate { - NaiveDate { inner:DateImpl::from_ymd_validated(year, month, day) } + /// + pub const fn from_ymd_validated(year: i16, month: u8, day: u8) -> NaiveDate { + NaiveDate { + inner: DateImpl::from_ymd_validated(year, Month::try_from_u8_validated(month), day), + } } /// Makes a new `NaiveDate` from the [ordinal date](#ordinal-date) @@ -310,8 +336,8 @@ impl NaiveDate { /// assert!(from_yo_opt(400000, 1).is_none()); /// assert!(from_yo_opt(-400000, 1).is_none()); /// ``` - pub fn from_yo_opt(year: i16, ordinal: u16) -> Option { - Some(NaiveDate { inner: DateImpl::from_yo(year, ordinal)? }) + pub const fn from_yo_opt(year: i16, ordinal: u16) -> Option { + Some(NaiveDate { inner: try_opt!(DateImpl::from_yo(year, ordinal)) }) } /// Makes a new `NaiveDate` from the [ISO week date](#week-date) @@ -368,8 +394,8 @@ impl NaiveDate { /// assert_eq!(from_isoywd_opt(2015, 54, Weekday::Mon), None); /// assert_eq!(from_isoywd_opt(2016, 1, Weekday::Mon), Some(from_ymd(2016, 1, 4))); /// ``` - pub fn from_isoywd_opt(year: i32, week: u16, weekday: Weekday) -> Option { - Some(NaiveDate { inner: DateImpl::from_isoywd_opt(year, week, weekday)? }) + pub const fn from_isoywd_opt(year: i32, week: u16, weekday: Weekday) -> Option { + Some(NaiveDate { inner: try_opt!(DateImpl::from_isoywd_opt(year, week, weekday)) }) } /// Makes a new `NaiveDate` from a day's number in the proleptic Gregorian calendar, with @@ -402,8 +428,8 @@ impl NaiveDate { /// assert_eq!(from_ndays_opt(100_000_000), None); /// assert_eq!(from_ndays_opt(-100_000_000), None); /// ``` - pub fn from_num_days_from_ce_opt(days: i32) -> Option { - Some(NaiveDate { inner: DateImpl::from_num_days_from_ce_opt(days)? }) + pub const fn from_num_days_from_ce_opt(days: i32) -> Option { + Some(NaiveDate { inner: try_opt!(DateImpl::from_num_days_from_ce_opt(days)) }) } /// Makes a new `NaiveDate` by counting the number of occurrences of a particular day-of-week @@ -433,19 +459,19 @@ impl NaiveDate { /// /// Returns `None` if `n` out-of-range; ie. if `n` is larger than the number of `weekday` in /// `month` (eg. the 6th Friday of March 2017), or if `n == 0`. - pub fn from_weekday_of_month_opt( + pub const fn from_weekday_of_month_opt( year: i16, month: u8, weekday: Weekday, n: u8, ) -> Option { Some(NaiveDate { - inner: DateImpl::from_weekday_of_month_opt( + inner: try_opt!(DateImpl::from_weekday_of_month_opt( year, - Month::try_from(month).ok()?, + try_opt!(Month::try_from_u8(month)), weekday, n, - )?, + )), }) } @@ -514,7 +540,7 @@ impl NaiveDate { /// Some(NaiveDate::from_ymd_opt(2022, 9, 30).unwrap()) /// ); /// ``` - pub fn checked_add_months(self, months: Months) -> Option { + pub const fn checked_add_months(self, months: Months) -> Option { if months.0 == 0 { return Some(self); } @@ -544,7 +570,7 @@ impl NaiveDate { /// None /// ); /// ``` - pub fn checked_sub_months(self, months: Months) -> Option { + pub const fn checked_sub_months(self, months: Months) -> Option { if months.0 == 0 { return Some(self); } @@ -556,8 +582,8 @@ impl NaiveDate { } } - fn diff_months(self, months: i32) -> Option { - Some(NaiveDate { inner: self.inner.diff_months(months)? }) + const fn diff_months(self, months: i32) -> Option { + Some(NaiveDate { inner: try_opt!(self.inner.diff_months(months)) }) } /// Add a duration in [`Days`] to the date @@ -575,12 +601,13 @@ impl NaiveDate { /// Some(NaiveDate::from_ymd_opt(2022, 8, 2).unwrap()) /// ); /// ``` - pub fn checked_add_days(self, days: Days) -> Option { + pub const fn checked_add_days(self, days: Days) -> Option { if days.0 == 0 { return Some(self); } - i64::try_from(days.0).ok().and_then(|d| self.diff_days(d)) + let days = try_from_u64_to_i64!(days.0); + self.diff_days(days) } /// Subtract a duration in [`Days`] from the date @@ -594,16 +621,17 @@ impl NaiveDate { /// Some(NaiveDate::from_ymd_opt(2022, 2, 14).unwrap()) /// ); /// ``` - pub fn checked_sub_days(self, days: Days) -> Option { + pub const fn checked_sub_days(self, days: Days) -> Option { if days.0 == 0 { return Some(self); } - i64::try_from(days.0).ok().and_then(|d| self.diff_days(-d)) + let days = try_from_u64_to_i64!(days.0); + self.diff_days(-days) } - fn diff_days(self, days: i64) -> Option { - Some(NaiveDate { inner: self.inner.diff_days(days)? }) + const fn diff_days(self, days: i64) -> Option { + Some(NaiveDate { inner: try_opt!(self.inner.diff_days(days)) }) } /// Makes a new `NaiveDateTime` from the current date and given `NaiveTime`. @@ -854,8 +882,8 @@ impl NaiveDate { /// assert_eq!(NaiveDate::MAX.succ_opt(), None); /// ``` #[inline] - pub fn succ_opt(&self) -> Option { - Some(NaiveDate { inner: self.inner.succ_opt()? }) + pub const fn succ_opt(&self) -> Option { + Some(NaiveDate { inner: try_opt!(self.inner.succ_opt()) }) } /// Makes a new `NaiveDate` for the previous calendar date. @@ -881,8 +909,8 @@ impl NaiveDate { /// assert_eq!(NaiveDate::MIN.pred_opt(), None); /// ``` #[inline] - pub fn pred_opt(&self) -> Option { - Some(NaiveDate { inner: self.inner.pred_opt()? }) + pub const fn pred_opt(&self) -> Option { + Some(NaiveDate { inner: try_opt!(self.inner.pred_opt()) }) } /// Subtracts another `NaiveDate` from the current date. @@ -2709,4 +2737,30 @@ mod tests { assert!(days.contains(&date)); } } + + #[cfg(feature = "const-validation")] + #[test] + fn test_const_ymd() { + use super::NaiveDate; + + let res = std::panic::catch_unwind(|| { + let _ = NaiveDate::from_ymd_validated(2022, 1, 0); + }); + assert!(res.is_err()); + let _ = NaiveDate::from_ymd_validated(2022, 1, 1); + let _ = NaiveDate::from_ymd_validated(2022, 12, 31); + let res = std::panic::catch_unwind(|| { + let _ = NaiveDate::from_ymd_validated(2022, 12, 32); + }); + assert!(res.is_err()); + + ymd!(2022, 1, 5); + ymd!(2022, 1, 1); + ymd!(2022, 1, 2); + ymd!(2022, 12, 31); + // ymd!(2022, 12, 32); // compile error! + + yo!(2022, 1); + yo!(2022, 365); + } } diff --git a/src/naive/datetime/mod.rs b/src/naive/datetime/mod.rs index 4c62f8431c..61fd949c49 100644 --- a/src/naive/datetime/mod.rs +++ b/src/naive/datetime/mod.rs @@ -9,11 +9,10 @@ use core::convert::TryFrom; use core::ops::{Add, AddAssign, Sub, SubAssign}; use core::{fmt, str}; -use num_integer::div_mod_floor; +use crate::naive::internals::const_div_mod_floor_i64; #[cfg(feature = "rkyv")] use rkyv::{Archive, Deserialize, Serialize}; -use super::internals::DateImpl; #[cfg(any(feature = "alloc", feature = "std", test))] use crate::format::DelayedFormat; use crate::format::{parse, ParseError, ParseResult, Parsed, StrftimeItems}; @@ -186,7 +185,7 @@ impl NaiveDateTime { /// ``` #[inline] pub fn from_timestamp_opt(secs: i64, nsecs: u32) -> Option { - let (days, secs) = div_mod_floor(secs, 86_400); + let (days, secs) = const_div_mod_floor_i64(secs, 86_400); let date = i32::try_from(days) .ok() .and_then(|days| days.checked_add(719_163)) diff --git a/src/naive/internals.rs b/src/naive/internals.rs index 39786d7607..3ec3b71fdf 100644 --- a/src/naive/internals.rs +++ b/src/naive/internals.rs @@ -17,11 +17,8 @@ #![allow(unreachable_pub)] #![allow(dead_code)] -use core::convert::TryFrom; use core::hash::Hash; use core::i32; -use num_integer::div_mod_floor; -use num_integer::{div_rem, mod_floor}; use ordinal_flags::Of; use year_flags::{cycle_to_yo, yo_to_cycle, YearTypeFlag}; @@ -88,24 +85,16 @@ macro_rules! try_from_u32_to_i32 { }; } -// forces the cosnt validation -#[cfg(feature = "const-validation")] -macro_rules! ymd { - ($y:expr, $m:expr, $d:expr) => {{ - const _: DateImpl = DateImpl::from_ymd_validated($y, $m, $d); - - DateImpl::from_ymd_validated($y, $m, $d) - }}; -} - -// forces the cosnt validation -#[cfg(feature = "const-validation")] -macro_rules! yo { - ($y:expr, $o:expr) => {{ - const _: DateImpl = DateImpl::from_yo_validated($y, $m, $d); - - DateImpl::from_yo_validated($y, $m, $d) - }}; +/// a +#[macro_export] +macro_rules! try_from_u64_to_i64 { + ($e:expr) => { + if $e <= i64::MAX as u64 { + $e as i64 + } else { + return None; + } + }; } // DateImpl of [u8; 4] @@ -184,19 +173,12 @@ impl DateImpl { } pub(super) const fn from_num_days_from_ce_opt(days: i32) -> Option { - // todo!() let days = try_opt!(days.checked_add(365)); // make December 31, 1 BCE equal to day 0 // dbg!(days); let (year_div_400, cycle) = const_div_mod_floor_i32(days, DAYS_IN_CYCLE); - // dbg!(year_div_400, cycle); let (year_mod_400, ordinal) = cycle_to_yo(cycle as u32); - // dbg!(year_mod_400, ordinal); - let flags = YearTypeFlag::calculate_from_year(year_mod_400 as i16); - // dbg!(flags); let year_base = year_div_400.checked_mul(400); - // dbg!(year_base); let year = try_opt!(year_base).checked_add(try_from_u32_to_i32!(year_mod_400)); - // dbg!(year); DateImpl::from_yo(try_from_i32_to_i16!(try_opt!(year)), ordinal as u16) } @@ -205,7 +187,6 @@ impl DateImpl { week: u16, weekday: Weekday, ) -> Option { - // dbg!(year, week, weekday); // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_an_ordinal_or_month_date_from_a_week_date let flags = YearTypeFlag::calculate_from_year(const_mod_floor_i16((year % 400) as i16, 400)); @@ -232,19 +213,13 @@ impl DateImpl { match base_ord { Some(ord) if ord <= flags.ndays().get() => { // matching cy - // let adj = mult_week.checked_sub(u16::from(flags.jan_1_weekday().num_days_from_monday()))?.checked_add(u16::from(weekday.num_days_from_monday()) + 1)?; - // dbg!("regular"); - // dbg!(flags.jan_1_weekday().num_days_from_monday(), weekday.num_days_from_monday(), adj); let cal_year = try_from_i32_to_i16!(year); - // let ordinal = adj; - // dbg!(cal_year, flags, ord); DateImpl::from_yo(cal_year, ord) } Some(ord) => { // next CY let cal_year = try_from_i32_to_i16!(year + 1); let adj_ord = try_opt!(ord.checked_sub(flags.ndays().get())); - // dbg!(cal_year, alt_flags, adj_ord); DateImpl::from_yo(cal_year, adj_ord) } None => { @@ -255,9 +230,7 @@ impl DateImpl { let ord = try_opt!(flags .week_1_jan_delta_days_from_jan_1_calendar_adj(alt_flags.ndays().get())); - // dbg!(ord); let adj_ord = ord + 1 + weekday.num_days_from_monday() as u16; - // dbg!(cal_year, adj_ord, Of::from_ordinal_and_year(adj_ord, cal_year).unwrap().weekday()); DateImpl::from_yo(cal_year, adj_ord) } } @@ -277,28 +250,13 @@ impl DateImpl { Weekday::Tue | Weekday::Wed | Weekday::Thu => self.year() as i32 + 1, Weekday::Mon | Weekday::Fri | Weekday::Sat | Weekday::Sun => self.year() as i32, } - // dbg!(self.weekday(), YearTypeFlag::calculate_from_year(mod_floor(self.year(), 400) + 1).jan_1_weekday()); - // todo!() } - _ => self.year() as i32, // Ordering::Greater => todo!(), + _ => self.year() as i32, } - // if ordinal is less than or equal to the max days in last week of prev year, - // then we are in the last week of the prev year - // if self.ordinal().get() - // < self.year_type().ordinal_of_first_day_of_first_week_in_matching_year() - // { - // i32::from(self.year()) - 1 - // } else if self.ordinal().get() > self.year_type().ordinal_of_last_day_in_last_week_in_matching_year() { - // i32::from(self.year()) + 1 - // } else { - // self.year().into() - // } } // u16 for convenience as u16 is a lot more convenient than Option! pub(super) const fn isoweek_number(&self) -> u16 { - // dbg!(self); - // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_an_ordinal_date let num = (self.ordinal().get() + 9 - self.weekday().num_days_from_monday() as u16) / 7; // dbg!(num); @@ -422,67 +380,49 @@ impl DateImpl { } pub(super) const fn checked_add_signed(self, rhs: i64) -> Option { - // dbg!(self); let rhs = try_from_i64_to_i32!(rhs); - // dbg!(rhs); let year = self.year(); - // dbg!(year); + let (mut year_div_400, year_mod_400) = const_div_mod_floor_i16(year, 400); - // dbg!(year_div_400, year_mod_400); let cycle = yo_to_cycle(year_mod_400 as u32, self.ordinal().get()); - // dbg!(cycle); + let cycle = try_opt!((cycle as i32).checked_add(rhs)); - // dbg!(cycle); let (cycle_div_400y, cycle) = const_div_mod_floor_i32(cycle, DAYS_IN_CYCLE); - // dbg!(cycle_div_400y, cycle); year_div_400 = try_opt!(year_div_400.checked_add(try_from_i32_to_i16!(cycle_div_400y))); - // dbg!(year_div_400); + let (year_mod_400, ordinal) = cycle_to_yo(cycle as u32); - // dbg!(year_mod_400, ordinal); + let year_mod_400 = try_from_u32_to_u16!(year_mod_400); - // dbg!(year_mod_400); + let ordinal = try_from_u32_to_u16!(ordinal); - // dbg!(ordinal); + let year_base = (year_div_400 as i32).checked_mul(400); - // dbg!(year_base); let year = try_opt!(year_base).checked_add(year_mod_400 as i32); - // dbg!(year); let year = try_from_i32_to_i16!(try_opt!(year)); - // dbg!(year); + DateImpl::from_yo(year, ordinal) } pub(super) const fn checked_sub_signed(self, rhs: i64) -> Option { - // dbg!(self); let rhs = try_from_i64_to_i32!(rhs); - // dbg!(rhs); let year = self.year(); - // dbg!(year); + let (mut year_div_400, year_mod_400) = const_div_mod_floor_i16(year, 400); - // dbg!(year_div_400, year_mod_400); let cycle = yo_to_cycle(year_mod_400 as u32, self.ordinal().get()); - // dbg!(cycle); + let cycle = try_opt!((cycle as i32).checked_sub(rhs)); - // dbg!(cycle); let (cycle_div_400y, cycle) = const_div_mod_floor_i32(cycle, DAYS_IN_CYCLE); - // dbg!(cycle_div_400y, cycle); year_div_400 = try_opt!(year_div_400.checked_add(try_from_i32_to_i16!(cycle_div_400y))); - // dbg!(year_div_400); let (year_mod_400, ordinal) = cycle_to_yo(cycle as u32); - // dbg!(year_mod_400, ordinal); let year_mod_400 = try_from_u32_to_u16!(year_mod_400); - // dbg!(year_mod_400); let ordinal = try_from_u32_to_u16!(ordinal); let year_base = (year_div_400 as i32).checked_mul(400); - // dbg!(year_base); let year = try_opt!(year_base).checked_add(year_mod_400 as i32); - // dbg!(year); let year = try_from_i32_to_i16!(try_opt!(year)); - // dbg!(year); DateImpl::from_yo(year, ordinal) } @@ -521,7 +461,21 @@ impl DateImpl { // from num-integer, copied so it can be const /// Floored integer modulo #[inline] -const fn const_mod_floor_i16(a: i16, b: i16) -> i16 { +pub(crate) const fn const_mod_floor_i16(a: i16, b: i16) -> i16 { + // Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_, + // December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf) + let r = a % b; + if (r > 0 && b < 0) || (r < 0 && b > 0) { + r + b + } else { + r + } +} + +// from num-integer, copied so it can be const +/// Floored integer modulo +#[inline] +pub(crate) const fn const_mod_floor_i32(a: i32, b: i32) -> i32 { // Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_, // December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf) let r = a % b; @@ -535,7 +489,7 @@ const fn const_mod_floor_i16(a: i16, b: i16) -> i16 { // from num-integer, copied so it can be const /// Floored integer modulo #[inline] -const fn const_mod_floor_i32(a: i32, b: i32) -> i32 { +pub(crate) const fn const_mod_floor_i64(a: i64, b: i64) -> i64 { // Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_, // December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf) let r = a % b; @@ -549,7 +503,7 @@ const fn const_mod_floor_i32(a: i32, b: i32) -> i32 { // from num-integer, copied so it can be const /// Floored integer modulo #[inline] -const fn const_div_floor_i16(a: i16, b: i16) -> i16 { +pub(crate) const fn const_div_floor_i16(a: i16, b: i16) -> i16 { // Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_, // December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf) let (d, r) = (a / b, a % b); @@ -563,7 +517,7 @@ const fn const_div_floor_i16(a: i16, b: i16) -> i16 { // from num-integer, copied so it can be const /// Floored integer modulo #[inline] -const fn const_div_floor_i32(a: i32, b: i32) -> i32 { +pub(crate) const fn const_div_floor_i32(a: i32, b: i32) -> i32 { // Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_, // December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf) let (d, r) = (a / b, a % b); @@ -577,17 +531,45 @@ const fn const_div_floor_i32(a: i32, b: i32) -> i32 { // from num-integer, copied so it can be const /// Floored integer modulo #[inline] -const fn const_div_mod_floor_i16(a: i16, b: i16) -> (i16, i16) { +pub(crate) const fn const_div_floor_i64(a: i64, b: i64) -> i64 { + // Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_, + // December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf) + let (d, r) = (a / b, a % b); + if (r > 0 && b < 0) || (r < 0 && b > 0) { + d - 1 + } else { + d + } +} + +// from num-integer, copied so it can be const +/// Floored integer modulo +#[inline] +pub(crate) const fn const_div_mod_floor_i16(a: i16, b: i16) -> (i16, i16) { (const_div_floor_i16(a, b), const_mod_floor_i16(a, b)) } // from num-integer, copied so it can be const /// Floored integer modulo #[inline] -const fn const_div_mod_floor_i32(a: i32, b: i32) -> (i32, i32) { +pub(crate) const fn const_div_mod_floor_i32(a: i32, b: i32) -> (i32, i32) { (const_div_floor_i32(a, b), const_mod_floor_i32(a, b)) } +// from num-integer, copied so it can be const +/// Floored integer modulo +#[inline] +pub(crate) const fn const_div_mod_floor_i64(a: i64, b: i64) -> (i64, i64) { + (const_div_floor_i64(a, b), const_mod_floor_i64(a, b)) +} + +// from num-integer, copied so it can be const +/// Floored integer modulo +#[inline] +pub(crate) const fn const_div_mod_floor_u32(a: u32, b: u32) -> (u32, u32) { + (a / b, a % b) +} + const fn is_leap(astronomical_year: i16) -> bool { if astronomical_year % 4 != 0 { return false; @@ -1398,7 +1380,6 @@ mod tests { use crate::naive::internals::is_leap; use crate::naive::internals::year_flags::year_deltas; use crate::{Month, Weekday}; - use num_integer::mod_floor; const NONLEAP_FLAGS: [YearTypeFlag; 7] = [A, B, C, D, E, F, G]; const LEAP_FLAGS: [YearTypeFlag; 7] = [AG, BA, CB, DC, ED, FE, GF]; @@ -1561,28 +1542,6 @@ mod tests { // } // } - #[cfg(feature = "const-validation")] - #[test] - fn test_const_ymd() { - use super::DateImpl; - use crate::Month; - - let res = std::panic::catch_unwind(|| { - let _ = DateImpl::from_ymd_validated(2022, Month::January, 0); - }); - assert!(res.is_err()); - let _ = DateImpl::from_ymd_validated(2022, Month::January, 1); - let _ = DateImpl::from_ymd_validated(2022, Month::December, 31); - let res = std::panic::catch_unwind(|| { - let _ = DateImpl::from_ymd_validated(2022, Month::December, 32); - }); - assert!(res.is_err()); - - ymd!(2022, Month::January, 5); - ymd!(2022, Month::January, 1); - ymd!(2022, Month::January, 2); - } - #[test] fn test_ndays() { for i in i16::MIN..=i16::MAX { @@ -1740,7 +1699,7 @@ mod tests { assert_eq!(flags.is_leap(), is_leap(y)); - assert_eq!(flags, YEAR_TO_FLAGS[mod_floor(y, 400) as usize]); + assert_eq!(flags, YEAR_TO_FLAGS[super::const_mod_floor_i16(y, 400) as usize]); flags.ndays(); } diff --git a/src/naive/mod.rs b/src/naive/mod.rs index 7591d00008..ab7d9d351f 100644 --- a/src/naive/mod.rs +++ b/src/naive/mod.rs @@ -7,7 +7,7 @@ mod date; pub(crate) mod datetime; #[macro_use] -mod internals; +pub(crate) mod internals; mod isoweek; mod time; diff --git a/src/naive/time/mod.rs b/src/naive/time/mod.rs index f49d2a8212..25b112da4d 100644 --- a/src/naive/time/mod.rs +++ b/src/naive/time/mod.rs @@ -8,7 +8,6 @@ use core::borrow::Borrow; use core::ops::{Add, AddAssign, Sub, SubAssign}; use core::{fmt, str}; -use num_integer::div_mod_floor; #[cfg(feature = "rkyv")] use rkyv::{Archive, Deserialize, Serialize}; @@ -16,6 +15,7 @@ use rkyv::{Archive, Deserialize, Serialize}; use crate::format::DelayedFormat; use crate::format::{parse, ParseError, ParseResult, Parsed, StrftimeItems}; use crate::format::{Fixed, Item, Numeric, Pad}; +use crate::naive::internals::const_div_mod_floor_u32; use crate::{TimeDelta, Timelike}; #[cfg(feature = "serde")] @@ -738,8 +738,8 @@ impl NaiveTime { /// Returns a triple of the hour, minute and second numbers. fn hms(&self) -> (u32, u32, u32) { - let (mins, sec) = div_mod_floor(self.secs, 60); - let (hour, min) = div_mod_floor(mins, 60); + let (mins, sec) = const_div_mod_floor_u32(self.secs, 60); + let (hour, min) = const_div_mod_floor_u32(mins, 60); (hour, min, sec) } diff --git a/src/offset/fixed.rs b/src/offset/fixed.rs index 40c66a2ec2..fbb47b3d39 100644 --- a/src/offset/fixed.rs +++ b/src/offset/fixed.rs @@ -6,11 +6,11 @@ use core::fmt; use core::ops::{Add, Sub}; -use num_integer::div_mod_floor; #[cfg(feature = "rkyv")] use rkyv::{Archive, Deserialize, Serialize}; use super::{LocalResult, Offset, TimeZone}; +use crate::naive::internals::const_div_mod_floor_i32; use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime}; use crate::time_delta::TimeDelta; use crate::DateTime; @@ -136,8 +136,8 @@ impl fmt::Debug for FixedOffset { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let offset = self.local_minus_utc; let (sign, offset) = if offset < 0 { ('-', -offset) } else { ('+', offset) }; - let (mins, sec) = div_mod_floor(offset, 60); - let (hour, min) = div_mod_floor(mins, 60); + let (mins, sec) = const_div_mod_floor_i32(offset, 60); + let (hour, min) = const_div_mod_floor_i32(mins, 60); if sec == 0 { write!(f, "{}{:02}:{:02}", sign, hour, min) } else { diff --git a/src/offset/local/tz_info/mod.rs b/src/offset/local/tz_info/mod.rs index bd2693b6bb..43f8fc1a35 100644 --- a/src/offset/local/tz_info/mod.rs +++ b/src/offset/local/tz_info/mod.rs @@ -100,7 +100,7 @@ impl From for Error { } } -// MSRV: 1.38 +// MSRV: 1.47 #[inline] fn rem_euclid(v: i64, rhs: i64) -> i64 { let r = v % rhs;