1 //! Internal helper types for working with dates. 2 3 #![cfg_attr(feature = "__internal_bench", allow(missing_docs))] 4 5 use core::fmt; 6 7 /// Year flags (aka the dominical letter). 8 /// 9 /// `YearFlags` are used as the last four bits of `NaiveDate`, `Mdf` and `IsoWeek`. 10 /// 11 /// There are 14 possible classes of year in the Gregorian calendar: 12 /// common and leap years starting with Monday through Sunday. 13 /// 14 /// The `YearFlags` stores this information into 4 bits `LWWW`. `L` is the leap year flag, with `1` 15 /// for the common year (this simplifies validating an ordinal in `NaiveDate`). `WWW` is a non-zero 16 /// `Weekday` of the last day in the preceding year. 17 #[allow(unreachable_pub)] // public as an alias for benchmarks only 18 #[derive(PartialEq, Eq, Copy, Clone, Hash)] 19 pub struct YearFlags(pub(super) u8); 20 21 // Weekday of the last day in the preceding year. 22 // Allows for quick day of week calculation from the 1-based ordinal. 23 const YEAR_STARTS_AFTER_MONDAY: u8 = 7; // non-zero to allow use with `NonZero*`. 24 const YEAR_STARTS_AFTER_THUESDAY: u8 = 1; 25 const YEAR_STARTS_AFTER_WEDNESDAY: u8 = 2; 26 const YEAR_STARTS_AFTER_THURSDAY: u8 = 3; 27 const YEAR_STARTS_AFTER_FRIDAY: u8 = 4; 28 const YEAR_STARTS_AFTER_SATURDAY: u8 = 5; 29 const YEAR_STARTS_AFTER_SUNDAY: u8 = 6; 30 31 const COMMON_YEAR: u8 = 1 << 3; 32 const LEAP_YEAR: u8 = 0 << 3; 33 34 pub(super) const A: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_SATURDAY); 35 pub(super) const AG: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_SATURDAY); 36 pub(super) const B: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_FRIDAY); 37 pub(super) const BA: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_FRIDAY); 38 pub(super) const C: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_THURSDAY); 39 pub(super) const CB: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_THURSDAY); 40 pub(super) const D: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_WEDNESDAY); 41 pub(super) const DC: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_WEDNESDAY); 42 pub(super) const E: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_THUESDAY); 43 pub(super) const ED: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_THUESDAY); 44 pub(super) const F: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_MONDAY); 45 pub(super) const FE: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_MONDAY); 46 pub(super) const G: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_SUNDAY); 47 pub(super) const GF: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_SUNDAY); 48 49 const YEAR_TO_FLAGS: &[YearFlags; 400] = &[ 50 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, 51 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, 52 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, 53 E, DC, B, A, G, FE, D, C, B, AG, F, E, D, // 100 54 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, 55 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, 56 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, 57 G, FE, D, C, B, AG, F, E, D, CB, A, G, F, // 200 58 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, 59 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, 60 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, 61 B, AG, F, E, D, CB, A, G, F, ED, C, B, A, // 300 62 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, 63 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, 64 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, 65 D, CB, A, G, F, ED, C, B, A, GF, E, D, C, // 400 66 ]; 67 68 impl YearFlags { 69 #[allow(unreachable_pub)] // public as an alias for benchmarks only 70 #[doc(hidden)] // for benchmarks only 71 #[inline] 72 #[must_use] from_year(year: i32) -> YearFlags73 pub const fn from_year(year: i32) -> YearFlags { 74 let year = year.rem_euclid(400); 75 YearFlags::from_year_mod_400(year) 76 } 77 78 #[inline] from_year_mod_400(year: i32) -> YearFlags79 pub(super) const fn from_year_mod_400(year: i32) -> YearFlags { 80 YEAR_TO_FLAGS[year as usize] 81 } 82 83 #[inline] ndays(&self) -> u3284 pub(super) const fn ndays(&self) -> u32 { 85 let YearFlags(flags) = *self; 86 366 - (flags >> 3) as u32 87 } 88 89 #[inline] isoweek_delta(&self) -> u3290 pub(super) const fn isoweek_delta(&self) -> u32 { 91 let YearFlags(flags) = *self; 92 let mut delta = (flags & 0b0111) as u32; 93 if delta < 3 { 94 delta += 7; 95 } 96 delta 97 } 98 99 #[inline] nisoweeks(&self) -> u32100 pub(super) const fn nisoweeks(&self) -> u32 { 101 let YearFlags(flags) = *self; 102 52 + ((0b0000_0100_0000_0110 >> flags as usize) & 1) 103 } 104 } 105 106 impl fmt::Debug for YearFlags { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result107 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 108 let YearFlags(flags) = *self; 109 match flags { 110 0o15 => "A".fmt(f), 111 0o05 => "AG".fmt(f), 112 0o14 => "B".fmt(f), 113 0o04 => "BA".fmt(f), 114 0o13 => "C".fmt(f), 115 0o03 => "CB".fmt(f), 116 0o12 => "D".fmt(f), 117 0o02 => "DC".fmt(f), 118 0o11 => "E".fmt(f), 119 0o01 => "ED".fmt(f), 120 0o10 => "F?".fmt(f), 121 0o00 => "FE?".fmt(f), // non-canonical 122 0o17 => "F".fmt(f), 123 0o07 => "FE".fmt(f), 124 0o16 => "G".fmt(f), 125 0o06 => "GF".fmt(f), 126 _ => write!(f, "YearFlags({})", flags), 127 } 128 } 129 } 130 131 // OL: (ordinal << 1) | leap year flag 132 const MAX_OL: u32 = 366 << 1; // `(366 << 1) | 1` would be day 366 in a non-leap year 133 const MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1; 134 135 // The next table are adjustment values to convert a date encoded as month-day-leapyear to 136 // ordinal-leapyear. OL = MDL - adjustment. 137 // Dates that do not exist are encoded as `XX`. 138 const XX: i8 = 0; 139 const MDL_TO_OL: &[i8; MAX_MDL as usize + 1] = &[ 140 XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, 141 XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, 142 XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0 143 XX, XX, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 144 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 145 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 1 146 XX, XX, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 147 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 148 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, XX, XX, XX, XX, XX, // 2 149 XX, XX, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 150 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 151 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, // 3 152 XX, XX, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 153 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 154 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, XX, XX, // 4 155 XX, XX, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 156 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 157 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, // 5 158 XX, XX, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 159 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 160 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, XX, XX, // 6 161 XX, XX, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 162 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 163 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, // 7 164 XX, XX, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 165 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 166 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, // 8 167 XX, XX, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 168 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 169 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, XX, XX, // 9 170 XX, XX, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 171 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 172 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, // 10 173 XX, XX, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 174 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 175 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, XX, XX, // 11 176 XX, XX, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 177 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 178 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 179 100, // 12 180 ]; 181 182 const OL_TO_MDL: &[u8; MAX_OL as usize + 1] = &[ 183 0, 0, // 0 184 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 185 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 186 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 1 187 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 188 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 189 66, 66, 66, 66, 66, 66, 66, 66, 66, // 2 190 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 191 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 192 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, // 3 193 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 194 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 195 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, // 4 196 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 197 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 198 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, // 5 199 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 200 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 201 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, // 6 202 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 203 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 204 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, // 7 205 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 206 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 207 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, // 8 208 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 209 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 210 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, // 9 211 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 212 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 213 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, // 10 214 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 215 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 216 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, // 11 217 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 218 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 219 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 220 98, // 12 221 ]; 222 223 /// Month, day of month and year flags: `(month << 9) | (day << 4) | flags` 224 /// `M_MMMD_DDDD_LFFF` 225 /// 226 /// The whole bits except for the least 3 bits are referred as `Mdl` (month, day of month, and leap 227 /// year flag), which is an index to the `MDL_TO_OL` lookup table. 228 /// 229 /// The conversion between the packed calendar date (`Mdf`) and the ordinal date (`NaiveDate`) is 230 /// based on the moderately-sized lookup table (~1.5KB) and the packed representation is chosen for 231 /// efficient lookup. 232 /// 233 /// The methods of `Mdf` validate their inputs as late as possible. Dates that can't exist, like 234 /// February 30, can still be represented. This allows the validation to be combined with the final 235 /// table lookup, which is good for performance. 236 #[derive(PartialEq, PartialOrd, Copy, Clone)] 237 pub(super) struct Mdf(u32); 238 239 impl Mdf { 240 /// Makes a new `Mdf` value from month, day and `YearFlags`. 241 /// 242 /// This method doesn't fully validate the range of the `month` and `day` parameters, only as 243 /// much as what can't be deferred until later. The year `flags` are trusted to be correct. 244 /// 245 /// # Errors 246 /// 247 /// Returns `None` if `month > 12` or `day > 31`. 248 #[inline] new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Option<Mdf>249 pub(super) const fn new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Option<Mdf> { 250 match month <= 12 && day <= 31 { 251 true => Some(Mdf((month << 9) | (day << 4) | flags as u32)), 252 false => None, 253 } 254 } 255 256 /// Makes a new `Mdf` value from an `i32` with an ordinal and a leap year flag, and year 257 /// `flags`. 258 /// 259 /// The `ol` is trusted to be valid, and the `flags` are trusted to match it. 260 #[inline] from_ol(ol: i32, YearFlags(flags): YearFlags) -> Mdf261 pub(super) const fn from_ol(ol: i32, YearFlags(flags): YearFlags) -> Mdf { 262 debug_assert!(ol > 1 && ol <= MAX_OL as i32); 263 // Array is indexed from `[2..=MAX_OL]`, with a `0` index having a meaningless value. 264 Mdf(((ol as u32 + OL_TO_MDL[ol as usize] as u32) << 3) | flags as u32) 265 } 266 267 /// Returns the month of this `Mdf`. 268 #[inline] month(&self) -> u32269 pub(super) const fn month(&self) -> u32 { 270 let Mdf(mdf) = *self; 271 mdf >> 9 272 } 273 274 /// Replaces the month of this `Mdf`, keeping the day and flags. 275 /// 276 /// # Errors 277 /// 278 /// Returns `None` if `month > 12`. 279 #[inline] with_month(&self, month: u32) -> Option<Mdf>280 pub(super) const fn with_month(&self, month: u32) -> Option<Mdf> { 281 if month > 12 { 282 return None; 283 } 284 285 let Mdf(mdf) = *self; 286 Some(Mdf((mdf & 0b1_1111_1111) | (month << 9))) 287 } 288 289 /// Returns the day of this `Mdf`. 290 #[inline] day(&self) -> u32291 pub(super) const fn day(&self) -> u32 { 292 let Mdf(mdf) = *self; 293 (mdf >> 4) & 0b1_1111 294 } 295 296 /// Replaces the day of this `Mdf`, keeping the month and flags. 297 /// 298 /// # Errors 299 /// 300 /// Returns `None` if `day > 31`. 301 #[inline] with_day(&self, day: u32) -> Option<Mdf>302 pub(super) const fn with_day(&self, day: u32) -> Option<Mdf> { 303 if day > 31 { 304 return None; 305 } 306 307 let Mdf(mdf) = *self; 308 Some(Mdf((mdf & !0b1_1111_0000) | (day << 4))) 309 } 310 311 /// Replaces the flags of this `Mdf`, keeping the month and day. 312 #[inline] with_flags(&self, YearFlags(flags): YearFlags) -> Mdf313 pub(super) const fn with_flags(&self, YearFlags(flags): YearFlags) -> Mdf { 314 let Mdf(mdf) = *self; 315 Mdf((mdf & !0b1111) | flags as u32) 316 } 317 318 /// Returns the ordinal that corresponds to this `Mdf`. 319 /// 320 /// This does a table lookup to calculate the corresponding ordinal. It will return an error if 321 /// the `Mdl` turns out not to be a valid date. 322 /// 323 /// # Errors 324 /// 325 /// Returns `None` if `month == 0` or `day == 0`, or if a the given day does not exist in the 326 /// given month. 327 #[inline] ordinal(&self) -> Option<u32>328 pub(super) const fn ordinal(&self) -> Option<u32> { 329 let mdl = self.0 >> 3; 330 match MDL_TO_OL[mdl as usize] { 331 XX => None, 332 v => Some((mdl - v as u8 as u32) >> 1), 333 } 334 } 335 336 /// Returns the year flags of this `Mdf`. 337 #[inline] year_flags(&self) -> YearFlags338 pub(super) const fn year_flags(&self) -> YearFlags { 339 YearFlags((self.0 & 0b1111) as u8) 340 } 341 342 /// Returns the ordinal that corresponds to this `Mdf`, encoded as a value including year flags. 343 /// 344 /// This does a table lookup to calculate the corresponding ordinal. It will return an error if 345 /// the `Mdl` turns out not to be a valid date. 346 /// 347 /// # Errors 348 /// 349 /// Returns `None` if `month == 0` or `day == 0`, or if a the given day does not exist in the 350 /// given month. 351 #[inline] ordinal_and_flags(&self) -> Option<i32>352 pub(super) const fn ordinal_and_flags(&self) -> Option<i32> { 353 let mdl = self.0 >> 3; 354 match MDL_TO_OL[mdl as usize] { 355 XX => None, 356 v => Some(self.0 as i32 - ((v as i32) << 3)), 357 } 358 } 359 360 #[cfg(test)] valid(&self) -> bool361 fn valid(&self) -> bool { 362 let mdl = self.0 >> 3; 363 MDL_TO_OL[mdl as usize] > 0 364 } 365 } 366 367 impl fmt::Debug for Mdf { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result368 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 369 let Mdf(mdf) = *self; 370 write!( 371 f, 372 "Mdf(({} << 9) | ({} << 4) | {:#04o} /*{:?}*/)", 373 mdf >> 9, 374 (mdf >> 4) & 0b1_1111, 375 mdf & 0b1111, 376 YearFlags((mdf & 0b1111) as u8) 377 ) 378 } 379 } 380 381 #[cfg(test)] 382 mod tests { 383 use super::Mdf; 384 use super::{YearFlags, A, AG, B, BA, C, CB, D, DC, E, ED, F, FE, G, GF}; 385 386 const NONLEAP_FLAGS: [YearFlags; 7] = [A, B, C, D, E, F, G]; 387 const LEAP_FLAGS: [YearFlags; 7] = [AG, BA, CB, DC, ED, FE, GF]; 388 const FLAGS: [YearFlags; 14] = [A, B, C, D, E, F, G, AG, BA, CB, DC, ED, FE, GF]; 389 390 #[test] test_year_flags_ndays_from_year()391 fn test_year_flags_ndays_from_year() { 392 assert_eq!(YearFlags::from_year(2014).ndays(), 365); 393 assert_eq!(YearFlags::from_year(2012).ndays(), 366); 394 assert_eq!(YearFlags::from_year(2000).ndays(), 366); 395 assert_eq!(YearFlags::from_year(1900).ndays(), 365); 396 assert_eq!(YearFlags::from_year(1600).ndays(), 366); 397 assert_eq!(YearFlags::from_year(1).ndays(), 365); 398 assert_eq!(YearFlags::from_year(0).ndays(), 366); // 1 BCE (proleptic Gregorian) 399 assert_eq!(YearFlags::from_year(-1).ndays(), 365); // 2 BCE 400 assert_eq!(YearFlags::from_year(-4).ndays(), 366); // 5 BCE 401 assert_eq!(YearFlags::from_year(-99).ndays(), 365); // 100 BCE 402 assert_eq!(YearFlags::from_year(-100).ndays(), 365); // 101 BCE 403 assert_eq!(YearFlags::from_year(-399).ndays(), 365); // 400 BCE 404 assert_eq!(YearFlags::from_year(-400).ndays(), 366); // 401 BCE 405 } 406 407 #[test] test_year_flags_nisoweeks()408 fn test_year_flags_nisoweeks() { 409 assert_eq!(A.nisoweeks(), 52); 410 assert_eq!(B.nisoweeks(), 52); 411 assert_eq!(C.nisoweeks(), 52); 412 assert_eq!(D.nisoweeks(), 53); 413 assert_eq!(E.nisoweeks(), 52); 414 assert_eq!(F.nisoweeks(), 52); 415 assert_eq!(G.nisoweeks(), 52); 416 assert_eq!(AG.nisoweeks(), 52); 417 assert_eq!(BA.nisoweeks(), 52); 418 assert_eq!(CB.nisoweeks(), 52); 419 assert_eq!(DC.nisoweeks(), 53); 420 assert_eq!(ED.nisoweeks(), 53); 421 assert_eq!(FE.nisoweeks(), 52); 422 assert_eq!(GF.nisoweeks(), 52); 423 } 424 425 #[test] test_mdf_valid()426 fn test_mdf_valid() { 427 fn check(expected: bool, flags: YearFlags, month1: u32, day1: u32, month2: u32, day2: u32) { 428 for month in month1..=month2 { 429 for day in day1..=day2 { 430 let mdf = match Mdf::new(month, day, flags) { 431 Some(mdf) => mdf, 432 None if !expected => continue, 433 None => panic!("Mdf::new({}, {}, {:?}) returned None", month, day, flags), 434 }; 435 436 assert!( 437 mdf.valid() == expected, 438 "month {} day {} = {:?} should be {} for dominical year {:?}", 439 month, 440 day, 441 mdf, 442 if expected { "valid" } else { "invalid" }, 443 flags 444 ); 445 } 446 } 447 } 448 449 for &flags in NONLEAP_FLAGS.iter() { 450 check(false, flags, 0, 0, 0, 1024); 451 check(false, flags, 0, 0, 16, 0); 452 check(true, flags, 1, 1, 1, 31); 453 check(false, flags, 1, 32, 1, 1024); 454 check(true, flags, 2, 1, 2, 28); 455 check(false, flags, 2, 29, 2, 1024); 456 check(true, flags, 3, 1, 3, 31); 457 check(false, flags, 3, 32, 3, 1024); 458 check(true, flags, 4, 1, 4, 30); 459 check(false, flags, 4, 31, 4, 1024); 460 check(true, flags, 5, 1, 5, 31); 461 check(false, flags, 5, 32, 5, 1024); 462 check(true, flags, 6, 1, 6, 30); 463 check(false, flags, 6, 31, 6, 1024); 464 check(true, flags, 7, 1, 7, 31); 465 check(false, flags, 7, 32, 7, 1024); 466 check(true, flags, 8, 1, 8, 31); 467 check(false, flags, 8, 32, 8, 1024); 468 check(true, flags, 9, 1, 9, 30); 469 check(false, flags, 9, 31, 9, 1024); 470 check(true, flags, 10, 1, 10, 31); 471 check(false, flags, 10, 32, 10, 1024); 472 check(true, flags, 11, 1, 11, 30); 473 check(false, flags, 11, 31, 11, 1024); 474 check(true, flags, 12, 1, 12, 31); 475 check(false, flags, 12, 32, 12, 1024); 476 check(false, flags, 13, 0, 16, 1024); 477 check(false, flags, u32::MAX, 0, u32::MAX, 1024); 478 check(false, flags, 0, u32::MAX, 16, u32::MAX); 479 check(false, flags, u32::MAX, u32::MAX, u32::MAX, u32::MAX); 480 } 481 482 for &flags in LEAP_FLAGS.iter() { 483 check(false, flags, 0, 0, 0, 1024); 484 check(false, flags, 0, 0, 16, 0); 485 check(true, flags, 1, 1, 1, 31); 486 check(false, flags, 1, 32, 1, 1024); 487 check(true, flags, 2, 1, 2, 29); 488 check(false, flags, 2, 30, 2, 1024); 489 check(true, flags, 3, 1, 3, 31); 490 check(false, flags, 3, 32, 3, 1024); 491 check(true, flags, 4, 1, 4, 30); 492 check(false, flags, 4, 31, 4, 1024); 493 check(true, flags, 5, 1, 5, 31); 494 check(false, flags, 5, 32, 5, 1024); 495 check(true, flags, 6, 1, 6, 30); 496 check(false, flags, 6, 31, 6, 1024); 497 check(true, flags, 7, 1, 7, 31); 498 check(false, flags, 7, 32, 7, 1024); 499 check(true, flags, 8, 1, 8, 31); 500 check(false, flags, 8, 32, 8, 1024); 501 check(true, flags, 9, 1, 9, 30); 502 check(false, flags, 9, 31, 9, 1024); 503 check(true, flags, 10, 1, 10, 31); 504 check(false, flags, 10, 32, 10, 1024); 505 check(true, flags, 11, 1, 11, 30); 506 check(false, flags, 11, 31, 11, 1024); 507 check(true, flags, 12, 1, 12, 31); 508 check(false, flags, 12, 32, 12, 1024); 509 check(false, flags, 13, 0, 16, 1024); 510 check(false, flags, u32::MAX, 0, u32::MAX, 1024); 511 check(false, flags, 0, u32::MAX, 16, u32::MAX); 512 check(false, flags, u32::MAX, u32::MAX, u32::MAX, u32::MAX); 513 } 514 } 515 516 #[test] test_mdf_fields()517 fn test_mdf_fields() { 518 for &flags in FLAGS.iter() { 519 for month in 1u32..=12 { 520 for day in 1u32..31 { 521 let mdf = match Mdf::new(month, day, flags) { 522 Some(mdf) => mdf, 523 None => continue, 524 }; 525 526 if mdf.valid() { 527 assert_eq!(mdf.month(), month); 528 assert_eq!(mdf.day(), day); 529 } 530 } 531 } 532 } 533 } 534 535 #[test] test_mdf_with_fields()536 fn test_mdf_with_fields() { 537 fn check(flags: YearFlags, month: u32, day: u32) { 538 let mdf = Mdf::new(month, day, flags).unwrap(); 539 540 for month in 0u32..=16 { 541 let mdf = match mdf.with_month(month) { 542 Some(mdf) => mdf, 543 None if month > 12 => continue, 544 None => panic!("failed to create Mdf with month {}", month), 545 }; 546 547 if mdf.valid() { 548 assert_eq!(mdf.month(), month); 549 assert_eq!(mdf.day(), day); 550 } 551 } 552 553 for day in 0u32..=1024 { 554 let mdf = match mdf.with_day(day) { 555 Some(mdf) => mdf, 556 None if day > 31 => continue, 557 None => panic!("failed to create Mdf with month {}", month), 558 }; 559 560 if mdf.valid() { 561 assert_eq!(mdf.month(), month); 562 assert_eq!(mdf.day(), day); 563 } 564 } 565 } 566 567 for &flags in NONLEAP_FLAGS.iter() { 568 check(flags, 1, 1); 569 check(flags, 1, 31); 570 check(flags, 2, 1); 571 check(flags, 2, 28); 572 check(flags, 2, 29); 573 check(flags, 12, 31); 574 } 575 for &flags in LEAP_FLAGS.iter() { 576 check(flags, 1, 1); 577 check(flags, 1, 31); 578 check(flags, 2, 1); 579 check(flags, 2, 29); 580 check(flags, 2, 30); 581 check(flags, 12, 31); 582 } 583 } 584 585 #[test] test_mdf_new_range()586 fn test_mdf_new_range() { 587 let flags = YearFlags::from_year(2023); 588 assert!(Mdf::new(13, 1, flags).is_none()); 589 assert!(Mdf::new(1, 32, flags).is_none()); 590 } 591 } 592