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