mirror of
https://github.com/chronotope/chrono.git
synced 2025-10-02 07:21:41 +00:00
Add Mdf::ordinal
and Mdf::year_flags
; remove Of
type
This commit is contained in:
parent
f95a973c4b
commit
6894b6fc39
@ -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::{self, Mdf, YearFlags};
|
||||
|
||||
/// A week represented by a [`NaiveDate`] and a [`Weekday`] which is the first
|
||||
/// day of the week.
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -1053,16 +1050,13 @@ impl NaiveDate {
|
||||
/// 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.
|
||||
@ -3425,6 +3419,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] = [
|
||||
|
@ -166,7 +166,6 @@ impl fmt::Debug for YearFlags {
|
||||
}
|
||||
|
||||
// OL: (ordinal << 1) | leap year flag
|
||||
pub(super) const MIN_OL: u32 = 1 << 1;
|
||||
pub(super) const MAX_OL: u32 = 366 << 1; // `(366 << 1) | 1` would be day 366 in a non-leap year
|
||||
pub(super) const MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1;
|
||||
|
||||
@ -255,70 +254,8 @@ const OL_TO_MDL: &[u8; MAX_OL as usize + 1] = &[
|
||||
98, // 12
|
||||
];
|
||||
|
||||
/// Ordinal (day of year) and year flags: `(ordinal << 4) | flags`.
|
||||
///
|
||||
/// The whole bits except for the least 3 bits are referred as `Ol` (ordinal and leap flag),
|
||||
/// which is an index to the `OL_TO_MDL` lookup table.
|
||||
///
|
||||
/// The methods implemented on `Of` always return a valid value.
|
||||
#[derive(PartialEq, PartialOrd, Copy, Clone)]
|
||||
pub(super) struct Of(u32);
|
||||
|
||||
impl Of {
|
||||
#[inline]
|
||||
pub(super) const fn from_mdf(Mdf(mdf): Mdf) -> Option<Of> {
|
||||
let mdl = mdf >> 3;
|
||||
if mdl > MAX_MDL {
|
||||
// Panicking on out-of-bounds indexing would be reasonable, but just return `None`.
|
||||
return None;
|
||||
}
|
||||
// Array is indexed from `[1..=MAX_MDL]`, with a `0` index having a meaningless value.
|
||||
let v = MDL_TO_OL[mdl as usize];
|
||||
let of = Of(mdf.wrapping_sub((v as i32 as u32 & 0x3ff) << 3));
|
||||
of.validate()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) const fn inner(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Returns `(ordinal << 1) | leap-year-flag`.
|
||||
#[inline]
|
||||
const fn ol(&self) -> u32 {
|
||||
self.0 >> 3
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn validate(self) -> Option<Of> {
|
||||
let ol = self.ol();
|
||||
match ol >= MIN_OL && ol <= MAX_OL {
|
||||
true => Some(self),
|
||||
false => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy::wrong_self_convention))]
|
||||
#[inline]
|
||||
pub(super) const fn to_mdf(&self) -> Mdf {
|
||||
Mdf::from_ol(self.0 as i32 >> 3, YearFlags((self.0 & 0b1111) as u8))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Of {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Of(of) = *self;
|
||||
write!(
|
||||
f,
|
||||
"Of(({} << 4) | {:#04o} /*{:?}*/)",
|
||||
of >> 4,
|
||||
of & 0b1111,
|
||||
YearFlags((of & 0b1111) as u8)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Month, day of month and year flags: `(month << 9) | (day << 4) | flags`
|
||||
/// `M_MMMD_DDDD_LFFF`
|
||||
///
|
||||
/// The whole bits except for the least 3 bits are referred as `Mdl`
|
||||
/// (month, day of month and leap flag),
|
||||
@ -397,10 +334,18 @@ impl Mdf {
|
||||
Mdf((mdf & !0b1111) | flags as u32)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy::wrong_self_convention))]
|
||||
#[inline]
|
||||
pub(super) const fn to_of(&self) -> Option<Of> {
|
||||
Of::from_mdf(*self)
|
||||
pub(super) const fn ordinal(&self) -> Option<u32> {
|
||||
let mdl = self.0 >> 3;
|
||||
match MDL_TO_OL[mdl as usize] {
|
||||
XX => None,
|
||||
v => Some((mdl - v as i32 as u32) >> 1),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) const fn year_flags(&self) -> YearFlags {
|
||||
YearFlags((self.0 & 0b1111) as u8)
|
||||
}
|
||||
}
|
||||
|
||||
@ -420,7 +365,7 @@ impl fmt::Debug for Mdf {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Mdf, Of};
|
||||
use super::Mdf;
|
||||
use super::{YearFlags, A, AG, B, BA, C, CB, D, DC, E, ED, F, FE, G, GF};
|
||||
|
||||
const NONLEAP_FLAGS: [YearFlags; 7] = [A, B, C, D, E, F, G];
|
||||
@ -622,56 +567,13 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of_to_mdf() {
|
||||
for i in 0u32..=8192 {
|
||||
if let Some(of) = Of(i).validate() {
|
||||
assert!(of.to_mdf().valid());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mdf_to_of() {
|
||||
for i in 0u32..=8192 {
|
||||
let mdf = Mdf(i);
|
||||
assert_eq!(mdf.valid(), mdf.to_of().is_some());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of_to_mdf_to_of() {
|
||||
for i in 0u32..=8192 {
|
||||
if let Some(of) = Of(i).validate() {
|
||||
assert_eq!(of, of.to_mdf().to_of().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mdf_to_of_to_mdf() {
|
||||
for i in 0u32..=8192 {
|
||||
let mdf = Mdf(i);
|
||||
if mdf.valid() {
|
||||
assert_eq!(mdf, mdf.to_of().unwrap().to_mdf());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_returns_none() {
|
||||
let regular_year = YearFlags::from_year(2023);
|
||||
let leap_year = YearFlags::from_year(2024);
|
||||
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());
|
||||
assert!(Mdf::new(1, 32, regular_year).is_none());
|
||||
assert!(Mdf::new(2, 31, regular_year).is_some());
|
||||
|
||||
assert!(Of::from_mdf(Mdf::new(2, 30, regular_year).unwrap()).is_none());
|
||||
assert!(Of::from_mdf(Mdf::new(2, 30, leap_year).unwrap()).is_none());
|
||||
assert!(Of::from_mdf(Mdf::new(2, 29, regular_year).unwrap()).is_none());
|
||||
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());
|
||||
let flags = YearFlags::from_year(2023);
|
||||
assert!(Mdf::new(0, 1, flags).is_none());
|
||||
assert!(Mdf::new(13, 1, flags).is_none());
|
||||
assert!(Mdf::new(1, 0, flags).is_none());
|
||||
assert!(Mdf::new(1, 32, flags).is_none());
|
||||
assert!(Mdf::new(2, 31, flags).is_some());
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user