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