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 1 #1428

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
53 changes: 35 additions & 18 deletions src/naive/date.rs
Expand Up @@ -2,6 +2,16 @@
// See README.md and LICENSE.txt for details.

//! ISO 8601 calendar date without timezone.
//!
//! The implementation is optimized for determining year, month, day and day of week.
//!
//! Format of `NaiveDate`:
//! `YYYY_YYYY_YYYY_YYYY_YYYO_OOOO_OOOO_LWWW`
//! `Y`: Year
//! `O`: Ordinal
//! `L`: leap year flag (1 = common year, 0 is leap year)
//! `W`: weekday before the first day of the year
//! `LWWW`: will also be referred to as the year flags (`F`)

#[cfg(feature = "alloc")]
use core::borrow::Borrow;
Expand All @@ -27,12 +37,9 @@ use crate::naive::{IsoWeek, NaiveDateTime, NaiveTime};
use crate::{expect, try_opt};
use crate::{Datelike, TimeDelta, Weekday};

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

const MAX_YEAR: i32 = internals::MAX_YEAR;
const MIN_YEAR: i32 = internals::MIN_YEAR;

/// A week represented by a [`NaiveDate`] and a [`Weekday`] which is the first
/// day of the week.
#[derive(Debug)]
Expand Down Expand Up @@ -196,7 +203,7 @@ impl Days {
)]
#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
pub struct NaiveDate {
ymdf: DateImpl, // (year << 13) | of
yof: i32, // (year << 13) | of
}

