• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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