/// Year flags (aka the dominical letter). /// /// `YearFlags` are used as the last four bits of `NaiveDate`, `Mdf` and `IsoWeek`. /// /// There are 14 possible classes of year in the Gregorian calendar: /// common and leap years starting with Monday through Sunday. /// /// The `YearFlags` stores this information into 4 bits `LWWW`. `L` is the leap year flag, with `1` /// for the common year (this simplifies validating an ordinal in `NaiveDate`). `WWW` is a non-zero /// `Weekday` of the last day in the preceding year. #[allow(unreachable_pub)] // public as an alias for benchmarks only #[derive(PartialEq, Eq, Copy, Clone, Hash)] pubstruct YearFlags(pub(super) u8);
// Weekday of the last day in the preceding year. // Allows for quick day of week calculation from the 1-based ordinal. const YEAR_STARTS_AFTER_MONDAY: u8 = 7; // non-zero to allow use with `NonZero*`. const YEAR_STARTS_AFTER_THUESDAY: u8 = 1; const YEAR_STARTS_AFTER_WEDNESDAY: u8 = 2; const YEAR_STARTS_AFTER_THURSDAY: u8 = 3; const YEAR_STARTS_AFTER_FRIDAY: u8 = 4; const YEAR_STARTS_AFTER_SATURDAY: u8 = 5; const YEAR_STARTS_AFTER_SUNDAY: u8 = 6;
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,
E, DC, B, A, G, FE, D, C, B, AG, F, E, D, // 100
C, 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, E, DC, B, A,
G, FE, D, C, B, AG, F, E, D, CB, A, G, F, // 200
E, 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, E, DC, B, A, G, FE, D, C,
B, AG, F, E, D, CB, A, G, F, ED, C, B, A, // 300
G, 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, 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, // 400
];
impl YearFlags { #[allow(unreachable_pub)] // public as an alias for benchmarks only #[doc(hidden)] // for benchmarks only #[inline] #[must_use] pubconstfn from_year(year: i32) -> YearFlags { let year = year.rem_euclid(400);
YearFlags::from_year_mod_400(year)
}
/// Month, day of month and year flags: `(month << 9) | (day << 4) | flags` /// `M_MMMD_DDDD_LFFF` /// /// The whole bits except for the least 3 bits are referred as `Mdl` (month, day of month, and leap /// year flag), which is an index to the `MDL_TO_OL` lookup table. /// /// The conversion between the packed calendar date (`Mdf`) and the ordinal date (`NaiveDate`) is /// based on the moderately-sized lookup table (~1.5KB) and the packed representation is chosen for /// efficient lookup. /// /// The methods of `Mdf` validate their inputs as late as possible. Dates that can't exist, like /// February 30, can still be represented. This allows the validation to be combined with the final /// table lookup, which is good for performance. #[derive(PartialEq, PartialOrd, Copy, Clone)] pub(super) struct Mdf(u32);
impl Mdf { /// Makes a new `Mdf` value from month, day and `YearFlags`. /// /// This method doesn't fully validate the range of the `month` and `day` parameters, only as /// much as what can't be deferred until later. The year `flags` are trusted to be correct. /// /// # Errors /// /// Returns `None` if `month > 12` or `day > 31`. #[inline] pub(super) constfn new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Option<Mdf> { match month <= 12 && day <= 31 { true => Some(Mdf((month << 9) | (day << 4) | flags as u32)), false => None,
}
}
/// Makes a new `Mdf` value from an `i32` with an ordinal and a leap year flag, and year /// `flags`. /// /// The `ol` is trusted to be valid, and the `flags` are trusted to match it. #[inline] pub(super) constfn from_ol(ol: i32, YearFlags(flags): YearFlags) -> Mdf {
debug_assert!(ol > 1 && ol <= MAX_OL as i32); // Array is indexed from `[2..=MAX_OL]`, with a `0` index having a meaningless value.
Mdf(((ol as u32 + OL_TO_MDL[ol as usize] as u32) << 3) | flags as u32)
}
/// Returns the month of this `Mdf`. #[inline] pub(super) constfn month(&self) -> u32 { let Mdf(mdf) = *self;
mdf >> 9
}
/// Replaces the month of this `Mdf`, keeping the day and flags. /// /// # Errors /// /// Returns `None` if `month > 12`. #[inline] pub(super) constfn with_month(&self, month: u32) -> Option<Mdf> { if month > 12 { return None;
}
/// Returns the day of this `Mdf`. #[inline] pub(super) constfn day(&self) -> u32 { let Mdf(mdf) = *self;
(mdf >> 4) & 0b1_1111
}
/// Replaces the day of this `Mdf`, keeping the month and flags. /// /// # Errors /// /// Returns `None` if `day > 31`. #[inline] pub(super) constfn with_day(&self, day: u32) -> Option<Mdf> { if day > 31 { return None;
}
/// Replaces the flags of this `Mdf`, keeping the month and day. #[inline] pub(super) constfn with_flags(&self, YearFlags(flags): YearFlags) -> Mdf { let Mdf(mdf) = *self;
Mdf((mdf & !0b1111) | flags as u32)
}
/// Returns the ordinal that corresponds to this `Mdf`. /// /// This does a table lookup to calculate the corresponding ordinal. It will return an error if /// the `Mdl` turns out not to be a valid date. /// /// # Errors /// /// Returns `None` if `month == 0` or `day == 0`, or if a the given day does not exist in the /// given month. #[inline] pub(super) constfn ordinal(&self) -> Option<u32> { let mdl = self.0 >> 3; match MDL_TO_OL[mdl as usize] {
XX => None,
v => Some((mdl - v as u8 as u32) >> 1),
}
}
/// Returns the year flags of this `Mdf`. #[inline] pub(super) constfn year_flags(&self) -> YearFlags {
YearFlags((self.0 & 0b1111) as u8)
}
/// Returns the ordinal that corresponds to this `Mdf`, encoded as a value including year flags. /// /// This does a table lookup to calculate the corresponding ordinal. It will return an error if /// the `Mdl` turns out not to be a valid date. /// /// # Errors /// /// Returns `None` if `month == 0` or `day == 0`, or if a the given day does not exist in the /// given month. #[inline] pub(super) constfn ordinal_and_flags(&self) -> Option<i32> { let mdl = self.0 >> 3; match MDL_TO_OL[mdl as usize] {
XX => None,
v => Some(self.0as i32 - ((v as i32) << 3)),
}
}
#[cfg(test)] fn valid(&self) -> bool { let mdl = self.0 >> 3;
MDL_TO_OL[mdl as usize] > 0
}
}
#[test] fn test_mdf_fields() { for &flags in FLAGS.iter() { for month in1u32..=12 { for day in1u32..31 { let mdf = match Mdf::new(month, day, flags) {
Some(mdf) => mdf,
None => continue,
};
for month in0u32..=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}"),
};
if mdf.valid() {
assert_eq!(mdf.month(), month);
assert_eq!(mdf.day(), day);
}
}
for day in0u32..=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);
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.