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 3 #1431

Merged
merged 5 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 82 additions & 8 deletions src/naive/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
use crate::{Datelike, TimeDelta, Weekday};

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

/// A week represented by a [`NaiveDate`] and a [`Weekday`] which is the first
/// day of the week.
Expand Down Expand Up @@ -1474,10 +1473,24 @@
self.mdf().day()
}

/// Returns the day of week.
// This duplicates `Datelike::weekday()`, because trait methods can't be const yet.
#[inline]
const fn weekday(&self) -> Weekday {
self.of().weekday()
match (((self.yof & ORDINAL_MASK) >> 4) + (self.yof & WEEKDAY_FLAGS_MASK)) % 7 {
0 => Weekday::Mon,
1 => Weekday::Tue,
2 => Weekday::Wed,
3 => Weekday::Thu,
4 => Weekday::Fri,
5 => Weekday::Sat,
_ => Weekday::Sun,
}
}

#[inline]
const fn year_flags(&self) -> YearFlags {
YearFlags((self.yof & YEAR_FLAGS_MASK) as u8)
}

/// Counts the days in the proleptic Gregorian calendar, with January 1, Year 1 (CE) as day 1.
Expand Down Expand Up @@ -1689,7 +1702,7 @@

#[inline]
fn iso_week(&self) -> IsoWeek {
isoweek::iso_week_from_yof(self.year(), self.of())
IsoWeek::from_yof(self.year(), self.ordinal(), self.year_flags())
}

/// Makes a new `NaiveDate` with the year number changed, while keeping the same month and day.
Expand Down Expand Up @@ -2333,6 +2346,12 @@
const OL_MASK: i32 = ORDINAL_MASK | LEAP_YEAR_MASK;
const MAX_OL: i32 = 366 << 4;

// Weekday of the last day in the preceding year.
// Allows for quick day of week calculation from the 1-based ordinal.
const WEEKDAY_FLAGS_MASK: i32 = 0b111;

const YEAR_FLAGS_MASK: i32 = LEAP_YEAR_MASK | WEEKDAY_FLAGS_MASK;

#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))]
fn test_encodable_json<F, E>(to_string: F)
where
Expand Down Expand Up @@ -2517,7 +2536,7 @@
#[cfg(test)]
mod tests {
use super::{Days, Months, NaiveDate, MAX_YEAR, MIN_YEAR};
use crate::naive::internals::YearFlags;
use crate::naive::internals::{YearFlags, A, AG, B, BA, C, CB, D, DC, E, ED, F, FE, G, GF};
use crate::{Datelike, TimeDelta, Weekday};

// as it is hard to verify year flags in `NaiveDate::MIN` and `NaiveDate::MAX`,
Expand All @@ -2529,12 +2548,12 @@
assert!(
NaiveDate::MIN == calculated_min,
"`NaiveDate::MIN` should have year flag {:?}",
calculated_min.of().flags()
calculated_min.year_flags()

Check warning on line 2551 in src/naive/date.rs

View check run for this annotation

Codecov / codecov/patch

src/naive/date.rs#L2551

Added line #L2551 was not covered by tests
);
assert!(
NaiveDate::MAX == calculated_max,
"`NaiveDate::MAX` should have year flag {:?} and ordinal {}",
calculated_max.of().flags(),
calculated_max.year_flags(),

Check warning on line 2556 in src/naive/date.rs

View check run for this annotation

Codecov / codecov/patch

src/naive/date.rs#L2556

Added line #L2556 was not covered by tests
calculated_max.ordinal()
);

Expand All @@ -2549,11 +2568,11 @@
);

const BEFORE_MIN: NaiveDate = NaiveDate::BEFORE_MIN;
assert_eq!(BEFORE_MIN.of().flags(), YearFlags::from_year(BEFORE_MIN.year()));
assert_eq!(BEFORE_MIN.year_flags(), YearFlags::from_year(BEFORE_MIN.year()));
assert_eq!((BEFORE_MIN.month(), BEFORE_MIN.day()), (12, 31));

const AFTER_MAX: NaiveDate = NaiveDate::AFTER_MAX;
assert_eq!(AFTER_MAX.of().flags(), YearFlags::from_year(AFTER_MAX.year()));
assert_eq!(AFTER_MAX.year_flags(), YearFlags::from_year(AFTER_MAX.year()));
assert_eq!((AFTER_MAX.month(), AFTER_MAX.day()), (1, 1));
}

Expand Down Expand Up @@ -3376,6 +3395,61 @@
}
}

#[test]
fn test_date_yearflags() {
for (year, year_flags, _) in YEAR_FLAGS {
assert_eq!(NaiveDate::from_yo_opt(year, 1).unwrap().year_flags(), year_flags);
}
}

