Coverage Report

Created: 2025-07-18 06:52

/rust/registry/src/index.crates.io-6f17d22bba15001f/chrono-0.4.41/src/naive/internals.rs
Line
Count
Source (jump to first uncovered line)
1
//! Internal helper types for working with dates.
2
3
#![cfg_attr(feature = "__internal_bench", allow(missing_docs))]
4
5
use core::fmt;
6
7
/// Year flags (aka the dominical letter).
8
///
9
/// `YearFlags` are used as the last four bits of `NaiveDate`, `Mdf` and `IsoWeek`.
10
///
11
/// There are 14 possible classes of year in the Gregorian calendar:
12
/// common and leap years starting with Monday through Sunday.
13
///
14
/// The `YearFlags` stores this information into 4 bits `LWWW`. `L` is the leap year flag, with `1`
15
/// for the common year (this simplifies validating an ordinal in `NaiveDate`). `WWW` is a non-zero
16
/// `Weekday` of the last day in the preceding year.
17
#[allow(unreachable_pub)] // public as an alias for benchmarks only
18
#[derive(PartialEq, Eq, Copy, Clone, Hash)]
19
pub struct YearFlags(pub(super) u8);
20
21
// Weekday of the last day in the preceding year.
22
// Allows for quick day of week calculation from the 1-based ordinal.
23
const YEAR_STARTS_AFTER_MONDAY: u8 = 7; // non-zero to allow use with `NonZero*`.
24
const YEAR_STARTS_AFTER_THUESDAY: u8 = 1;
25
const YEAR_STARTS_AFTER_WEDNESDAY: u8 = 2;
26
const YEAR_STARTS_AFTER_THURSDAY: u8 = 3;
27
const YEAR_STARTS_AFTER_FRIDAY: u8 = 4;
28
const YEAR_STARTS_AFTER_SATURDAY: u8 = 5;
29
const YEAR_STARTS_AFTER_SUNDAY: u8 = 6;
30
31
const COMMON_YEAR: u8 = 1 << 3;
32
const LEAP_YEAR: u8 = 0 << 3;
33
34
pub(super) const A: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_SATURDAY);
35
pub(super) const AG: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_SATURDAY);
36
pub(super) const B: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_FRIDAY);
37
pub(super) const BA: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_FRIDAY);
38
pub(super) const C: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_THURSDAY);
39
pub(super) const CB: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_THURSDAY);
40
pub(super) const D: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_WEDNESDAY);
41
pub(super) const DC: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_WEDNESDAY);
42
pub(super) const E: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_THUESDAY);
43
pub(super) const ED: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_THUESDAY);
44
pub(super) const F: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_MONDAY);
45
pub(super) const FE: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_MONDAY);
46
pub(super) const G: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_SUNDAY);
47
pub(super) const GF: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_SUNDAY);
48
49
const YEAR_TO_FLAGS: &[YearFlags; 400] = &[
50
    BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA,
51
    G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G,
52
    F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F,
53
    E, DC, B, A, G, FE, D, C, B, AG, F, E, D, // 100
54
    C, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC,
55
    B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B,
56
    A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A,
57
    G, FE, D, C, B, AG, F, E, D, CB, A, G, F, // 200
58
    E, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE,
59
    D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D,
60
    C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C,
61
    B, AG, F, E, D, CB, A, G, F, ED, C, B, A, // 300
62
    G, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG,
63
    F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F,
64
    E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E,
65
    D, CB, A, G, F, ED, C, B, A, GF, E, D, C, // 400
66
];
67
68
impl YearFlags {
69
    #[allow(unreachable_pub)] // public as an alias for benchmarks only
70
    #[doc(hidden)] // for benchmarks only
71
    #[inline]
72
    #[must_use]
73
0
    pub const fn from_year(year: i32) -> YearFlags {
74
0
        let year = year.rem_euclid(400);
75
0
        YearFlags::from_year_mod_400(year)
76
0
    }
77
78
    #[inline]
79
0
    pub(super) const fn from_year_mod_400(year: i32) -> YearFlags {
80
0
        YEAR_TO_FLAGS[year as usize]
81
0
    }
82
83
    #[inline]
84
0
    pub(super) const fn ndays(&self) -> u32 {
85
0
        let YearFlags(flags) = *self;
86
0
        366 - (flags >> 3) as u32
87
0
    }
88
89
    #[inline]
90
0
    pub(super) const fn isoweek_delta(&self) -> u32 {
91
0
        let YearFlags(flags) = *self;
92
0
        let mut delta = (flags & 0b0111) as u32;
93
0
        if delta < 3 {
94
0
            delta += 7;
95
0
        }
96
0
        delta
97
0
    }
98
99
    #[inline]
100
0
    pub(super) const fn nisoweeks(&self) -> u32 {
101
0
        let YearFlags(flags) = *self;
102
0
        52 + ((0b0000_0100_0000_0110 >> flags as usize) & 1)
103
0
    }
104
}
105
106
impl fmt::Debug for YearFlags {
107
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
108
0
        let YearFlags(flags) = *self;
109
0
        match flags {
110
0
            0o15 => "A".fmt(f),
111
0
            0o05 => "AG".fmt(f),
112
0
            0o14 => "B".fmt(f),
113
0
            0o04 => "BA".fmt(f),
114
0
            0o13 => "C".fmt(f),
115
0
            0o03 => "CB".fmt(f),
116
0
            0o12 => "D".fmt(f),
117
0
            0o02 => "DC".fmt(f),
118
0
            0o11 => "E".fmt(f),
119
0
            0o01 => "ED".fmt(f),
120
0
            0o10 => "F?".fmt(f),
121
0
            0o00 => "FE?".fmt(f), // non-canonical
122
0
            0o17 => "F".fmt(f),
123
0
            0o07 => "FE".fmt(f),
124
0
            0o16 => "G".fmt(f),
125
0
            0o06 => "GF".fmt(f),
126
0
            _ => write!(f, "YearFlags({})", flags),
127
        }
128
0
    }
