Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor internals module part 4 #1432

Merged
merged 4 commits into from Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
93 changes: 65 additions & 28 deletions src/naive/date.rs
Expand Up @@ -37,7 +37,7 @@ use crate::naive::{IsoWeek, NaiveDateTime, NaiveTime};
use crate::{expect, try_opt};
use crate::{Datelike, TimeDelta, Weekday};

use super::internals::{self, Mdf, Of, YearFlags};
use super::internals::{Mdf, YearFlags};

/// A week represented by a [`NaiveDate`] and a [`Weekday`] which is the first
/// day of the week.
Expand Down Expand Up @@ -251,11 +251,8 @@ impl NaiveDate {
/// Makes a new `NaiveDate` from year and packed month-day-flags.
/// Does not check whether the flags are correct for the provided year.
const fn from_mdf(year: i32, mdf: Mdf) -> Option<NaiveDate> {
if year < MIN_YEAR || year > MAX_YEAR {
return None; // Out-of-range
}
match mdf.to_of() {
Some(of) => Some(NaiveDate { yof: (year << 13) | (of.inner() as i32) }),
match mdf.ordinal() {
Some(ordinal) => NaiveDate::from_ordinal_and_flags(year, ordinal, mdf.year_flags()),
None => None, // Non-existing date
}
}
Expand Down Expand Up @@ -488,7 +485,7 @@ impl NaiveDate {
let days = try_opt!(days.checked_add(365)); // make December 31, 1 BCE equal to day 0
let year_div_400 = days.div_euclid(146_097);
let cycle = days.rem_euclid(146_097);
let (year_mod_400, ordinal) = internals::cycle_to_yo(cycle as u32);
let (year_mod_400, ordinal) = cycle_to_yo(cycle as u32);
let flags = YearFlags::from_year_mod_400(year_mod_400 as i32);
NaiveDate::from_ordinal_and_flags(year_div_400 * 400 + year_mod_400 as i32, ordinal, flags)
}
Expand Down Expand Up @@ -806,12 +803,12 @@ impl NaiveDate {
// do the full check
let year = self.year();
let (mut year_div_400, year_mod_400) = div_mod_floor(year, 400);
let cycle = internals::yo_to_cycle(year_mod_400 as u32, self.ordinal());
let cycle = yo_to_cycle(year_mod_400 as u32, self.ordinal());
let cycle = try_opt!((cycle as i32).checked_add(days));
let (cycle_div_400y, cycle) = div_mod_floor(cycle, 146_097);
year_div_400 += cycle_div_400y;

let (year_mod_400, ordinal) = internals::cycle_to_yo(cycle as u32);
let (year_mod_400, ordinal) = cycle_to_yo(cycle as u32);
let flags = YearFlags::from_year_mod_400(year_mod_400 as i32);
NaiveDate::from_ordinal_and_flags(year_div_400 * 400 + year_mod_400 as i32, ordinal, flags)
}
Expand Down Expand Up @@ -1045,30 +1042,21 @@ impl NaiveDate {
/// Returns the packed month-day-flags.
#[inline]
const fn mdf(&self) -> Mdf {
self.of().to_mdf()
}

/// Returns the packed ordinal-flags.
#[inline]
const fn of(&self) -> Of {
Of::from_date_impl(self.yof)
Mdf::from_ol((self.yof & OL_MASK) >> 3, self.year_flags())
}

/// Makes a new `NaiveDate` with the packed month-day-flags changed.
///
/// Returns `None` when the resulting `NaiveDate` would be invalid.
#[inline]
const fn with_mdf(&self, mdf: Mdf) -> Option<NaiveDate> {
Some(self.with_of(try_opt!(mdf.to_of())))
}

/// Makes a new `NaiveDate` with the packed ordinal-flags changed.
///
/// Returns `None` when the resulting `NaiveDate` would be invalid.
/// Does not check if the year flags match the year.
#[inline]
const fn with_of(&self, of: Of) -> NaiveDate {
NaiveDate { yof: (self.yof & !0b1_1111_1111_1111) | of.inner() as i32 }
debug_assert!(self.year_flags().0 == mdf.year_flags().0);
match mdf.ordinal() {
Some(ordinal) => {
Some(NaiveDate { yof: (self.yof & !ORDINAL_MASK) | (ordinal << 4) as i32 })
}
None => None, // Non-existing date
}
}

/// Makes a new `NaiveDate` for the next calendar date.
Expand Down Expand Up @@ -1231,8 +1219,8 @@ impl NaiveDate {
let year2 = rhs.year();
let (year1_div_400, year1_mod_400) = div_mod_floor(year1, 400);
let (year2_div_400, year2_mod_400) = div_mod_floor(year2, 400);
let cycle1 = internals::yo_to_cycle(year1_mod_400 as u32, self.ordinal()) as i64;
let cycle2 = internals::yo_to_cycle(year2_mod_400 as u32, rhs.ordinal()) as i64;
let cycle1 = yo_to_cycle(year1_mod_400 as u32, self.ordinal()) as i64;
let cycle2 = yo_to_cycle(year2_mod_400 as u32, rhs.ordinal()) as i64;
TimeDelta::days((year1_div_400 as i64 - year2_div_400 as i64) * 146_097 + (cycle1 - cycle2))
}

Expand Down Expand Up @@ -2321,6 +2309,23 @@ impl Default for NaiveDate {
}
}

const fn cycle_to_yo(cycle: u32) -> (u32, u32) {
let mut year_mod_400 = cycle / 365;
let mut ordinal0 = cycle % 365;
let delta = YEAR_DELTAS[year_mod_400 as usize] as u32;
if ordinal0 < delta {
year_mod_400 -= 1;
ordinal0 += 365 - YEAR_DELTAS[year_mod_400 as usize] as u32;
} else {
ordinal0 -= delta;
}
(year_mod_400, ordinal0 + 1)
}

const fn yo_to_cycle(year_mod_400: u32, ordinal: u32) -> u32 {
year_mod_400 * 365 + YEAR_DELTAS[year_mod_400 as usize] as u32 + ordinal - 1
}

const fn div_mod_floor(val: i32, div: i32) -> (i32, i32) {
(val.div_euclid(div), val.rem_euclid(div))
}
Expand Down Expand Up @@ -2352,6 +2357,28 @@ const WEEKDAY_FLAGS_MASK: i32 = 0b111;

const YEAR_FLAGS_MASK: i32 = LEAP_YEAR_MASK | WEEKDAY_FLAGS_MASK;

const YEAR_DELTAS: &[u8; 401] = &[
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,
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,
15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20,
21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, // 100
25, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30,
30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36,
36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42,
42, 43, 43, 43, 43, 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 48, 48, 48,
48, 49, 49, 49, // 200
49, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54,
54, 55, 55, 55, 55, 56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, 60, 60, 60,
60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63, 64, 64, 64, 64, 65, 65, 65, 65, 66, 66, 66,
66, 67, 67, 67, 67, 68, 68, 68, 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, 72,
72, 73, 73, 73, // 300
73, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, 77, 78, 78, 78,
78, 79, 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82, 82, 82, 82, 83, 83, 83, 83, 84, 84, 84,
84, 85, 85, 85, 85, 86, 86, 86, 86, 87, 87, 87, 87, 88, 88, 88, 88, 89, 89, 89, 89, 90, 90, 90,
90, 91, 91, 91, 91, 92, 92, 92, 92, 93, 93, 93, 93, 94, 94, 94, 94, 95, 95, 95, 95, 96, 96, 96,
96, 97, 97, 97, 97, // 400+1
];

#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))]
fn test_encodable_json<F, E>(to_string: F)
where
Expand Down Expand Up @@ -3431,6 +3458,16 @@ mod tests {
}
}

#[test]
fn test_date_to_mdf_to_date() {
for (year, year_flags, _) in YEAR_FLAGS {
for ordinal in 1..=year_flags.ndays() {
let date = NaiveDate::from_yo_opt(year, ordinal).unwrap();
assert_eq!(date, NaiveDate::from_mdf(date.year(), date.mdf()).unwrap());
}
}
}

// Used for testing some methods with all combinations of `YearFlags`.
// (year, flags, first weekday of year)
const YEAR_FLAGS: [(i32, YearFlags, Weekday); 14] = [
Expand Down