#[test]
fn test_weekday_with_yearflags() {
for (year, year_flags, first_weekday) in YEAR_FLAGS {
let first_day_of_year = NaiveDate::from_yo_opt(year, 1).unwrap();
dbg!(year);
assert_eq!(first_day_of_year.year_flags(), year_flags);
assert_eq!(first_day_of_year.weekday(), first_weekday);

let mut prev = first_day_of_year.weekday();
for ordinal in 2u32..=year_flags.ndays() {
let date = NaiveDate::from_yo_opt(year, ordinal).unwrap();
let expected = prev.succ();
assert_eq!(date.weekday(), expected);
prev = expected;
}
}
}

#[test]
fn test_isoweekdate_with_yearflags() {
for (year, year_flags, _) in YEAR_FLAGS {
// January 4 should be in the first week
let jan4 = NaiveDate::from_ymd_opt(year, 1, 4).unwrap();
let iso_week = jan4.iso_week();
assert_eq!(jan4.year_flags(), year_flags);
assert_eq!(iso_week.week(), 1);
}
}

// Used for testing some methods with all combinations of `YearFlags`.
// (year, flags, first weekday of year)
const YEAR_FLAGS: [(i32, YearFlags, Weekday); 14] = [
(2006, A, Weekday::Sun),
(2005, B, Weekday::Sat),
(2010, C, Weekday::Fri),
(2009, D, Weekday::Thu),
(2003, E, Weekday::Wed),
(2002, F, Weekday::Tue),
(2001, G, Weekday::Mon),
(2012, AG, Weekday::Sun),
(2000, BA, Weekday::Sat),
(2016, CB, Weekday::Fri),
(2004, DC, Weekday::Thu),
(2020, ED, Weekday::Wed),
(2008, FE, Weekday::Tue),
(2024, GF, Weekday::Mon),
];