129
}
130
131
// OL: (ordinal << 1) | leap year flag
132
const MAX_OL: u32 = 366 << 1; // `(366 << 1) | 1` would be day 366 in a non-leap year
133
const MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1;
134
135
// The next table are adjustment values to convert a date encoded as month-day-leapyear to
136
// ordinal-leapyear. OL = MDL - adjustment.
137
// Dates that do not exist are encoded as `XX`.
138
const XX: i8 = 0;
139
const MDL_TO_OL: &[i8; MAX_MDL as usize + 1] = &[
140
    XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
141
    XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
142
    XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0
143
    XX, XX, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
144
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
145
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 1
146
    XX, XX, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
147
    66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
148
    66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, XX, XX, XX, XX, XX, // 2
149
    XX, XX, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74,
150
    72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74,
151
    72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, // 3
152
    XX, XX, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76,
153
    74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76,
154
    74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, XX, XX, // 4
155
    XX, XX, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80,
156
    78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80,
157
    78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, // 5
158
    XX, XX, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82,
159
    80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82,
160
    80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, XX, XX, // 6
161
    XX, XX, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86,
162
    84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86,
163
    84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, // 7
164
    XX, XX, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88,
165
    86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88,
166
    86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, // 8
167
    XX, XX, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90,
168
    88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90,
169
    88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, XX, XX, // 9
170
    XX, XX, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94,
171
    92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94,
172
    92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, // 10
173
    XX, XX, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96,
174
    94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96,
175
    94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, XX, XX, // 11
176
    XX, XX, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98,
177
    100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100,
178
    98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98,
179
    100, // 12
180
];
181
182
const OL_TO_MDL: &[u8; MAX_OL as usize + 1] = &[
183
    0, 0, // 0
184
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
185
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
186
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 1
187
    66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
188
    66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
189
    66, 66, 66, 66, 66, 66, 66, 66, 66, // 2
190
    74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72,
191
    74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72,
192
    74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, // 3
193
    76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74,
194
    76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74,
195
    76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, // 4
196
    80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78,
197
    80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78,
198
    80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, // 5
199
    82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80,
200
    82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80,
201
    82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, // 6
202
    86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84,
203
    86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84,
204
    86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, // 7
205
    88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86,
206
    88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86,
207
    88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, // 8
208
    90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88,
209
    90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88,
210
    90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, // 9
211
    94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92,
212
    94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92,
213
    94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, // 10
214
    96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94,
215
    96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94,
216
    96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, // 11
217
    100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100,
218
    98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98,
219
    100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100,
220
    98, // 12
221
];
222
223
/// Month, day of month and year flags: `(month << 9) | (day << 4) | flags`
224
/// `M_MMMD_DDDD_LFFF`
225
///
226
/// The whole bits except for the least 3 bits are referred as `Mdl` (month, day of month, and leap
227
/// year flag), which is an index to the `MDL_TO_OL` lookup table.
228
///
229
/// The conversion between the packed calendar date (`Mdf`) and the ordinal date (`NaiveDate`) is
230
/// based on the moderately-sized lookup table (~1.5KB) and the packed representation is chosen for
231
/// efficient lookup.
232
///
233
/// The methods of `Mdf` validate their inputs as late as possible. Dates that can't exist, like
234
/// February 30, can still be represented. This allows the validation to be combined with the final
235
/// table lookup, which is good for performance.
236
#[derive(PartialEq, PartialOrd, Copy, Clone)]
237
pub(super) struct Mdf(u32);
238
239
impl Mdf {
240
    /// Makes a new `Mdf` value from month, day and `YearFlags`.
241
    ///
242
    /// This method doesn't fully validate the range of the `month` and `day` parameters, only as
243
    /// much as what can't be deferred until later. The year `flags` are trusted to be correct.
244
    ///
245
    /// # Errors
246
    ///
247
    /// Returns `None` if `month > 12` or `day > 31`.
248
    #[inline]
249
0
    pub(super) const fn new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Option<Mdf> {
250
0
        match month <= 12 && day <= 31 {
251
0
            true => Some(Mdf((month << 9) | (day << 4) | flags as u32)),
252
0
            false => None,
253
        }
254
0
    }
255
256
    /// Makes a new `Mdf` value from an `i32` with an ordinal and a leap year flag, and year
257
    /// `flags`.
258
    ///
259
    /// The `ol` is trusted to be valid, and the `flags` are trusted to match it.
260
    #[inline]
261
0
    pub(super) const fn from_ol(ol: i32, YearFlags(flags): YearFlags) -> Mdf {
262
0
        debug_assert!(ol > 1 && ol <= MAX_OL as i32);
263
        // Array is indexed from `[2..=MAX_OL]`, with a `0` index having a meaningless value.
264
0
        Mdf(((ol as u32 + OL_TO_MDL[ol as usize] as u32) << 3) | flags as u32)
265
0
    }
Unexecuted instantiation: <chrono::naive::internals::Mdf>::from_ol
Unexecuted instantiation: <chrono::naive::internals::Mdf>::from_ol
266
267
    /// Returns the month of this `Mdf`.
268
    #[inline]
269
0
    pub(super) const fn month(&self) -> u32 {
270
0
        let Mdf(mdf) = *self;
271
0
        mdf >> 9
272
0
    }
Unexecuted instantiation: <chrono::naive::internals::Mdf>::month
Unexecuted instantiation: <chrono::naive::internals::Mdf>::month
273
274
    /// Replaces the month of this `Mdf`, keeping the day and flags.
275
    ///
276
    /// # Errors
277
    ///
278
    /// Returns `None` if `month > 12`.
279
    #[inline]
280
0
    pub(super) const fn with_month(&self, month: u32) -> Option<Mdf> {
281
0
        if month > 12 {
282
0
            return None;
283
0
        }
284
0
285
0
        let Mdf(mdf) = *self;
286
0
        Some(Mdf((mdf & 0b1_1111_1111) | (month << 9)))
287
0
    }
288
289
    /// Returns the day of this `Mdf`.
290
    #[inline]
291
0
    pub(super) const fn day(&self) -> u32 {
292
0
        let Mdf(mdf) = *self;
293
0
        (mdf >> 4) & 0b1_1111
294
0
    }
Unexecuted instantiation: <chrono::naive::internals::Mdf>::day
Unexecuted instantiation: <chrono::naive::internals::Mdf>::day
295
296
    /// Replaces the day of this `Mdf`, keeping the month and flags.
297
    ///
298
    /// # Errors
299
    ///
300
    /// Returns `None` if `day > 31`.
301
    #[inline]
302
0
    pub(super) const fn with_day(&self, day: u32) -> Option<Mdf> {
303
0
        if day > 31 {
304
0
            return None;
305
0
        }
306
0
307
0
        let Mdf(mdf) = *self;
308
0
        Some(Mdf((mdf & !0b1_1111_0000) | (day << 4)))
309
0
    }
310
311
    /// Replaces the flags of this `Mdf`, keeping the month and day.
312
    #[inline]
313
0
    pub(super) const fn with_flags(&self, YearFlags(flags): YearFlags) -> Mdf {
314
0
        let Mdf(mdf) = *self;
315
0
        Mdf((mdf & !0b1111) | flags as u32)
316
0
    }
317
318
    /// Returns the ordinal that corresponds to this `Mdf`.
319
    ///
320
    /// This does a table lookup to calculate the corresponding ordinal. It will return an error if
321
    /// the `Mdl` turns out not to be a valid date.
322
    ///
323
    /// # Errors
324
    ///
325
    /// Returns `None` if `month == 0` or `day == 0`, or if a the given day does not exist in the
326
    /// given month.
327
    #[inline]
328
0
    pub(super) const fn ordinal(&self) -> Option<u32> {
329
0
        let mdl = self.0 >> 3;
330
0
        match MDL_TO_OL[mdl as usize] {
331
0
            XX => None,
332
0
            v => Some((mdl - v as u8 as u32) >> 1),
333
        }
334
0
    }
335
336
    /// Returns the year flags of this `Mdf`.
337
    #[inline]
338
0
    pub(super) const fn year_flags(&self) -> YearFlags {
339
0
        YearFlags((self.0 & 0b1111) as u8)
340
0
    }
341
342
    /// Returns the ordinal that corresponds to this `Mdf`, encoded as a value including year flags.
343
    ///
344
    /// This does a table lookup to calculate the corresponding ordinal. It will return an error if
345
    /// the `Mdl` turns out not to be a valid date.
346
    ///
347
    /// # Errors
348
    ///
349
    /// Returns `None` if `month == 0` or `day == 0`, or if a the given day does not exist in the
350
    /// given month.
351
    #[inline]
352
0
    pub(super) const fn ordinal_and_flags(&self) -> Option<i32> {
353
0
        let mdl = self.0 >> 3;
354
0
        match MDL_TO_OL[mdl as usize] {
355
0
            XX => None,
356
0
            v => Some(self.0 as i32 - ((v as i32) << 3)),
357
        }
358
0
    }
359
360
    #[cfg(test)]
361
    fn valid(&self) -> bool {
362
        let mdl = self.0 >> 3;
363
        MDL_TO_OL[mdl as usize] > 0
364
    }
365
}
366
367
impl fmt::Debug for Mdf {
368
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
369
0
        let Mdf(mdf) = *self;
370
0
        write!(
371
0
            f,
372
0
            "Mdf(({} << 9) | ({} << 4) | {:#04o} /*{:?}*/)",
373
0
            mdf >> 9,
374
0
            (mdf >> 4) & 0b1_1111,
375
0
            mdf & 0b1111,
376
0
            YearFlags((mdf & 0b1111) as u8)
377
0
        )
378
0
    }
