1 // This is a part of Chrono.
2 // See README.md and LICENSE.txt for details.
3 
4 //! The internal implementation of the calendar and ordinal date.
5 //!
6 //! The current implementation is optimized for determining year, month, day and day of week.
7 //! 4-bit `YearFlags` map to one of 14 possible classes of year in the Gregorian calendar,
8 //! which are included in every packed `NaiveDate` instance.
9 //! The conversion between the packed calendar date (`Mdf`) and the ordinal date (`Of`) is
10 //! based on the moderately-sized lookup table (~1.5KB)
11 //! and the packed representation is chosen for the efficient lookup.
12 //! Every internal data structure does not validate its input,
13 //! but the conversion keeps the valid value valid and the invalid value invalid
14 //! so that the user-facing `NaiveDate` can validate the input as late as possible.
15 
16 #![cfg_attr(feature = "__internal_bench", allow(missing_docs))]
17 
18 use crate::Weekday;
19 use core::fmt;
20 
21 /// The internal date representation: `year << 13 | Of`
22 pub(super) type DateImpl = i32;
23 
24 /// MAX_YEAR is one year less than the type is capable of representing. Internally we may sometimes
25 /// use the headroom, notably to handle cases where the offset of a `DateTime` constructed with
26 /// `NaiveDate::MAX` pushes it beyond the valid, representable range.
27 pub(super) const MAX_YEAR: DateImpl = (i32::MAX >> 13) - 1;
28 
29 /// MIN_YEAR is one year more than the type is capable of representing. Internally we may sometimes
30 /// use the headroom, notably to handle cases where the offset of a `DateTime` constructed with
31 /// `NaiveDate::MIN` pushes it beyond the valid, representable range.
32 pub(super) const MIN_YEAR: DateImpl = (i32::MIN >> 13) + 1;
33 
34 /// The year flags (aka the dominical letter).
35 ///
36 /// There are 14 possible classes of year in the Gregorian calendar:
37 /// common and leap years starting with Monday through Sunday.
38 /// The `YearFlags` stores this information into 4 bits `abbb`,
39 /// where `a` is `1` for the common year (simplifies the `Of` validation)
40 /// and `bbb` is a non-zero `Weekday` (mapping `Mon` to 7) of the last day in the past year
41 /// (simplifies the day of week calculation from the 1-based ordinal).
42 #[allow(unreachable_pub)] // public as an alias for benchmarks only
43 #[derive(PartialEq, Eq, Copy, Clone, Hash)]
44 pub struct YearFlags(pub(super) u8);
45 
46 pub(super) const A: YearFlags = YearFlags(0o15);
47 pub(super) const AG: YearFlags = YearFlags(0o05);
48 pub(super) const B: YearFlags = YearFlags(0o14);
49 pub(super) const BA: YearFlags = YearFlags(0o04);
50 pub(super) const C: YearFlags = YearFlags(0o13);
51 pub(super) const CB: YearFlags = YearFlags(0o03);
52 pub(super) const D: YearFlags = YearFlags(0o12);
53 pub(super) const DC: YearFlags = YearFlags(0o02);
54 pub(super) const E: YearFlags = YearFlags(0o11);
55 pub(super) const ED: YearFlags = YearFlags(0o01);
56 pub(super) const F: YearFlags = YearFlags(0o17);
57 pub(super) const FE: YearFlags = YearFlags(0o07);
58 pub(super) const G: YearFlags = YearFlags(0o16);
59 pub(super) const GF: YearFlags = YearFlags(0o06);
60 
61 const YEAR_TO_FLAGS: &[YearFlags; 400] = &[
62     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,
63     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,
64     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,
65     E, DC, B, A, G, FE, D, C, B, AG, F, E, D, // 100
66     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,
67     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,
68     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,
69     G, FE, D, C, B, AG, F, E, D, CB, A, G, F, // 200
70     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,
71     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,
72     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,
73     B, AG, F, E, D, CB, A, G, F, ED, C, B, A, // 300
74     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,
75     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,
76     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,
77     D, CB, A, G, F, ED, C, B, A, GF, E, D, C, // 400
78 ];
79 
80 const YEAR_DELTAS: &[u8; 401] = &[
81     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,
82     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,
83     15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20,
84     21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, // 100
85     25, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30,
86     30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36,
87     36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42,
88     42, 43, 43, 43, 43, 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 48, 48, 48,
89     48, 49, 49, 49, // 200
90     49, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54,
91     54, 55, 55, 55, 55, 56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, 60, 60, 60,
92     60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63, 64, 64, 64, 64, 65, 65, 65, 65, 66, 66, 66,
93     66, 67, 67, 67, 67, 68, 68, 68, 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, 72,
94     72, 73, 73, 73, // 300
95     73, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, 77, 78, 78, 78,
96     78, 79, 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82, 82, 82, 82, 83, 83, 83, 83, 84, 84, 84,
97     84, 85, 85, 85, 85, 86, 86, 86, 86, 87, 87, 87, 87, 88, 88, 88, 88, 89, 89, 89, 89, 90, 90, 90,
98     90, 91, 91, 91, 91, 92, 92, 92, 92, 93, 93, 93, 93, 94, 94, 94, 94, 95, 95, 95, 95, 96, 96, 96,
99     96, 97, 97, 97, 97, // 400+1
100 ];
101 
cycle_to_yo(cycle: u32) -> (u32, u32)102 pub(super) const fn cycle_to_yo(cycle: u32) -> (u32, u32) {
103     let mut year_mod_400 = cycle / 365;
104     let mut ordinal0 = cycle % 365;
105     let delta = YEAR_DELTAS[year_mod_400 as usize] as u32;
106     if ordinal0 < delta {
107         year_mod_400 -= 1;
108         ordinal0 += 365 - YEAR_DELTAS[year_mod_400 as usize] as u32;
109     } else {
110         ordinal0 -= delta;
111     }
112     (year_mod_400, ordinal0 + 1)
113 }
114 
yo_to_cycle(year_mod_400: u32, ordinal: u32) -> u32115 pub(super) const fn yo_to_cycle(year_mod_400: u32, ordinal: u32) -> u32 {
116     year_mod_400 * 365 + YEAR_DELTAS[year_mod_400 as usize] as u32 + ordinal - 1
117 }
118 
119 impl YearFlags {
120     #[allow(unreachable_pub)] // public as an alias for benchmarks only
121     #[doc(hidden)] // for benchmarks only
122     #[inline]
123     #[must_use]
from_year(year: i32) -> YearFlags124     pub const fn from_year(year: i32) -> YearFlags {
125         let year = year.rem_euclid(400);
126         YearFlags::from_year_mod_400(year)
127     }
128 
129     #[inline]
from_year_mod_400(year: i32) -> YearFlags130     pub(super) const fn from_year_mod_400(year: i32) -> YearFlags {
131         YEAR_TO_FLAGS[year as usize]
132     }
133 
134     #[inline]
ndays(&self) -> u32135     pub(super) const fn ndays(&self) -> u32 {
136         let YearFlags(flags) = *self;
137         366 - (flags >> 3) as u32
138     }
139 
140     #[inline]
isoweek_delta(&self) -> u32141     pub(super) const fn isoweek_delta(&self) -> u32 {
142         let YearFlags(flags) = *self;
143         let mut delta = (flags & 0b0111) as u32;
144         if delta < 3 {
145             delta += 7;
146         }
147         delta
148     }
149 
150     #[inline]
nisoweeks(&self) -> u32151     pub(super) const fn nisoweeks(&self) -> u32 {
152         let YearFlags(flags) = *self;
153         52 + ((0b0000_0100_0000_0110 >> flags as usize) & 1)
154     }
155 }
156 
157 impl fmt::Debug for YearFlags {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result158     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
159         let YearFlags(flags) = *self;
160         match flags {
161             0o15 => "A".fmt(f),
162             0o05 => "AG".fmt(f),
163             0o14 => "B".fmt(f),
164             0o04 => "BA".fmt(f),
165             0o13 => "C".fmt(f),
166             0o03 => "CB".fmt(f),
167             0o12 => "D".fmt(f),
168             0o02 => "DC".fmt(f),
169             0o11 => "E".fmt(f),
170             0o01 => "ED".fmt(f),
171             0o10 => "F?".fmt(f),
172             0o00 => "FE?".fmt(f), // non-canonical
173             0o17 => "F".fmt(f),
174             0o07 => "FE".fmt(f),
175             0o16 => "G".fmt(f),
176             0o06 => "GF".fmt(f),
177             _ => write!(f, "YearFlags({})", flags),
178         }
179     }
180 }
181 
182 // OL: (ordinal << 1) | leap year flag
183 pub(super) const MIN_OL: u32 = 1 << 1;
184 pub(super) const MAX_OL: u32 = 366 << 1; // `(366 << 1) | 1` would be day 366 in a non-leap year
185 pub(super) const MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1;
186 
187 const XX: i8 = -128;
188 const MDL_TO_OL: &[i8; MAX_MDL as usize + 1] = &[
189     XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
190     XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
191     XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0
192     XX, XX, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
193     64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
194     64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 1
195     XX, XX, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
196     66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
197     66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, XX, XX, XX, XX, XX, // 2
198     XX, XX, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74,
199     72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74,
200     72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, // 3
201     XX, XX, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76,
202     74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76,
203     74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, XX, XX, // 4
204     XX, XX, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80,
205     78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80,
206     78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, // 5
207     XX, XX, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82,
208     80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82,
209     80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, XX, XX, // 6
210     XX, XX, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86,
211     84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86,
212     84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, // 7
213     XX, XX, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88,
214     86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88,
215     86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, // 8
216     XX, XX, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90,
217     88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90,
218     88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, XX, XX, // 9
219     XX, XX, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94,
220     92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94,
221     92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, // 10
222     XX, XX, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96,
223     94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96,
224     94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, XX, XX, // 11
225     XX, XX, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98,
226     100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100,
227     98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98,
228     100, // 12
229 ];
230 
231 const OL_TO_MDL: &[u8; MAX_OL as usize + 1] = &[
232     0, 0, // 0
233     64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
234     64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
235     64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 1
236     66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
237     66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
238     66, 66, 66, 66, 66, 66, 66, 66, 66, // 2
239     74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72,
240     74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72,
241     74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, // 3
242     76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74,
243     76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74,
244     76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, // 4
245     80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78,
246     80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78,
247     80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, // 5
248     82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80,
249     82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80,
250     82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, // 6
251     86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84,
252     86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84,
253     86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, // 7
254     88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86,
255     88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86,
256     88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, // 8
257     90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88,
258     90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88,
259     90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, // 9
260     94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92,
261     94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92,
262     94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, // 10
263     96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94,
264     96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94,
265     96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, // 11
266     100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100,
267     98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98,
268     100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100,
269     98, // 12
270 ];
271 
272 /// Ordinal (day of year) and year flags: `(ordinal << 4) | flags`.
273 ///
274 /// The whole bits except for the least 3 bits are referred as `Ol` (ordinal and leap flag),
275 /// which is an index to the `OL_TO_MDL` lookup table.
276 ///
277 /// The methods implemented on `Of` always return a valid value.
278 #[derive(PartialEq, PartialOrd, Copy, Clone)]
279 pub(super) struct Of(u32);
280 
281 impl Of {
282     #[inline]
new(ordinal: u32, YearFlags(flags): YearFlags) -> Option<Of>283     pub(super) const fn new(ordinal: u32, YearFlags(flags): YearFlags) -> Option<Of> {
284         let of = Of((ordinal << 4) | flags as u32);
285         of.validate()
286     }
287 
from_date_impl(date_impl: DateImpl) -> Of288     pub(super) const fn from_date_impl(date_impl: DateImpl) -> Of {
289         // We assume the value in the `DateImpl` is valid.
290         Of((date_impl & 0b1_1111_1111_1111) as u32)
291     }
292 
293     #[inline]
from_mdf(Mdf(mdf): Mdf) -> Option<Of>294     pub(super) const fn from_mdf(Mdf(mdf): Mdf) -> Option<Of> {
295         let mdl = mdf >> 3;
296         if mdl > MAX_MDL {
297             // Panicking on out-of-bounds indexing would be reasonable, but just return `None`.
298             return None;
299         }
300         // Array is indexed from `[1..=MAX_MDL]`, with a `0` index having a meaningless value.
301         let v = MDL_TO_OL[mdl as usize];
302         let of = Of(mdf.wrapping_sub((v as i32 as u32 & 0x3ff) << 3));
303         of.validate()
304     }
305 
306     #[inline]
inner(&self) -> u32307     pub(super) const fn inner(&self) -> u32 {
308         self.0
309     }
310 
311     /// Returns `(ordinal << 1) | leap-year-flag`.
312     #[inline]
ol(&self) -> u32313     const fn ol(&self) -> u32 {
314         self.0 >> 3
315     }
316 
317     #[inline]
validate(self) -> Option<Of>318     const fn validate(self) -> Option<Of> {
319         let ol = self.ol();
320         match ol >= MIN_OL && ol <= MAX_OL {
321             true => Some(self),
322             false => None,
323         }
324     }
325 
326     #[inline]
ordinal(&self) -> u32327     pub(super) const fn ordinal(&self) -> u32 {
328         self.0 >> 4
329     }
330 
331     #[inline]
with_ordinal(&self, ordinal: u32) -> Option<Of>332     pub(super) const fn with_ordinal(&self, ordinal: u32) -> Option<Of> {
333         let of = Of((ordinal << 4) | (self.0 & 0b1111));
334         of.validate()
335     }
336 
337     #[inline]
flags(&self) -> YearFlags338     pub(super) const fn flags(&self) -> YearFlags {
339         YearFlags((self.0 & 0b1111) as u8)
340     }
341 
342     #[inline]
weekday(&self) -> Weekday343     pub(super) const fn weekday(&self) -> Weekday {
344         let Of(of) = *self;
345         weekday_from_u32_mod7((of >> 4) + (of & 0b111))
346     }
347 
348     #[inline]
isoweekdate_raw(&self) -> (u32, Weekday)349     pub(super) fn isoweekdate_raw(&self) -> (u32, Weekday) {
350         // week ordinal = ordinal + delta
351         let Of(of) = *self;
352         let weekord = (of >> 4).wrapping_add(self.flags().isoweek_delta());
353         (weekord / 7, weekday_from_u32_mod7(weekord))
354     }
355 
356     #[cfg_attr(feature = "cargo-clippy", allow(clippy::wrong_self_convention))]
357     #[inline]
to_mdf(&self) -> Mdf358     pub(super) const fn to_mdf(&self) -> Mdf {
359         Mdf::from_of(*self)
360     }
361 
362     /// Returns an `Of` with the next day, or `None` if this is the last day of the year.
363     #[inline]
succ(&self) -> Option<Of>364     pub(super) const fn succ(&self) -> Option<Of> {
365         let of = Of(self.0 + (1 << 4));
366         of.validate()
367     }
368 
369     /// Returns an `Of` with the previous day, or `None` if this is the first day of the year.
370     #[inline]
pred(&self) -> Option<Of>371     pub(super) const fn pred(&self) -> Option<Of> {
372         match self.ordinal() {
373             1 => None,
374             _ => Some(Of(self.0 - (1 << 4))),
375         }
376     }
377 }
378 
379 impl fmt::Debug for Of {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result380     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
381         let Of(of) = *self;
382         write!(
383             f,
384             "Of(({} << 4) | {:#04o} /*{:?}*/)",
385             of >> 4,
386             of & 0b1111,
387             YearFlags((of & 0b1111) as u8)
388         )
389     }
390 }
391 
392 /// Month, day of month and year flags: `(month << 9) | (day << 4) | flags`
393 ///
394 /// The whole bits except for the least 3 bits are referred as `Mdl`
395 /// (month, day of month and leap flag),
396 /// which is an index to the `MDL_TO_OL` lookup table.
397 ///
398 /// The methods implemented on `Mdf` do not always return a valid value.
399 /// Dates that can't exist, like February 30, can still be represented.
400 /// Use `Mdl::valid` to check whether the date is valid.
401 #[derive(PartialEq, PartialOrd, Copy, Clone)]
402 pub(super) struct Mdf(u32);
403 
404 impl Mdf {
405     #[inline]
new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Option<Mdf>406     pub(super) const fn new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Option<Mdf> {
407         match month >= 1 && month <= 12 && day >= 1 && day <= 31 {
408             true => Some(Mdf((month << 9) | (day << 4) | flags as u32)),
409             false => None,
410         }
411     }
412 
413     #[inline]
from_of(Of(of): Of) -> Mdf414     pub(super) const fn from_of(Of(of): Of) -> Mdf {
415         let ol = of >> 3;
416         if ol <= MAX_OL {
417             // Array is indexed from `[1..=MAX_OL]`, with a `0` index having a meaningless value.
418             Mdf(of + ((OL_TO_MDL[ol as usize] as u32) << 3))
419         } else {
420             // Panicking here would be reasonable, but we are just going on with a safe value.
421             Mdf(0)
422         }
423     }
424 
425     #[cfg(test)]
valid(&self) -> bool426     pub(super) const fn valid(&self) -> bool {
427         let Mdf(mdf) = *self;
428         let mdl = mdf >> 3;
429         if mdl <= MAX_MDL {
430             // Array is indexed from `[1..=MAX_MDL]`, with a `0` index having a meaningless value.
431             MDL_TO_OL[mdl as usize] >= 0
432         } else {
433             // Panicking here would be reasonable, but we are just going on with a safe value.
434             false
435         }
436     }
437 
438     #[inline]
month(&self) -> u32439     pub(super) const fn month(&self) -> u32 {
440         let Mdf(mdf) = *self;
441         mdf >> 9
442     }
443 
444     #[inline]
with_month(&self, month: u32) -> Option<Mdf>445     pub(super) const fn with_month(&self, month: u32) -> Option<Mdf> {
446         if month > 12 {
447             return None;
448         }
449 
450         let Mdf(mdf) = *self;
451         Some(Mdf((mdf & 0b1_1111_1111) | (month << 9)))
452     }
453 
454     #[inline]
day(&self) -> u32455     pub(super) const fn day(&self) -> u32 {
456         let Mdf(mdf) = *self;
457         (mdf >> 4) & 0b1_1111
458     }
459 
460     #[inline]
with_day(&self, day: u32) -> Option<Mdf>461     pub(super) const fn with_day(&self, day: u32) -> Option<Mdf> {
462         if day > 31 {
463             return None;
464         }
465 
466         let Mdf(mdf) = *self;
467         Some(Mdf((mdf & !0b1_1111_0000) | (day << 4)))
468     }
469 
470     #[inline]
with_flags(&self, YearFlags(flags): YearFlags) -> Mdf471     pub(super) const fn with_flags(&self, YearFlags(flags): YearFlags) -> Mdf {
472         let Mdf(mdf) = *self;
473         Mdf((mdf & !0b1111) | flags as u32)
474     }
475 
476     #[cfg_attr(feature = "cargo-clippy", allow(clippy::wrong_self_convention))]
477     #[inline]
to_of(&self) -> Option<Of>478     pub(super) const fn to_of(&self) -> Option<Of> {
479         Of::from_mdf(*self)
480     }
481 }
482 
483 impl fmt::Debug for Mdf {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result484     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
485         let Mdf(mdf) = *self;
486         write!(
487             f,
488             "Mdf(({} << 9) | ({} << 4) | {:#04o} /*{:?}*/)",
489             mdf >> 9,
490             (mdf >> 4) & 0b1_1111,
491             mdf & 0b1111,
492             YearFlags((mdf & 0b1111) as u8)
493         )
494     }
495 }
496 
497 /// Create a `Weekday` from an `u32`, with Monday = 0.
498 /// Infallible, takes any `n` and applies `% 7`.
499 #[inline]
weekday_from_u32_mod7(n: u32) -> Weekday500 const fn weekday_from_u32_mod7(n: u32) -> Weekday {
501     match n % 7 {
502         0 => Weekday::Mon,
503         1 => Weekday::Tue,
504         2 => Weekday::Wed,
505         3 => Weekday::Thu,
506         4 => Weekday::Fri,
507         5 => Weekday::Sat,
508         _ => Weekday::Sun,
509     }
510 }
511 
512 #[cfg(test)]
513 mod tests {
514     use super::weekday_from_u32_mod7;
515     use super::{Mdf, Of};
516     use super::{YearFlags, A, AG, B, BA, C, CB, D, DC, E, ED, F, FE, G, GF};
517     use crate::Weekday;
518 
519     const NONLEAP_FLAGS: [YearFlags; 7] = [A, B, C, D, E, F, G];
520     const LEAP_FLAGS: [YearFlags; 7] = [AG, BA, CB, DC, ED, FE, GF];
521     const FLAGS: [YearFlags; 14] = [A, B, C, D, E, F, G, AG, BA, CB, DC, ED, FE, GF];
522 
523     #[test]
test_year_flags_ndays_from_year()524     fn test_year_flags_ndays_from_year() {
525         assert_eq!(YearFlags::from_year(2014).ndays(), 365);
526         assert_eq!(YearFlags::from_year(2012).ndays(), 366);
527         assert_eq!(YearFlags::from_year(2000).ndays(), 366);
528         assert_eq!(YearFlags::from_year(1900).ndays(), 365);
529         assert_eq!(YearFlags::from_year(1600).ndays(), 366);
530         assert_eq!(YearFlags::from_year(1).ndays(), 365);
531         assert_eq!(YearFlags::from_year(0).ndays(), 366); // 1 BCE (proleptic Gregorian)
532         assert_eq!(YearFlags::from_year(-1).ndays(), 365); // 2 BCE
533         assert_eq!(YearFlags::from_year(-4).ndays(), 366); // 5 BCE
534         assert_eq!(YearFlags::from_year(-99).ndays(), 365); // 100 BCE
535         assert_eq!(YearFlags::from_year(-100).ndays(), 365); // 101 BCE
536         assert_eq!(YearFlags::from_year(-399).ndays(), 365); // 400 BCE
537         assert_eq!(YearFlags::from_year(-400).ndays(), 366); // 401 BCE
538     }
539 
540     #[test]
test_year_flags_nisoweeks()541     fn test_year_flags_nisoweeks() {
542         assert_eq!(A.nisoweeks(), 52);
543         assert_eq!(B.nisoweeks(), 52);
544         assert_eq!(C.nisoweeks(), 52);
545         assert_eq!(D.nisoweeks(), 53);
546         assert_eq!(E.nisoweeks(), 52);
547         assert_eq!(F.nisoweeks(), 52);
548         assert_eq!(G.nisoweeks(), 52);
549         assert_eq!(AG.nisoweeks(), 52);
550         assert_eq!(BA.nisoweeks(), 52);
551         assert_eq!(CB.nisoweeks(), 52);
552         assert_eq!(DC.nisoweeks(), 53);
553         assert_eq!(ED.nisoweeks(), 53);
554         assert_eq!(FE.nisoweeks(), 52);
555         assert_eq!(GF.nisoweeks(), 52);
556     }
557 
558     #[test]
test_of()559     fn test_of() {
560         fn check(expected: bool, flags: YearFlags, ordinal1: u32, ordinal2: u32) {
561             for ordinal in ordinal1..=ordinal2 {
562                 let of = match Of::new(ordinal, flags) {
563                     Some(of) => of,
564                     None if !expected => continue,
565                     None => panic!("Of::new({}, {:?}) returned None", ordinal, flags),
566                 };
567 
568                 assert!(
569                     of.validate().is_some() == expected,
570                     "ordinal {} = {:?} should be {} for dominical year {:?}",
571                     ordinal,
572                     of,
573                     if expected { "valid" } else { "invalid" },
574                     flags
575                 );
576             }
577         }
578 
579         for &flags in NONLEAP_FLAGS.iter() {
580             check(false, flags, 0, 0);
581             check(true, flags, 1, 365);
582             check(false, flags, 366, 1024);
583             check(false, flags, u32::MAX, u32::MAX);
584         }
585 
586         for &flags in LEAP_FLAGS.iter() {
587             check(false, flags, 0, 0);
588             check(true, flags, 1, 366);
589             check(false, flags, 367, 1024);
590             check(false, flags, u32::MAX, u32::MAX);
591         }
592     }
593 
594     #[test]
test_mdf_valid()595     fn test_mdf_valid() {
596         fn check(expected: bool, flags: YearFlags, month1: u32, day1: u32, month2: u32, day2: u32) {
597             for month in month1..=month2 {
598                 for day in day1..=day2 {
599                     let mdf = match Mdf::new(month, day, flags) {
600                         Some(mdf) => mdf,
601                         None if !expected => continue,
602                         None => panic!("Mdf::new({}, {}, {:?}) returned None", month, day, flags),
603                     };
604 
605                     assert!(
606                         mdf.valid() == expected,
607                         "month {} day {} = {:?} should be {} for dominical year {:?}",
608                         month,
609                         day,
610                         mdf,
611                         if expected { "valid" } else { "invalid" },
612                         flags
613                     );
614                 }
615             }
616         }
617 
618         for &flags in NONLEAP_FLAGS.iter() {
619             check(false, flags, 0, 0, 0, 1024);
620             check(false, flags, 0, 0, 16, 0);
621             check(true, flags, 1, 1, 1, 31);
622             check(false, flags, 1, 32, 1, 1024);
623             check(true, flags, 2, 1, 2, 28);
624             check(false, flags, 2, 29, 2, 1024);
625             check(true, flags, 3, 1, 3, 31);
626             check(false, flags, 3, 32, 3, 1024);
627             check(true, flags, 4, 1, 4, 30);
628             check(false, flags, 4, 31, 4, 1024);
629             check(true, flags, 5, 1, 5, 31);
630             check(false, flags, 5, 32, 5, 1024);
631             check(true, flags, 6, 1, 6, 30);
632             check(false, flags, 6, 31, 6, 1024);
633             check(true, flags, 7, 1, 7, 31);
634             check(false, flags, 7, 32, 7, 1024);
635             check(true, flags, 8, 1, 8, 31);
636             check(false, flags, 8, 32, 8, 1024);
637             check(true, flags, 9, 1, 9, 30);
638             check(false, flags, 9, 31, 9, 1024);
639             check(true, flags, 10, 1, 10, 31);
640             check(false, flags, 10, 32, 10, 1024);
641             check(true, flags, 11, 1, 11, 30);
642             check(false, flags, 11, 31, 11, 1024);
643             check(true, flags, 12, 1, 12, 31);
644             check(false, flags, 12, 32, 12, 1024);
645             check(false, flags, 13, 0, 16, 1024);
646             check(false, flags, u32::MAX, 0, u32::MAX, 1024);
647             check(false, flags, 0, u32::MAX, 16, u32::MAX);
648             check(false, flags, u32::MAX, u32::MAX, u32::MAX, u32::MAX);
649         }
650 
651         for &flags in LEAP_FLAGS.iter() {
652             check(false, flags, 0, 0, 0, 1024);
653             check(false, flags, 0, 0, 16, 0);
654             check(true, flags, 1, 1, 1, 31);
655             check(false, flags, 1, 32, 1, 1024);
656             check(true, flags, 2, 1, 2, 29);
657             check(false, flags, 2, 30, 2, 1024);
658             check(true, flags, 3, 1, 3, 31);
659             check(false, flags, 3, 32, 3, 1024);
660             check(true, flags, 4, 1, 4, 30);
661             check(false, flags, 4, 31, 4, 1024);
662             check(true, flags, 5, 1, 5, 31);
663             check(false, flags, 5, 32, 5, 1024);
664             check(true, flags, 6, 1, 6, 30);
665             check(false, flags, 6, 31, 6, 1024);
666             check(true, flags, 7, 1, 7, 31);
667             check(false, flags, 7, 32, 7, 1024);
668             check(true, flags, 8, 1, 8, 31);
669             check(false, flags, 8, 32, 8, 1024);
670             check(true, flags, 9, 1, 9, 30);
671             check(false, flags, 9, 31, 9, 1024);
672             check(true, flags, 10, 1, 10, 31);
673             check(false, flags, 10, 32, 10, 1024);
674             check(true, flags, 11, 1, 11, 30);
675             check(false, flags, 11, 31, 11, 1024);
676             check(true, flags, 12, 1, 12, 31);
677             check(false, flags, 12, 32, 12, 1024);
678             check(false, flags, 13, 0, 16, 1024);
679             check(false, flags, u32::MAX, 0, u32::MAX, 1024);
680             check(false, flags, 0, u32::MAX, 16, u32::MAX);
681             check(false, flags, u32::MAX, u32::MAX, u32::MAX, u32::MAX);
682         }
683     }
684 
685     #[test]
test_of_fields()686     fn test_of_fields() {
687         for &flags in FLAGS.iter() {
688             for ordinal in 1u32..=366 {
689                 if let Some(of) = Of::new(ordinal, flags) {
690                     assert_eq!(of.ordinal(), ordinal);
691                 }
692             }
693         }
694     }
695 
696     #[test]
test_of_with_fields()697     fn test_of_with_fields() {
698         fn check(flags: YearFlags, ordinal: u32) {
699             let of = Of::new(ordinal, flags).unwrap();
700 
701             for ordinal in 0u32..=1024 {
702                 let of = of.with_ordinal(ordinal);
703                 assert_eq!(of, Of::new(ordinal, flags));
704                 if let Some(of) = of {
705                     assert_eq!(of.ordinal(), ordinal);
706                 }
707             }
708         }
709 
710         for &flags in NONLEAP_FLAGS.iter() {
711             check(flags, 1);
712             check(flags, 365);
713         }
714         for &flags in LEAP_FLAGS.iter() {
715             check(flags, 1);
716             check(flags, 366);
717         }
718     }
719 
720     #[test]
test_of_weekday()721     fn test_of_weekday() {
722         assert_eq!(Of::new(1, A).unwrap().weekday(), Weekday::Sun);
723         assert_eq!(Of::new(1, B).unwrap().weekday(), Weekday::Sat);
724         assert_eq!(Of::new(1, C).unwrap().weekday(), Weekday::Fri);
725         assert_eq!(Of::new(1, D).unwrap().weekday(), Weekday::Thu);
726         assert_eq!(Of::new(1, E).unwrap().weekday(), Weekday::Wed);
727         assert_eq!(Of::new(1, F).unwrap().weekday(), Weekday::Tue);
728         assert_eq!(Of::new(1, G).unwrap().weekday(), Weekday::Mon);
729         assert_eq!(Of::new(1, AG).unwrap().weekday(), Weekday::Sun);
730         assert_eq!(Of::new(1, BA).unwrap().weekday(), Weekday::Sat);
731         assert_eq!(Of::new(1, CB).unwrap().weekday(), Weekday::Fri);
732         assert_eq!(Of::new(1, DC).unwrap().weekday(), Weekday::Thu);
733         assert_eq!(Of::new(1, ED).unwrap().weekday(), Weekday::Wed);
734         assert_eq!(Of::new(1, FE).unwrap().weekday(), Weekday::Tue);
735         assert_eq!(Of::new(1, GF).unwrap().weekday(), Weekday::Mon);
736 
737         for &flags in FLAGS.iter() {
738             let mut prev = Of::new(1, flags).unwrap().weekday();
739             for ordinal in 2u32..=flags.ndays() {
740                 let of = Of::new(ordinal, flags).unwrap();
741                 let expected = prev.succ();
742                 assert_eq!(of.weekday(), expected);
743                 prev = expected;
744             }
745         }
746     }
747 
748     #[test]
test_mdf_fields()749     fn test_mdf_fields() {
750         for &flags in FLAGS.iter() {
751             for month in 1u32..=12 {
752                 for day in 1u32..31 {
753                     let mdf = match Mdf::new(month, day, flags) {
754                         Some(mdf) => mdf,
755                         None => continue,
756                     };
757 
758                     if mdf.valid() {
759                         assert_eq!(mdf.month(), month);
760                         assert_eq!(mdf.day(), day);
761                     }
762                 }
763             }
764         }
765     }
766 
767     #[test]
test_mdf_with_fields()768     fn test_mdf_with_fields() {
769         fn check(flags: YearFlags, month: u32, day: u32) {
770             let mdf = Mdf::new(month, day, flags).unwrap();
771 
772             for month in 0u32..=16 {
773                 let mdf = match mdf.with_month(month) {
774                     Some(mdf) => mdf,
775                     None if month > 12 => continue,
776                     None => panic!("failed to create Mdf with month {}", month),
777                 };
778 
779                 if mdf.valid() {
780                     assert_eq!(mdf.month(), month);
781                     assert_eq!(mdf.day(), day);
782                 }
783             }
784 
785             for day in 0u32..=1024 {
786                 let mdf = match mdf.with_day(day) {
787                     Some(mdf) => mdf,
788                     None if day > 31 => continue,
789                     None => panic!("failed to create Mdf with month {}", month),
790                 };
791 
792                 if mdf.valid() {
793                     assert_eq!(mdf.month(), month);
794                     assert_eq!(mdf.day(), day);
795                 }
796             }
797         }
798 
799         for &flags in NONLEAP_FLAGS.iter() {
800             check(flags, 1, 1);
801             check(flags, 1, 31);
802             check(flags, 2, 1);
803             check(flags, 2, 28);
804             check(flags, 2, 29);
805             check(flags, 12, 31);
806         }
807         for &flags in LEAP_FLAGS.iter() {
808             check(flags, 1, 1);
809             check(flags, 1, 31);
810             check(flags, 2, 1);
811             check(flags, 2, 29);
812             check(flags, 2, 30);
813             check(flags, 12, 31);
814         }
815     }
816 
817     #[test]
test_of_isoweekdate_raw()818     fn test_of_isoweekdate_raw() {
819         for &flags in FLAGS.iter() {
820             // January 4 should be in the first week
821             let (week, _) = Of::new(4 /* January 4 */, flags).unwrap().isoweekdate_raw();
822             assert_eq!(week, 1);
823         }
824     }
825 
826     #[test]
test_of_to_mdf()827     fn test_of_to_mdf() {
828         for i in 0u32..=8192 {
829             if let Some(of) = Of(i).validate() {
830                 assert!(of.to_mdf().valid());
831             }
832         }
833     }
834 
835     #[test]
test_mdf_to_of()836     fn test_mdf_to_of() {
837         for i in 0u32..=8192 {
838             let mdf = Mdf(i);
839             assert_eq!(mdf.valid(), mdf.to_of().is_some());
840         }
841     }
842 
843     #[test]
test_of_to_mdf_to_of()844     fn test_of_to_mdf_to_of() {
845         for i in 0u32..=8192 {
846             if let Some(of) = Of(i).validate() {
847                 assert_eq!(of, of.to_mdf().to_of().unwrap());
848             }
849         }
850     }
851 
852     #[test]
test_mdf_to_of_to_mdf()853     fn test_mdf_to_of_to_mdf() {
854         for i in 0u32..=8192 {
855             let mdf = Mdf(i);
856             if mdf.valid() {
857                 assert_eq!(mdf, mdf.to_of().unwrap().to_mdf());
858             }
859         }
860     }
861 
862     #[test]
test_invalid_returns_none()863     fn test_invalid_returns_none() {
864         let regular_year = YearFlags::from_year(2023);
865         let leap_year = YearFlags::from_year(2024);
866         assert!(Of::new(0, regular_year).is_none());
867         assert!(Of::new(366, regular_year).is_none());
868         assert!(Of::new(366, leap_year).is_some());
869         assert!(Of::new(367, regular_year).is_none());
870 
871         assert!(Mdf::new(0, 1, regular_year).is_none());
872         assert!(Mdf::new(13, 1, regular_year).is_none());
873         assert!(Mdf::new(1, 0, regular_year).is_none());
874         assert!(Mdf::new(1, 32, regular_year).is_none());
875         assert!(Mdf::new(2, 31, regular_year).is_some());
876 
877         assert!(Of::from_mdf(Mdf::new(2, 30, regular_year).unwrap()).is_none());
878         assert!(Of::from_mdf(Mdf::new(2, 30, leap_year).unwrap()).is_none());
879         assert!(Of::from_mdf(Mdf::new(2, 29, regular_year).unwrap()).is_none());
880         assert!(Of::from_mdf(Mdf::new(2, 29, leap_year).unwrap()).is_some());
881         assert!(Of::from_mdf(Mdf::new(2, 28, regular_year).unwrap()).is_some());
882 
883         assert!(Of::new(365, regular_year).unwrap().succ().is_none());
884         assert!(Of::new(365, leap_year).unwrap().succ().is_some());
885         assert!(Of::new(366, leap_year).unwrap().succ().is_none());
886 
887         assert!(Of::new(1, regular_year).unwrap().pred().is_none());
888         assert!(Of::new(1, leap_year).unwrap().pred().is_none());
889     }
890 
891     #[test]
test_weekday_from_u32_mod7()892     fn test_weekday_from_u32_mod7() {
893         for i in 0..=1000 {
894             assert_eq!(weekday_from_u32_mod7(i), Weekday::try_from((i % 7) as u8).unwrap());
895         }
896         assert_eq!(weekday_from_u32_mod7(u32::MAX), Weekday::Thu);
897     }
898 }
899