#[test]
#[cfg(feature = "rkyv-validation")]
fn test_rkyv_validation() {
Expand Down
130 changes: 0 additions & 130 deletions src/naive/internals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

#![cfg_attr(feature = "__internal_bench", allow(missing_docs))]

use crate::Weekday;
use core::fmt;

/// The year flags (aka the dominical letter).
Expand Down Expand Up @@ -266,13 +265,6 @@ const OL_TO_MDL: &[u8; MAX_OL as usize + 1] = &[
pub(super) struct Of(u32);

impl Of {
#[inline]
#[cfg(test)]
pub(super) const fn new(ordinal: u32, YearFlags(flags): YearFlags) -> Option<Of> {
let of = Of((ordinal << 4) | flags as u32);
of.validate()
}

pub(super) const fn from_date_impl(date_impl: i32) -> Of {
// We assume the value in `date_impl` is valid.
Of((date_impl & 0b1_1111_1111_1111) as u32)
Expand Down Expand Up @@ -311,25 +303,6 @@ impl Of {
}
}

#[inline]
pub(super) const fn flags(&self) -> YearFlags {
YearFlags((self.0 & 0b1111) as u8)
}

#[inline]
pub(super) const fn weekday(&self) -> Weekday {
let Of(of) = *self;
weekday_from_u32_mod7((of >> 4) + (of & 0b111))
}

#[inline]
pub(super) fn isoweekdate_raw(&self) -> (u32, Weekday) {
// week ordinal = ordinal + delta
let Of(of) = *self;
let weekord = (of >> 4).wrapping_add(self.flags().isoweek_delta());
(weekord / 7, weekday_from_u32_mod7(weekord))
}

#[cfg_attr(feature = "cargo-clippy", allow(clippy::wrong_self_convention))]
#[inline]
pub(super) const fn to_mdf(&self) -> Mdf {
Expand Down Expand Up @@ -455,27 +428,10 @@ impl fmt::Debug for Mdf {
}
}

/// Create a `Weekday` from an `u32`, with Monday = 0.
/// Infallible, takes any `n` and applies `% 7`.
#[inline]
const fn weekday_from_u32_mod7(n: u32) -> Weekday {
match n % 7 {
0 => Weekday::Mon,
1 => Weekday::Tue,
2 => Weekday::Wed,
3 => Weekday::Thu,
4 => Weekday::Fri,
5 => Weekday::Sat,
_ => Weekday::Sun,
}
}

#[cfg(test)]
mod tests {
use super::weekday_from_u32_mod7;
use super::{Mdf, Of};
use super::{YearFlags, A, AG, B, BA, C, CB, D, DC, E, ED, F, FE, G, GF};
use crate::Weekday;

const NONLEAP_FLAGS: [YearFlags; 7] = [A, B, C, D, E, F, G];
const LEAP_FLAGS: [YearFlags; 7] = [AG, BA, CB, DC, ED, FE, GF];
Expand Down Expand Up @@ -516,42 +472,6 @@ mod tests {
assert_eq!(GF.nisoweeks(), 52);
}

#[test]
fn test_of() {
fn check(expected: bool, flags: YearFlags, ordinal1: u32, ordinal2: u32) {
for ordinal in ordinal1..=ordinal2 {
let of = match Of::new(ordinal, flags) {
Some(of) => of,
None if !expected => continue,
None => panic!("Of::new({}, {:?}) returned None", ordinal, flags),
};

assert!(
of.validate().is_some() == expected,
"ordinal {} = {:?} should be {} for dominical year {:?}",
ordinal,
of,
if expected { "valid" } else { "invalid" },
flags
);
}
}

for &flags in NONLEAP_FLAGS.iter() {
check(false, flags, 0, 0);
check(true, flags, 1, 365);
check(false, flags, 366, 1024);
check(false, flags, u32::MAX, u32::MAX);
}

for &flags in LEAP_FLAGS.iter() {
check(false, flags, 0, 0);
check(true, flags, 1, 366);
check(false, flags, 367, 1024);
check(false, flags, u32::MAX, u32::MAX);
}
}

#[test]
fn test_mdf_valid() {
fn check(expected: bool, flags: YearFlags, month1: u32, day1: u32, month2: u32, day2: u32) {
Expand Down Expand Up @@ -643,34 +563,6 @@ mod tests {
}
}

#[test]
fn test_of_weekday() {
assert_eq!(Of::new(1, A).unwrap().weekday(), Weekday::Sun);
assert_eq!(Of::new(1, B).unwrap().weekday(), Weekday::Sat);
assert_eq!(Of::new(1, C).unwrap().weekday(), Weekday::Fri);
assert_eq!(Of::new(1, D).unwrap().weekday(), Weekday::Thu);
assert_eq!(Of::new(1, E).unwrap().weekday(), Weekday::Wed);
assert_eq!(Of::new(1, F).unwrap().weekday(), Weekday::Tue);
assert_eq!(Of::new(1, G).unwrap().weekday(), Weekday::Mon);
assert_eq!(Of::new(1, AG).unwrap().weekday(), Weekday::Sun);
assert_eq!(Of::new(1, BA).unwrap().weekday(), Weekday::Sat);
assert_eq!(Of::new(1, CB).unwrap().weekday(), Weekday::Fri);
assert_eq!(Of::new(1, DC).unwrap().weekday(), Weekday::Thu);
assert_eq!(Of::new(1, ED).unwrap().weekday(), Weekday::Wed);
assert_eq!(Of::new(1, FE).unwrap().weekday(), Weekday::Tue);
assert_eq!(Of::new(1, GF).unwrap().weekday(), Weekday::Mon);

for &flags in FLAGS.iter() {
let mut prev = Of::new(1, flags).unwrap().weekday();
for ordinal in 2u32..=flags.ndays() {
let of = Of::new(ordinal, flags).unwrap();
let expected = prev.succ();
assert_eq!(of.weekday(), expected);
prev = expected;
}
}
}

#[test]
fn test_mdf_fields() {
for &flags in FLAGS.iter() {
Expand Down Expand Up @@ -740,15 +632,6 @@ mod tests {
}
}

#[test]
fn test_of_isoweekdate_raw() {
for &flags in FLAGS.iter() {
// January 4 should be in the first week
let (week, _) = Of::new(4 /* January 4 */, flags).unwrap().isoweekdate_raw();
assert_eq!(week, 1);
}
}

#[test]
fn test_of_to_mdf() {
for i in 0u32..=8192 {
Expand Down Expand Up @@ -789,11 +672,6 @@ mod tests {
fn test_invalid_returns_none() {
let regular_year = YearFlags::from_year(2023);
let leap_year = YearFlags::from_year(2024);
assert!(Of::new(0, regular_year).is_none());
assert!(Of::new(366, regular_year).is_none());
assert!(Of::new(366, leap_year).is_some());
assert!(Of::new(367, regular_year).is_none());

assert!(Mdf::new(0, 1, regular_year).is_none());
assert!(Mdf::new(13, 1, regular_year).is_none());
assert!(Mdf::new(1, 0, regular_year).is_none());
Expand All @@ -806,12 +684,4 @@ mod tests {
assert!(Of::from_mdf(Mdf::new(2, 29, leap_year).unwrap()).is_some());
assert!(Of::from_mdf(Mdf::new(2, 28, regular_year).unwrap()).is_some());
}

#[test]
fn test_weekday_from_u32_mod7() {
for i in 0..=1000 {
assert_eq!(weekday_from_u32_mod7(i), Weekday::try_from((i % 7) as u8).unwrap());
}
assert_eq!(weekday_from_u32_mod7(u32::MAX), Weekday::Thu);
}
}
Loading
Loading