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
18use crate::Weekday;
19use core::{fmt, i32};
20
21/// The internal date representation: `year << 13 | Of`
22pub(super) type DateImpl = i32;
23
24pub(super) const MAX_YEAR: DateImpl = i32::MAX >> 13;
25pub(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)]
37pub struct YearFlags(pub(super) u8);
38
39pub(super) const A: YearFlags = YearFlags(0o15);
40pub(super) const AG: YearFlags = YearFlags(0o05);
41pub(super) const B: YearFlags = YearFlags(0o14);
42pub(super) const BA: YearFlags = YearFlags(0o04);
43pub(super) const C: YearFlags = YearFlags(0o13);
44pub(super) const CB: YearFlags = YearFlags(0o03);
45pub(super) const D: YearFlags = YearFlags(0o12);
46pub(super) const DC: YearFlags = YearFlags(0o02);
47pub(super) const E: YearFlags = YearFlags(0o11);
48pub(super) const ED: YearFlags = YearFlags(0o01);
49pub(super) const F: YearFlags = YearFlags(0o17);
50pub(super) const FE: YearFlags = YearFlags(0o07);
51pub(super) const G: YearFlags = YearFlags(0o16);
52pub(super) const GF: YearFlags = YearFlags(0o06);
53
54const 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
73const 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
95pub(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
108pub(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
112impl 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
150impl 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
176pub(super) const MIN_OL: u32 = 1 << 1;
177pub(super) const MAX_OL: u32 = 366 << 1; // `(366 << 1) | 1` would be day 366 in a non-leap year
178pub(super) const MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1;
179
180const XX: i8 = -128;
181const 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
224const 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)]
272pub(super) struct Of(u32);
273
274impl 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
372impl 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)]
395pub(super) struct Mdf(u32);
396
397impl 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
476impl 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]
493const 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)]
506mod 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