/// The minimum possible `NaiveDate` (January 1, 262145 BCE).
Expand Down Expand Up @@ -233,7 +240,7 @@ impl NaiveDate {
}
debug_assert!(YearFlags::from_year(year).0 == flags.0);
match Of::new(ordinal, flags) {
Some(of) => Some(NaiveDate { ymdf: (year << 13) | (of.inner() as DateImpl) }),
Some(of) => Some(NaiveDate { yof: (year << 13) | (of.inner() as i32) }),
None => None, // Invalid: Ordinal outside of the nr of days in a year with those flags.
}
}
Expand All @@ -245,7 +252,7 @@ impl NaiveDate {
return None; // Out-of-range
}
match mdf.to_of() {
Some(of) => Some(NaiveDate { ymdf: (year << 13) | (of.inner() as DateImpl) }),
Some(of) => Some(NaiveDate { yof: (year << 13) | (of.inner() as i32) }),
None => None, // Non-existing date
}
}
Expand Down Expand Up @@ -787,10 +794,10 @@ impl NaiveDate {
pub(crate) const fn add_days(self, days: i32) -> Option<Self> {
// fast path if the result is within the same year
const ORDINAL_MASK: i32 = 0b1_1111_1111_0000;
if let Some(ordinal) = ((self.ymdf & ORDINAL_MASK) >> 4).checked_add(days) {
if let Some(ordinal) = ((self.yof & ORDINAL_MASK) >> 4).checked_add(days) {
if ordinal > 0 && ordinal <= 365 {
let year_and_flags = self.ymdf & !ORDINAL_MASK;
return Some(NaiveDate { ymdf: year_and_flags | (ordinal << 4) });
let year_and_flags = self.yof & !ORDINAL_MASK;
return Some(NaiveDate { yof: year_and_flags | (ordinal << 4) });
}
}
// do the full check
Expand Down Expand Up @@ -1041,7 +1048,7 @@ impl NaiveDate {
/// Returns the packed ordinal-flags.
#[inline]
const fn of(&self) -> Of {
Of::from_date_impl(self.ymdf)
Of::from_date_impl(self.yof)
}

/// Makes a new `NaiveDate` with the packed month-day-flags changed.
Expand All @@ -1058,7 +1065,7 @@ impl NaiveDate {
/// Does not check if the year flags match the year.
#[inline]
const fn with_of(&self, of: Of) -> NaiveDate {
NaiveDate { ymdf: (self.ymdf & !0b1_1111_1111_1111) | of.inner() as DateImpl }
NaiveDate { yof: (self.yof & !0b1_1111_1111_1111) | of.inner() as i32 }
}

/// Makes a new `NaiveDate` for the next calendar date.
Expand Down Expand Up @@ -1433,13 +1440,13 @@ impl NaiveDate {
/// assert_eq!(NaiveDate::from_ymd_opt(2100, 1, 1).unwrap().leap_year(), false);
/// ```
pub const fn leap_year(&self) -> bool {
self.ymdf & (0b1000) == 0
self.yof & (0b1000) == 0
}

// This duplicates `Datelike::year()`, because trait methods can't be const yet.
#[inline]
const fn year(&self) -> i32 {
self.ymdf >> 13
self.yof >> 13
}

/// Returns the day of year starting from 1.
Expand Down Expand Up @@ -1484,16 +1491,16 @@ impl NaiveDate {
}

/// The minimum possible `NaiveDate` (January 1, 262144 BCE).
pub const MIN: NaiveDate = NaiveDate { ymdf: (MIN_YEAR << 13) | (1 << 4) | 0o12 /*D*/ };
pub const MIN: NaiveDate = NaiveDate { yof: (MIN_YEAR << 13) | (1 << 4) | 0o12 /*D*/ };
/// The maximum possible `NaiveDate` (December 31, 262142 CE).
pub const MAX: NaiveDate = NaiveDate { ymdf: (MAX_YEAR << 13) | (365 << 4) | 0o16 /*G*/ };
pub const MAX: NaiveDate = NaiveDate { yof: (MAX_YEAR << 13) | (365 << 4) | 0o16 /*G*/ };

/// One day before the minimum possible `NaiveDate` (December 31, 262145 BCE).
pub(crate) const BEFORE_MIN: NaiveDate =
NaiveDate { ymdf: ((MIN_YEAR - 1) << 13) | (366 << 4) | 0o07 /*FE*/ };
NaiveDate { yof: ((MIN_YEAR - 1) << 13) | (366 << 4) | 0o07 /*FE*/ };
/// One day after the maximum possible `NaiveDate` (January 1, 262143 CE).
pub(crate) const AFTER_MAX: NaiveDate =
NaiveDate { ymdf: ((MAX_YEAR + 1) << 13) | (1 << 4) | 0o17 /*F*/ };
NaiveDate { yof: ((MAX_YEAR + 1) << 13) | (1 << 4) | 0o17 /*F*/ };
}

impl Datelike for NaiveDate {
Expand Down Expand Up @@ -2292,6 +2299,16 @@ const fn div_mod_floor(val: i32, div: i32) -> (i32, i32) {
(val.div_euclid(div), val.rem_euclid(div))
}

/// MAX_YEAR is one year less than the type is capable of representing. Internally we may sometimes
/// use the headroom, notably to handle cases where the offset of a `DateTime` constructed with
/// `NaiveDate::MAX` pushes it beyond the valid, representable range.
pub(super) const MAX_YEAR: i32 = (i32::MAX >> 13) - 1;

/// MIN_YEAR is one year more than the type is capable of representing. Internally we may sometimes
/// use the headroom, notably to handle cases where the offset of a `DateTime` constructed with
/// `NaiveDate::MIN` pushes it beyond the valid, representable range.
pub(super) const MIN_YEAR: i32 = (i32::MIN >> 13) + 1;

#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))]
fn test_encodable_json<F, E>(to_string: F)
where
Expand Down
17 changes: 2 additions & 15 deletions src/naive/internals.rs
Expand Up @@ -18,19 +18,6 @@
use crate::Weekday;
use core::fmt;

/// The internal date representation: `year << 13 | Of`
pub(super) type DateImpl = i32;

/// MAX_YEAR is one year less than the type is capable of representing. Internally we may sometimes
/// use the headroom, notably to handle cases where the offset of a `DateTime` constructed with
/// `NaiveDate::MAX` pushes it beyond the valid, representable range.
pub(super) const MAX_YEAR: DateImpl = (i32::MAX >> 13) - 1;

/// MIN_YEAR is one year more than the type is capable of representing. Internally we may sometimes
/// use the headroom, notably to handle cases where the offset of a `DateTime` constructed with
/// `NaiveDate::MIN` pushes it beyond the valid, representable range.
pub(super) const MIN_YEAR: DateImpl = (i32::MIN >> 13) + 1;

/// The year flags (aka the dominical letter).
///
/// There are 14 possible classes of year in the Gregorian calendar:
Expand Down Expand Up @@ -285,8 +272,8 @@ impl Of {
of.validate()
}

pub(super) const fn from_date_impl(date_impl: DateImpl) -> Of {
// We assume the value in the `DateImpl` is valid.
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
12 changes: 6 additions & 6 deletions src/naive/isoweek.rs
Expand Up @@ -5,7 +5,7 @@

use core::fmt;

use super::internals::{DateImpl, Of, YearFlags};
use super::internals::{Of, YearFlags};

#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))]
use rkyv::{Archive, Deserialize, Serialize};
Expand All @@ -28,7 +28,7 @@ pub struct IsoWeek {
// note that this allows for larger year range than `NaiveDate`.
// this is crucial because we have an edge case for the first and last week supported,
// which year number might not match the calendar year number.
ywf: DateImpl, // (year << 10) | (week << 4) | flag
ywf: i32, // (year << 10) | (week << 4) | flag
}

/// Returns the corresponding `IsoWeek` from the year and the `Of` internal value.
Expand All @@ -53,7 +53,7 @@ pub(super) fn iso_week_from_yof(year: i32, of: Of) -> IsoWeek {
}
};
let flags = YearFlags::from_year(year);
IsoWeek { ywf: (year << 10) | (week << 4) as DateImpl | DateImpl::from(flags.0) }
IsoWeek { ywf: (year << 10) | (week << 4) as i32 | i32::from(flags.0) }
}

impl IsoWeek {
Expand Down Expand Up @@ -155,21 +155,21 @@ impl fmt::Debug for IsoWeek {
mod tests {
#[cfg(feature = "rkyv-validation")]
use super::IsoWeek;
use crate::naive::{internals, NaiveDate};
use crate::naive::date::{self, NaiveDate};
use crate::Datelike;

#[test]
fn test_iso_week_extremes() {
let minweek = NaiveDate::MIN.iso_week();
let maxweek = NaiveDate::MAX.iso_week();

assert_eq!(minweek.year(), internals::MIN_YEAR);
assert_eq!(minweek.year(), date::MIN_YEAR);
assert_eq!(minweek.week(), 1);
assert_eq!(minweek.week0(), 0);
#[cfg(feature = "alloc")]
assert_eq!(format!("{:?}", minweek), NaiveDate::MIN.format("%G-W%V").to_string());

assert_eq!(maxweek.year(), internals::MAX_YEAR + 1);
assert_eq!(maxweek.year(), date::MAX_YEAR + 1);
assert_eq!(maxweek.week(), 1);
assert_eq!(maxweek.week0(), 0);
#[cfg(feature = "alloc")]
Expand Down