379
}
380
381
#[cfg(test)]
382
mod tests {
383
    use super::Mdf;
384
    use super::{A, AG, B, BA, C, CB, D, DC, E, ED, F, FE, G, GF, YearFlags};
385
386
    const NONLEAP_FLAGS: [YearFlags; 7] = [A, B, C, D, E, F, G];
387
    const LEAP_FLAGS: [YearFlags; 7] = [AG, BA, CB, DC, ED, FE, GF];
388
    const FLAGS: [YearFlags; 14] = [A, B, C, D, E, F, G, AG, BA, CB, DC, ED, FE, GF];
389
390
    #[test]
391
    fn test_year_flags_ndays_from_year() {
392
        assert_eq!(YearFlags::from_year(2014).ndays(), 365);
393
        assert_eq!(YearFlags::from_year(2012).ndays(), 366);
394
        assert_eq!(YearFlags::from_year(2000).ndays(), 366);
395
        assert_eq!(YearFlags::from_year(1900).ndays(), 365);
396
        assert_eq!(YearFlags::from_year(1600).ndays(), 366);
397
        assert_eq!(YearFlags::from_year(1).ndays(), 365);
398
        assert_eq!(YearFlags::from_year(0).ndays(), 366); // 1 BCE (proleptic Gregorian)
399
        assert_eq!(YearFlags::from_year(-1).ndays(), 365); // 2 BCE
400
        assert_eq!(YearFlags::from_year(-4).ndays(), 366); // 5 BCE
401
        assert_eq!(YearFlags::from_year(-99).ndays(), 365); // 100 BCE
402
        assert_eq!(YearFlags::from_year(-100).ndays(), 365); // 101 BCE
403
        assert_eq!(YearFlags::from_year(-399).ndays(), 365); // 400 BCE
404
        assert_eq!(YearFlags::from_year(-400).ndays(), 366); // 401 BCE
405
    }
406
407
    #[test]
408
    fn test_year_flags_nisoweeks() {
409
        assert_eq!(A.nisoweeks(), 52);
410
        assert_eq!(B.nisoweeks(), 52);
411
        assert_eq!(C.nisoweeks(), 52);
412
        assert_eq!(D.nisoweeks(), 53);
413
        assert_eq!(E.nisoweeks(), 52);
414
        assert_eq!(F.nisoweeks(), 52);
415
        assert_eq!(G.nisoweeks(), 52);
416
        assert_eq!(AG.nisoweeks(), 52);
417
        assert_eq!(BA.nisoweeks(), 52);
418
        assert_eq!(CB.nisoweeks(), 52);
419
        assert_eq!(DC.nisoweeks(), 53);
420
        assert_eq!(ED.nisoweeks(), 53);
421
        assert_eq!(FE.nisoweeks(), 52);
422
        assert_eq!(GF.nisoweeks(), 52);
423
    }
424
425
    #[test]
426
    fn test_mdf_valid() {
427
        fn check(expected: bool, flags: YearFlags, month1: u32, day1: u32, month2: u32, day2: u32) {
428
            for month in month1..=month2 {
429
                for day in day1..=day2 {
430
                    let mdf = match Mdf::new(month, day, flags) {
431
                        Some(mdf) => mdf,
432
                        None if !expected => continue,
433
                        None => panic!("Mdf::new({}, {}, {:?}) returned None", month, day, flags),
434
                    };
435
436
                    assert!(
437
                        mdf.valid() == expected,
438
                        "month {} day {} = {:?} should be {} for dominical year {:?}",
439
                        month,
440
                        day,
441
                        mdf,
442
                        if expected { "valid" } else { "invalid" },
443
                        flags
444
                    );
445
                }
446
            }
447
        }
448
449
        for &flags in NONLEAP_FLAGS.iter() {
450
            check(false, flags, 0, 0, 0, 1024);
451
            check(false, flags, 0, 0, 16, 0);
452
            check(true, flags, 1, 1, 1, 31);
453
            check(false, flags, 1, 32, 1, 1024);
454
            check(true, flags, 2, 1, 2, 28);
455
            check(false, flags, 2, 29, 2, 1024);
456
            check(true, flags, 3, 1, 3, 31);
457
            check(false, flags, 3, 32, 3, 1024);
458
            check(true, flags, 4, 1, 4, 30);
459
            check(false, flags, 4, 31, 4, 1024);
460
            check(true, flags, 5, 1, 5, 31);
461
            check(false, flags, 5, 32, 5, 1024);
462
            check(true, flags, 6, 1, 6, 30);
463
            check(false, flags, 6, 31, 6, 1024);
464
            check(true, flags, 7, 1, 7, 31);
465
            check(false, flags, 7, 32, 7, 1024);
466
            check(true, flags, 8, 1, 8, 31);
467
            check(false, flags, 8, 32, 8, 1024);
468
            check(true, flags, 9, 1, 9, 30);
469
            check(false, flags, 9, 31, 9, 1024);
470
            check(true, flags, 10, 1, 10, 31);
471
            check(false, flags, 10, 32, 10, 1024);
472
            check(true, flags, 11, 1, 11, 30);
473
            check(false, flags, 11, 31, 11, 1024);
474
            check(true, flags, 12, 1, 12, 31);
475
            check(false, flags, 12, 32, 12, 1024);
476
            check(false, flags, 13, 0, 16, 1024);
477
            check(false, flags, u32::MAX, 0, u32::MAX, 1024);
478
            check(false, flags, 0, u32::MAX, 16, u32::MAX);
479
            check(false, flags, u32::MAX, u32::MAX, u32::MAX, u32::MAX);
480
        }
481
482
        for &flags in LEAP_FLAGS.iter() {
483
            check(false, flags, 0, 0, 0, 1024);
484
            check(false, flags, 0, 0, 16, 0);
485
            check(true, flags, 1, 1, 1, 31);
486
            check(false, flags, 1, 32, 1, 1024);
487
            check(true, flags, 2, 1, 2, 29);
488
            check(false, flags, 2, 30, 2, 1024);
489
            check(true, flags, 3, 1, 3, 31);
490
            check(false, flags, 3, 32, 3, 1024);
491
            check(true, flags, 4, 1, 4, 30);
492
            check(false, flags, 4, 31, 4, 1024);
493
            check(true, flags, 5, 1, 5, 31);
494
            check(false, flags, 5, 32, 5, 1024);
495
            check(true, flags, 6, 1, 6, 30);
496
            check(false, flags, 6, 31, 6, 1024);
497
            check(true, flags, 7, 1, 7, 31);
498
            check(false, flags, 7, 32, 7, 1024);
499
            check(true, flags, 8, 1, 8, 31);
500
            check(false, flags, 8, 32, 8, 1024);
501
            check(true, flags, 9, 1, 9, 30);
502
            check(false, flags, 9, 31, 9, 1024);
503
            check(true, flags, 10, 1, 10, 31);
504
            check(false, flags, 10, 32, 10, 1024);
505
            check(true, flags, 11, 1, 11, 30);
506
            check(false, flags, 11, 31, 11, 1024);
507
            check(true, flags, 12, 1, 12, 31);
508
            check(false, flags, 12, 32, 12, 1024);
509
            check(false, flags, 13, 0, 16, 1024);
510
            check(false, flags, u32::MAX, 0, u32::MAX, 1024);
511
            check(false, flags, 0, u32::MAX, 16, u32::MAX);
512
            check(false, flags, u32::MAX, u32::MAX, u32::MAX, u32::MAX);
513
        }
514
    }
515
516
    #[test]
517
    fn test_mdf_fields() {
518
        for &flags in FLAGS.iter() {
519
            for month in 1u32..=12 {
520
                for day in 1u32..31 {
521
                    let mdf = match Mdf::new(month, day, flags) {
522
                        Some(mdf) => mdf,
523
                        None => continue,
524
                    };
525
526
                    if mdf.valid() {
527
                        assert_eq!(mdf.month(), month);
528
                        assert_eq!(mdf.day(), day);
529
                    }
530
                }
531
            }
532
        }
533
    }
534
535
    #[test]
536
    fn test_mdf_with_fields() {
537
        fn check(flags: YearFlags, month: u32, day: u32) {
538
            let mdf = Mdf::new(month, day, flags).unwrap();
539
540
            for month in 0u32..=16 {
541
                let mdf = match mdf.with_month(month) {
542
                    Some(mdf) => mdf,
543
                    None if month > 12 => continue,
544
                    None => panic!("failed to create Mdf with month {}", month),
545
                };
546
547
                if mdf.valid() {
548
                    assert_eq!(mdf.month(), month);
549
                    assert_eq!(mdf.day(), day);
550
                }
551
            }
552
553
            for day in 0u32..=1024 {
554
                let mdf = match mdf.with_day(day) {
555
                    Some(mdf) => mdf,
556
                    None if day > 31 => continue,
557
                    None => panic!("failed to create Mdf with month {}", month),
558
                };
559
560
                if mdf.valid() {
561
                    assert_eq!(mdf.month(), month);
562
                    assert_eq!(mdf.day(), day);
563
                }
564
            }
565
        }
566
567
        for &flags in NONLEAP_FLAGS.iter() {
568
            check(flags, 1, 1);
569
            check(flags, 1, 31);
570
            check(flags, 2, 1);
571
            check(flags, 2, 28);
572
            check(flags, 2, 29);
573
            check(flags, 12, 31);
574
        }
575
        for &flags in LEAP_FLAGS.iter() {
576
            check(flags, 1, 1);
577
            check(flags, 1, 31);
578
            check(flags, 2, 1);
579
            check(flags, 2, 29);
580
            check(flags, 2, 30);
581
            check(flags, 12, 31);
582
        }
583
    }
584
585
    #[test]
586
    fn test_mdf_new_range() {
587
        let flags = YearFlags::from_year(2023);
588
        assert!(Mdf::new(13, 1, flags).is_none());
589
        assert!(Mdf::new(1, 32, flags).is_none());
590
    }
591
}