diff options
Diffstat (limited to 'src/naive/internals.rs')
-rw-r--r-- | src/naive/internals.rs | 477 |
1 files changed, 280 insertions, 197 deletions
diff --git a/src/naive/internals.rs b/src/naive/internals.rs index 346063c..c6d7536 100644 --- a/src/naive/internals.rs +++ b/src/naive/internals.rs @@ -13,19 +13,23 @@ //! but the conversion keeps the valid value valid and the invalid value invalid //! so that the user-facing `NaiveDate` can validate the input as late as possible. -#![allow(dead_code)] // some internal methods have been left for consistency #![cfg_attr(feature = "__internal_bench", allow(missing_docs))] -use core::{fmt, i32}; -use div::{div_rem, mod_floor}; -use num_traits::FromPrimitive; -use Weekday; +use crate::Weekday; +use core::fmt; -/// The internal date representation. This also includes the packed `Mdf` value. -pub type DateImpl = i32; +/// The internal date representation: `year << 13 | Of` +pub(super) type DateImpl = i32; -pub const MAX_YEAR: DateImpl = i32::MAX >> 13; -pub const MIN_YEAR: DateImpl = i32::MIN >> 13; +/// MAX_YEAR is one year less than the type is capable of representing. Internally we may sometimes +/// use the headroom, notably to handle cases where the offset of a `DateTime` constructed with +/// `NaiveDate::MAX` pushes it beyond the valid, representable range. +pub(super) const MAX_YEAR: DateImpl = (i32::MAX >> 13) - 1; + +/// MIN_YEAR is one year more than the type is capable of representing. Internally we may sometimes +/// use the headroom, notably to handle cases where the offset of a `DateTime` constructed with +/// `NaiveDate::MIN` pushes it beyond the valid, representable range. +pub(super) const MIN_YEAR: DateImpl = (i32::MIN >> 13) + 1; /// The year flags (aka the dominical letter). /// @@ -35,25 +39,26 @@ pub const MIN_YEAR: DateImpl = i32::MIN >> 13; /// where `a` is `1` for the common year (simplifies the `Of` validation) /// and `bbb` is a non-zero `Weekday` (mapping `Mon` to 7) of the last day in the past year /// (simplifies the day of week calculation from the 1-based ordinal). -#[derive(PartialEq, Eq, Copy, Clone)] -pub struct YearFlags(pub u8); - -pub const A: YearFlags = YearFlags(0o15); -pub const AG: YearFlags = YearFlags(0o05); -pub const B: YearFlags = YearFlags(0o14); -pub const BA: YearFlags = YearFlags(0o04); -pub const C: YearFlags = YearFlags(0o13); -pub const CB: YearFlags = YearFlags(0o03); -pub const D: YearFlags = YearFlags(0o12); -pub const DC: YearFlags = YearFlags(0o02); -pub const E: YearFlags = YearFlags(0o11); -pub const ED: YearFlags = YearFlags(0o01); -pub const F: YearFlags = YearFlags(0o17); -pub const FE: YearFlags = YearFlags(0o07); -pub const G: YearFlags = YearFlags(0o16); -pub const GF: YearFlags = YearFlags(0o06); - -static YEAR_TO_FLAGS: [YearFlags; 400] = [ +#[allow(unreachable_pub)] // public as an alias for benchmarks only +#[derive(PartialEq, Eq, Copy, Clone, Hash)] +pub struct YearFlags(pub(super) u8); + +pub(super) const A: YearFlags = YearFlags(0o15); +pub(super) const AG: YearFlags = YearFlags(0o05); +pub(super) const B: YearFlags = YearFlags(0o14); +pub(super) const BA: YearFlags = YearFlags(0o04); +pub(super) const C: YearFlags = YearFlags(0o13); +pub(super) const CB: YearFlags = YearFlags(0o03); +pub(super) const D: YearFlags = YearFlags(0o12); +pub(super) const DC: YearFlags = YearFlags(0o02); +pub(super) const E: YearFlags = YearFlags(0o11); +pub(super) const ED: YearFlags = YearFlags(0o01); +pub(super) const F: YearFlags = YearFlags(0o17); +pub(super) const FE: YearFlags = YearFlags(0o07); +pub(super) const G: YearFlags = YearFlags(0o16); +pub(super) const GF: YearFlags = YearFlags(0o06); + +const YEAR_TO_FLAGS: &[YearFlags; 400] = &[ BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, @@ -72,7 +77,7 @@ static YEAR_TO_FLAGS: [YearFlags; 400] = [ D, CB, A, G, F, ED, C, B, A, GF, E, D, C, // 400 ]; -static YEAR_DELTAS: [u8; 401] = [ +const YEAR_DELTAS: &[u8; 401] = &[ 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, @@ -94,44 +99,48 @@ static YEAR_DELTAS: [u8; 401] = [ 96, 97, 97, 97, 97, // 400+1 ]; -pub fn cycle_to_yo(cycle: u32) -> (u32, u32) { - let (mut year_mod_400, mut ordinal0) = div_rem(cycle, 365); - let delta = u32::from(YEAR_DELTAS[year_mod_400 as usize]); +pub(super) const fn cycle_to_yo(cycle: u32) -> (u32, u32) { + let mut year_mod_400 = cycle / 365; + let mut ordinal0 = cycle % 365; + let delta = YEAR_DELTAS[year_mod_400 as usize] as u32; if ordinal0 < delta { year_mod_400 -= 1; - ordinal0 += 365 - u32::from(YEAR_DELTAS[year_mod_400 as usize]); + ordinal0 += 365 - YEAR_DELTAS[year_mod_400 as usize] as u32; } else { ordinal0 -= delta; } (year_mod_400, ordinal0 + 1) } -pub fn yo_to_cycle(year_mod_400: u32, ordinal: u32) -> u32 { - year_mod_400 * 365 + u32::from(YEAR_DELTAS[year_mod_400 as usize]) + ordinal - 1 +pub(super) const fn yo_to_cycle(year_mod_400: u32, ordinal: u32) -> u32 { + year_mod_400 * 365 + YEAR_DELTAS[year_mod_400 as usize] as u32 + ordinal - 1 } impl YearFlags { + #[allow(unreachable_pub)] // public as an alias for benchmarks only + #[doc(hidden)] // for benchmarks only #[inline] - pub fn from_year(year: i32) -> YearFlags { - let year = mod_floor(year, 400); + #[must_use] + pub const fn from_year(year: i32) -> YearFlags { + let year = year.rem_euclid(400); YearFlags::from_year_mod_400(year) } #[inline] - pub fn from_year_mod_400(year: i32) -> YearFlags { + pub(super) const fn from_year_mod_400(year: i32) -> YearFlags { YEAR_TO_FLAGS[year as usize] } #[inline] - pub fn ndays(&self) -> u32 { + pub(super) const fn ndays(&self) -> u32 { let YearFlags(flags) = *self; - 366 - u32::from(flags >> 3) + 366 - (flags >> 3) as u32 } #[inline] - pub fn isoweek_delta(&self) -> u32 { + pub(super) const fn isoweek_delta(&self) -> u32 { let YearFlags(flags) = *self; - let mut delta = u32::from(flags) & 0b0111; + let mut delta = (flags & 0b0111) as u32; if delta < 3 { delta += 7; } @@ -139,7 +148,7 @@ impl YearFlags { } #[inline] - pub fn nisoweeks(&self) -> u32 { + pub(super) const fn nisoweeks(&self) -> u32 { let YearFlags(flags) = *self; 52 + ((0b0000_0100_0000_0110 >> flags as usize) & 1) } @@ -170,13 +179,13 @@ impl fmt::Debug for YearFlags { } } -pub const MIN_OL: u32 = 1 << 1; -pub const MAX_OL: u32 = 366 << 1; // larger than the non-leap last day `(365 << 1) | 1` -pub const MIN_MDL: u32 = (1 << 6) | (1 << 1); -pub const MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1; +// OL: (ordinal << 1) | leap year flag +pub(super) const MIN_OL: u32 = 1 << 1; +pub(super) const MAX_OL: u32 = 366 << 1; // `(366 << 1) | 1` would be day 366 in a non-leap year +pub(super) const MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1; const XX: i8 = -128; -static MDL_TO_OL: [i8; MAX_MDL as usize + 1] = [ +const MDL_TO_OL: &[i8; MAX_MDL as usize + 1] = &[ XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0 @@ -219,7 +228,7 @@ static MDL_TO_OL: [i8; MAX_MDL as usize + 1] = [ 100, // 12 ]; -static OL_TO_MDL: [u8; MAX_OL as usize + 1] = [ +const OL_TO_MDL: &[u8; MAX_OL as usize + 1] = &[ 0, 0, // 0 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, @@ -264,95 +273,106 @@ static OL_TO_MDL: [u8; MAX_OL as usize + 1] = [ /// /// The whole bits except for the least 3 bits are referred as `Ol` (ordinal and leap flag), /// which is an index to the `OL_TO_MDL` lookup table. +/// +/// The methods implemented on `Of` always return a valid value. #[derive(PartialEq, PartialOrd, Copy, Clone)] -pub struct Of(pub u32); +pub(super) struct Of(u32); impl Of { #[inline] - fn clamp_ordinal(ordinal: u32) -> u32 { - if ordinal > 366 { - 0 - } else { - ordinal - } + pub(super) const fn new(ordinal: u32, YearFlags(flags): YearFlags) -> Option<Of> { + let of = Of((ordinal << 4) | flags as u32); + of.validate() } - #[inline] - pub fn new(ordinal: u32, YearFlags(flags): YearFlags) -> Of { - let ordinal = Of::clamp_ordinal(ordinal); - Of((ordinal << 4) | u32::from(flags)) + pub(super) const fn from_date_impl(date_impl: DateImpl) -> Of { + // We assume the value in the `DateImpl` is valid. + Of((date_impl & 0b1_1111_1111_1111) as u32) } #[inline] - pub fn from_mdf(Mdf(mdf): Mdf) -> Of { + pub(super) const fn from_mdf(Mdf(mdf): Mdf) -> Option<Of> { let mdl = mdf >> 3; - match MDL_TO_OL.get(mdl as usize) { - Some(&v) => Of(mdf.wrapping_sub((i32::from(v) as u32 & 0x3ff) << 3)), - None => Of(0), + if mdl > MAX_MDL { + // Panicking on out-of-bounds indexing would be reasonable, but just return `None`. + return None; } + // Array is indexed from `[1..=MAX_MDL]`, with a `0` index having a meaningless value. + let v = MDL_TO_OL[mdl as usize]; + let of = Of(mdf.wrapping_sub((v as i32 as u32 & 0x3ff) << 3)); + of.validate() } #[inline] - pub fn valid(&self) -> bool { - let Of(of) = *self; - let ol = of >> 3; - MIN_OL <= ol && ol <= MAX_OL + pub(super) const fn inner(&self) -> u32 { + self.0 } + /// Returns `(ordinal << 1) | leap-year-flag`. #[inline] - pub fn ordinal(&self) -> u32 { - let Of(of) = *self; - of >> 4 + const fn ol(&self) -> u32 { + self.0 >> 3 } #[inline] - pub fn with_ordinal(&self, ordinal: u32) -> Of { - let ordinal = Of::clamp_ordinal(ordinal); - let Of(of) = *self; - Of((of & 0b1111) | (ordinal << 4)) + const fn validate(self) -> Option<Of> { + let ol = self.ol(); + match ol >= MIN_OL && ol <= MAX_OL { + true => Some(self), + false => None, + } } #[inline] - pub fn flags(&self) -> YearFlags { - let Of(of) = *self; - YearFlags((of & 0b1111) as u8) + pub(super) const fn ordinal(&self) -> u32 { + self.0 >> 4 } #[inline] - pub fn with_flags(&self, YearFlags(flags): YearFlags) -> Of { - let Of(of) = *self; - Of((of & !0b1111) | u32::from(flags)) + pub(super) const fn with_ordinal(&self, ordinal: u32) -> Option<Of> { + let of = Of((ordinal << 4) | (self.0 & 0b1111)); + of.validate() + } + + #[inline] + pub(super) const fn flags(&self) -> YearFlags { + YearFlags((self.0 & 0b1111) as u8) } #[inline] - pub fn weekday(&self) -> Weekday { + pub(super) const fn weekday(&self) -> Weekday { let Of(of) = *self; - Weekday::from_u32(((of >> 4) + (of & 0b111)) % 7).unwrap() + weekday_from_u32_mod7((of >> 4) + (of & 0b111)) } #[inline] - pub fn isoweekdate_raw(&self) -> (u32, Weekday) { + pub(super) fn isoweekdate_raw(&self) -> (u32, Weekday) { // week ordinal = ordinal + delta let Of(of) = *self; let weekord = (of >> 4).wrapping_add(self.flags().isoweek_delta()); - (weekord / 7, Weekday::from_u32(weekord % 7).unwrap()) + (weekord / 7, weekday_from_u32_mod7(weekord)) } + #[cfg_attr(feature = "cargo-clippy", allow(clippy::wrong_self_convention))] #[inline] - pub fn to_mdf(&self) -> Mdf { + pub(super) const fn to_mdf(&self) -> Mdf { Mdf::from_of(*self) } + /// Returns an `Of` with the next day, or `None` if this is the last day of the year. #[inline] - pub fn succ(&self) -> Of { - let Of(of) = *self; - Of(of + (1 << 4)) + pub(super) const fn succ(&self) -> Option<Of> { + let of = Of(self.0 + (1 << 4)); + of.validate() } + /// Returns an `Of` with the previous day, or `None` if this is the first day of the year. #[inline] - pub fn pred(&self) -> Of { - let Of(of) = *self; - Of(of - (1 << 4)) + pub(super) const fn pred(&self) -> Option<Of> { + match self.ordinal() { + 1 => None, + _ => Some(Of(self.0 - (1 << 4))), + } } } @@ -374,94 +394,88 @@ impl fmt::Debug for Of { /// The whole bits except for the least 3 bits are referred as `Mdl` /// (month, day of month and leap flag), /// which is an index to the `MDL_TO_OL` lookup table. +/// +/// The methods implemented on `Mdf` do not always return a valid value. +/// Dates that can't exist, like February 30, can still be represented. +/// Use `Mdl::valid` to check whether the date is valid. #[derive(PartialEq, PartialOrd, Copy, Clone)] -pub struct Mdf(pub u32); +pub(super) struct Mdf(u32); impl Mdf { #[inline] - fn clamp_month(month: u32) -> u32 { - if month > 12 { - 0 - } else { - month + pub(super) const fn new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Option<Mdf> { + match month >= 1 && month <= 12 && day >= 1 && day <= 31 { + true => Some(Mdf((month << 9) | (day << 4) | flags as u32)), + false => None, } } #[inline] - fn clamp_day(day: u32) -> u32 { - if day > 31 { - 0 - } else { - day - } - } - - #[inline] - pub fn new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Mdf { - let month = Mdf::clamp_month(month); - let day = Mdf::clamp_day(day); - Mdf((month << 9) | (day << 4) | u32::from(flags)) - } - - #[inline] - pub fn from_of(Of(of): Of) -> Mdf { + pub(super) const fn from_of(Of(of): Of) -> Mdf { let ol = of >> 3; - match OL_TO_MDL.get(ol as usize) { - Some(&v) => Mdf(of + (u32::from(v) << 3)), - None => Mdf(0), + if ol <= MAX_OL { + // Array is indexed from `[1..=MAX_OL]`, with a `0` index having a meaningless value. + Mdf(of + ((OL_TO_MDL[ol as usize] as u32) << 3)) + } else { + // Panicking here would be reasonable, but we are just going on with a safe value. + Mdf(0) } } - #[inline] - pub fn valid(&self) -> bool { + #[cfg(test)] + pub(super) const fn valid(&self) -> bool { let Mdf(mdf) = *self; let mdl = mdf >> 3; - match MDL_TO_OL.get(mdl as usize) { - Some(&v) => v >= 0, - None => false, + if mdl <= MAX_MDL { + // Array is indexed from `[1..=MAX_MDL]`, with a `0` index having a meaningless value. + MDL_TO_OL[mdl as usize] >= 0 + } else { + // Panicking here would be reasonable, but we are just going on with a safe value. + false } } #[inline] - pub fn month(&self) -> u32 { + pub(super) const fn month(&self) -> u32 { let Mdf(mdf) = *self; mdf >> 9 } #[inline] - pub fn with_month(&self, month: u32) -> Mdf { - let month = Mdf::clamp_month(month); + pub(super) const fn with_month(&self, month: u32) -> Option<Mdf> { + if month > 12 { + return None; + } + let Mdf(mdf) = *self; - Mdf((mdf & 0b1_1111_1111) | (month << 9)) + Some(Mdf((mdf & 0b1_1111_1111) | (month << 9))) } #[inline] - pub fn day(&self) -> u32 { + pub(super) const fn day(&self) -> u32 { let Mdf(mdf) = *self; (mdf >> 4) & 0b1_1111 } #[inline] - pub fn with_day(&self, day: u32) -> Mdf { - let day = Mdf::clamp_day(day); - let Mdf(mdf) = *self; - Mdf((mdf & !0b1_1111_0000) | (day << 4)) - } + pub(super) const fn with_day(&self, day: u32) -> Option<Mdf> { + if day > 31 { + return None; + } - #[inline] - pub fn flags(&self) -> YearFlags { let Mdf(mdf) = *self; - YearFlags((mdf & 0b1111) as u8) + Some(Mdf((mdf & !0b1_1111_0000) | (day << 4))) } #[inline] - pub fn with_flags(&self, YearFlags(flags): YearFlags) -> Mdf { + pub(super) const fn with_flags(&self, YearFlags(flags): YearFlags) -> Mdf { let Mdf(mdf) = *self; - Mdf((mdf & !0b1111) | u32::from(flags)) + Mdf((mdf & !0b1111) | flags as u32) } + #[cfg_attr(feature = "cargo-clippy", allow(clippy::wrong_self_convention))] #[inline] - pub fn to_of(&self) -> Of { + pub(super) const fn to_of(&self) -> Option<Of> { Of::from_mdf(*self) } } @@ -480,16 +494,27 @@ impl fmt::Debug for Mdf { } } +/// Create a `Weekday` from an `u32`, with Monday = 0. +/// Infallible, takes any `n` and applies `% 7`. +#[inline] +const fn weekday_from_u32_mod7(n: u32) -> Weekday { + match n % 7 { + 0 => Weekday::Mon, + 1 => Weekday::Tue, + 2 => Weekday::Wed, + 3 => Weekday::Thu, + 4 => Weekday::Fri, + 5 => Weekday::Sat, + _ => Weekday::Sun, + } +} + #[cfg(test)] mod tests { - #[cfg(test)] - extern crate num_iter; - - use self::num_iter::range_inclusive; + use super::weekday_from_u32_mod7; use super::{Mdf, Of}; use super::{YearFlags, A, AG, B, BA, C, CB, D, DC, E, ED, F, FE, G, GF}; - use std::u32; - use Weekday; + use crate::Weekday; const NONLEAP_FLAGS: [YearFlags; 7] = [A, B, C, D, E, F, G]; const LEAP_FLAGS: [YearFlags; 7] = [AG, BA, CB, DC, ED, FE, GF]; @@ -533,10 +558,15 @@ mod tests { #[test] fn test_of() { fn check(expected: bool, flags: YearFlags, ordinal1: u32, ordinal2: u32) { - for ordinal in range_inclusive(ordinal1, ordinal2) { - let of = Of::new(ordinal, flags); + for ordinal in ordinal1..=ordinal2 { + let of = match Of::new(ordinal, flags) { + Some(of) => of, + None if !expected => continue, + None => panic!("Of::new({}, {:?}) returned None", ordinal, flags), + }; + assert!( - of.valid() == expected, + of.validate().is_some() == expected, "ordinal {} = {:?} should be {} for dominical year {:?}", ordinal, of, @@ -564,9 +594,14 @@ mod tests { #[test] fn test_mdf_valid() { fn check(expected: bool, flags: YearFlags, month1: u32, day1: u32, month2: u32, day2: u32) { - for month in range_inclusive(month1, month2) { - for day in range_inclusive(day1, day2) { - let mdf = Mdf::new(month, day, flags); + for month in month1..=month2 { + for day in day1..=day2 { + let mdf = match Mdf::new(month, day, flags) { + Some(mdf) => mdf, + None if !expected => continue, + None => panic!("Mdf::new({}, {}, {:?}) returned None", month, day, flags), + }; + assert!( mdf.valid() == expected, "month {} day {} = {:?} should be {} for dominical year {:?}", @@ -650,9 +685,8 @@ mod tests { #[test] fn test_of_fields() { for &flags in FLAGS.iter() { - for ordinal in range_inclusive(1u32, 366) { - let of = Of::new(ordinal, flags); - if of.valid() { + for ordinal in 1u32..=366 { + if let Some(of) = Of::new(ordinal, flags) { assert_eq!(of.ordinal(), ordinal); } } @@ -662,12 +696,12 @@ mod tests { #[test] fn test_of_with_fields() { fn check(flags: YearFlags, ordinal: u32) { - let of = Of::new(ordinal, flags); + let of = Of::new(ordinal, flags).unwrap(); - for ordinal in range_inclusive(0u32, 1024) { + for ordinal in 0u32..=1024 { let of = of.with_ordinal(ordinal); - assert_eq!(of.valid(), Of::new(ordinal, flags).valid()); - if of.valid() { + assert_eq!(of, Of::new(ordinal, flags)); + if let Some(of) = of { assert_eq!(of.ordinal(), ordinal); } } @@ -685,25 +719,25 @@ mod tests { #[test] fn test_of_weekday() { - assert_eq!(Of::new(1, A).weekday(), Weekday::Sun); - assert_eq!(Of::new(1, B).weekday(), Weekday::Sat); - assert_eq!(Of::new(1, C).weekday(), Weekday::Fri); - assert_eq!(Of::new(1, D).weekday(), Weekday::Thu); - assert_eq!(Of::new(1, E).weekday(), Weekday::Wed); - assert_eq!(Of::new(1, F).weekday(), Weekday::Tue); - assert_eq!(Of::new(1, G).weekday(), Weekday::Mon); - assert_eq!(Of::new(1, AG).weekday(), Weekday::Sun); - assert_eq!(Of::new(1, BA).weekday(), Weekday::Sat); - assert_eq!(Of::new(1, CB).weekday(), Weekday::Fri); - assert_eq!(Of::new(1, DC).weekday(), Weekday::Thu); - assert_eq!(Of::new(1, ED).weekday(), Weekday::Wed); - assert_eq!(Of::new(1, FE).weekday(), Weekday::Tue); - assert_eq!(Of::new(1, GF).weekday(), Weekday::Mon); + assert_eq!(Of::new(1, A).unwrap().weekday(), Weekday::Sun); + assert_eq!(Of::new(1, B).unwrap().weekday(), Weekday::Sat); + assert_eq!(Of::new(1, C).unwrap().weekday(), Weekday::Fri); + assert_eq!(Of::new(1, D).unwrap().weekday(), Weekday::Thu); + assert_eq!(Of::new(1, E).unwrap().weekday(), Weekday::Wed); + assert_eq!(Of::new(1, F).unwrap().weekday(), Weekday::Tue); + assert_eq!(Of::new(1, G).unwrap().weekday(), Weekday::Mon); + assert_eq!(Of::new(1, AG).unwrap().weekday(), Weekday::Sun); + assert_eq!(Of::new(1, BA).unwrap().weekday(), Weekday::Sat); + assert_eq!(Of::new(1, CB).unwrap().weekday(), Weekday::Fri); + assert_eq!(Of::new(1, DC).unwrap().weekday(), Weekday::Thu); + assert_eq!(Of::new(1, ED).unwrap().weekday(), Weekday::Wed); + assert_eq!(Of::new(1, FE).unwrap().weekday(), Weekday::Tue); + assert_eq!(Of::new(1, GF).unwrap().weekday(), Weekday::Mon); for &flags in FLAGS.iter() { - let mut prev = Of::new(1, flags).weekday(); - for ordinal in range_inclusive(2u32, flags.ndays()) { - let of = Of::new(ordinal, flags); + let mut prev = Of::new(1, flags).unwrap().weekday(); + for ordinal in 2u32..=flags.ndays() { + let of = Of::new(ordinal, flags).unwrap(); let expected = prev.succ(); assert_eq!(of.weekday(), expected); prev = expected; @@ -714,9 +748,13 @@ mod tests { #[test] fn test_mdf_fields() { for &flags in FLAGS.iter() { - for month in range_inclusive(1u32, 12) { - for day in range_inclusive(1u32, 31) { - let mdf = Mdf::new(month, day, flags); + for month in 1u32..=12 { + for day in 1u32..31 { + let mdf = match Mdf::new(month, day, flags) { + Some(mdf) => mdf, + None => continue, + }; + if mdf.valid() { assert_eq!(mdf.month(), month); assert_eq!(mdf.day(), day); @@ -729,20 +767,28 @@ mod tests { #[test] fn test_mdf_with_fields() { fn check(flags: YearFlags, month: u32, day: u32) { - let mdf = Mdf::new(month, day, flags); + let mdf = Mdf::new(month, day, flags).unwrap(); + + for month in 0u32..=16 { + let mdf = match mdf.with_month(month) { + Some(mdf) => mdf, + None if month > 12 => continue, + None => panic!("failed to create Mdf with month {}", month), + }; - for month in range_inclusive(0u32, 16) { - let mdf = mdf.with_month(month); - assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid()); if mdf.valid() { assert_eq!(mdf.month(), month); assert_eq!(mdf.day(), day); } } - for day in range_inclusive(0u32, 1024) { - let mdf = mdf.with_day(day); - assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid()); + for day in 0u32..=1024 { + let mdf = match mdf.with_day(day) { + Some(mdf) => mdf, + None if day > 31 => continue, + None => panic!("failed to create Mdf with month {}", month), + }; + if mdf.valid() { assert_eq!(mdf.month(), month); assert_eq!(mdf.day(), day); @@ -772,44 +818,81 @@ mod tests { fn test_of_isoweekdate_raw() { for &flags in FLAGS.iter() { // January 4 should be in the first week - let (week, _) = Of::new(4 /* January 4 */, flags).isoweekdate_raw(); + let (week, _) = Of::new(4 /* January 4 */, flags).unwrap().isoweekdate_raw(); assert_eq!(week, 1); } } #[test] fn test_of_to_mdf() { - for i in range_inclusive(0u32, 8192) { - let of = Of(i); - assert_eq!(of.valid(), of.to_mdf().valid()); + for i in 0u32..=8192 { + if let Some(of) = Of(i).validate() { + assert!(of.to_mdf().valid()); + } } } #[test] fn test_mdf_to_of() { - for i in range_inclusive(0u32, 8192) { + for i in 0u32..=8192 { let mdf = Mdf(i); - assert_eq!(mdf.valid(), mdf.to_of().valid()); + assert_eq!(mdf.valid(), mdf.to_of().is_some()); } } #[test] fn test_of_to_mdf_to_of() { - for i in range_inclusive(0u32, 8192) { - let of = Of(i); - if of.valid() { - assert_eq!(of, of.to_mdf().to_of()); + for i in 0u32..=8192 { + if let Some(of) = Of(i).validate() { + assert_eq!(of, of.to_mdf().to_of().unwrap()); } } } #[test] fn test_mdf_to_of_to_mdf() { - for i in range_inclusive(0u32, 8192) { + for i in 0u32..=8192 { let mdf = Mdf(i); if mdf.valid() { - assert_eq!(mdf, mdf.to_of().to_mdf()); + assert_eq!(mdf, mdf.to_of().unwrap().to_mdf()); } } } + + #[test] + fn test_invalid_returns_none() { + let regular_year = YearFlags::from_year(2023); + let leap_year = YearFlags::from_year(2024); + assert!(Of::new(0, regular_year).is_none()); + assert!(Of::new(366, regular_year).is_none()); + assert!(Of::new(366, leap_year).is_some()); + assert!(Of::new(367, regular_year).is_none()); + + assert!(Mdf::new(0, 1, regular_year).is_none()); + assert!(Mdf::new(13, 1, regular_year).is_none()); + assert!(Mdf::new(1, 0, regular_year).is_none()); + assert!(Mdf::new(1, 32, regular_year).is_none()); + assert!(Mdf::new(2, 31, regular_year).is_some()); + + assert!(Of::from_mdf(Mdf::new(2, 30, regular_year).unwrap()).is_none()); + assert!(Of::from_mdf(Mdf::new(2, 30, leap_year).unwrap()).is_none()); + assert!(Of::from_mdf(Mdf::new(2, 29, regular_year).unwrap()).is_none()); + assert!(Of::from_mdf(Mdf::new(2, 29, leap_year).unwrap()).is_some()); + assert!(Of::from_mdf(Mdf::new(2, 28, regular_year).unwrap()).is_some()); + + assert!(Of::new(365, regular_year).unwrap().succ().is_none()); + assert!(Of::new(365, leap_year).unwrap().succ().is_some()); + assert!(Of::new(366, leap_year).unwrap().succ().is_none()); + + assert!(Of::new(1, regular_year).unwrap().pred().is_none()); + assert!(Of::new(1, leap_year).unwrap().pred().is_none()); + } + + #[test] + fn test_weekday_from_u32_mod7() { + for i in 0..=1000 { + assert_eq!(weekday_from_u32_mod7(i), Weekday::try_from((i % 7) as u8).unwrap()); + } + assert_eq!(weekday_from_u32_mod7(u32::MAX), Weekday::Thu); + } } |