aboutsummaryrefslogtreecommitdiff
path: root/src/naive/internals.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/naive/internals.rs')
-rw-r--r--src/naive/internals.rs477
1 files changed, 280 insertions, 197 deletions
diff --git a/src/naive/internals.rs b/src/naive/internals.rs
index 346063c..c6d7536 100644
--- a/src/naive/internals.rs
+++ b/src/naive/internals.rs
@@ -13,19 +13,23 @@
//! but the conversion keeps the valid value valid and the invalid value invalid
//! so that the user-facing `NaiveDate` can validate the input as late as possible.
-#![allow(dead_code)] // some internal methods have been left for consistency
#![cfg_attr(feature = "__internal_bench", allow(missing_docs))]
-use core::{fmt, i32};
-use div::{div_rem, mod_floor};
-use num_traits::FromPrimitive;
-use Weekday;
+use crate::Weekday;
+use core::fmt;
-/// The internal date representation. This also includes the packed `Mdf` value.
-pub type DateImpl = i32;
+/// The internal date representation: `year << 13 | Of`
+pub(super) type DateImpl = i32;
-pub const MAX_YEAR: DateImpl = i32::MAX >> 13;
-pub const MIN_YEAR: DateImpl = i32::MIN >> 13;
+/// 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).
///
@@ -35,25 +39,26 @@ pub const MIN_YEAR: DateImpl = i32::MIN >> 13;
/// where `a` is `1` for the common year (simplifies the `Of` validation)
/// and `bbb` is a non-zero `Weekday` (mapping `Mon` to 7) of the last day in the past year
/// (simplifies the day of week calculation from the 1-based ordinal).
-#[derive(PartialEq, Eq, Copy, Clone)]
-pub struct YearFlags(pub u8);
-
-pub const A: YearFlags = YearFlags(0o15);
-pub const AG: YearFlags = YearFlags(0o05);
-pub const B: YearFlags = YearFlags(0o14);
-pub const BA: YearFlags = YearFlags(0o04);
-pub const C: YearFlags = YearFlags(0o13);
-pub const CB: YearFlags = YearFlags(0o03);
-pub const D: YearFlags = YearFlags(0o12);
-pub const DC: YearFlags = YearFlags(0o02);
-pub const E: YearFlags = YearFlags(0o11);
-pub const ED: YearFlags = YearFlags(0o01);
-pub const F: YearFlags = YearFlags(0o17);
-pub const FE: YearFlags = YearFlags(0o07);
-pub const G: YearFlags = YearFlags(0o16);
-pub const GF: YearFlags = YearFlags(0o06);
-
-static YEAR_TO_FLAGS: [YearFlags; 400] = [
+#[allow(unreachable_pub)] // public as an alias for benchmarks only
+#[derive(PartialEq, Eq, Copy, Clone, Hash)]
+pub struct YearFlags(pub(super) u8);
+
+pub(super) const A: YearFlags = YearFlags(0o15);
+pub(super) const AG: YearFlags = YearFlags(0o05);
+pub(super) const B: YearFlags = YearFlags(0o14);
+pub(super) const BA: YearFlags = YearFlags(0o04);
+pub(super) const C: YearFlags = YearFlags(0o13);
+pub(super) const CB: YearFlags = YearFlags(0o03);
+pub(super) const D: YearFlags = YearFlags(0o12);
+pub(super) const DC: YearFlags = YearFlags(0o02);
+pub(super) const E: YearFlags = YearFlags(0o11);
+pub(super) const ED: YearFlags = YearFlags(0o01);
+pub(super) const F: YearFlags = YearFlags(0o17);
+pub(super) const FE: YearFlags = YearFlags(0o07);
+pub(super) const G: YearFlags = YearFlags(0o16);
+pub(super) const GF: YearFlags = YearFlags(0o06);
+
+const YEAR_TO_FLAGS: &[YearFlags; 400] = &[
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,
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,
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,
@@ -72,7 +77,7 @@ static YEAR_TO_FLAGS: [YearFlags; 400] = [
D, CB, A, G, F, ED, C, B, A, GF, E, D, C, // 400
];
-static YEAR_DELTAS: [u8; 401] = [
+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,
@@ -94,44 +99,48 @@ static YEAR_DELTAS: [u8; 401] = [
96, 97, 97, 97, 97, // 400+1
];
-pub fn cycle_to_yo(cycle: u32) -> (u32, u32) {
- let (mut year_mod_400, mut ordinal0) = div_rem(cycle, 365);
- let delta = u32::from(YEAR_DELTAS[year_mod_400 as usize]);
+pub(super) 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 - u32::from(YEAR_DELTAS[year_mod_400 as usize]);
+ ordinal0 += 365 - YEAR_DELTAS[year_mod_400 as usize] as u32;
} else {
ordinal0 -= delta;
}
(year_mod_400, ordinal0 + 1)
}
-pub fn yo_to_cycle(year_mod_400: u32, ordinal: u32) -> u32 {
- year_mod_400 * 365 + u32::from(YEAR_DELTAS[year_mod_400 as usize]) + ordinal - 1
+pub(super) 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
}
impl YearFlags {
+ #[allow(unreachable_pub)] // public as an alias for benchmarks only
+ #[doc(hidden)] // for benchmarks only
#[inline]
- pub fn from_year(year: i32) -> YearFlags {
- let year = mod_floor(year, 400);
+ #[must_use]
+ pub const fn from_year(year: i32) -> YearFlags {
+ let year = year.rem_euclid(400);
YearFlags::from_year_mod_400(year)
}
#[inline]
- pub fn from_year_mod_400(year: i32) -> YearFlags {
+ pub(super) const fn from_year_mod_400(year: i32) -> YearFlags {
YEAR_TO_FLAGS[year as usize]
}
#[inline]
- pub fn ndays(&self) -> u32 {
+ pub(super) const fn ndays(&self) -> u32 {
let YearFlags(flags) = *self;
- 366 - u32::from(flags >> 3)
+ 366 - (flags >> 3) as u32
}
#[inline]
- pub fn isoweek_delta(&self) -> u32 {
+ pub(super) const fn isoweek_delta(&self) -> u32 {
let YearFlags(flags) = *self;
- let mut delta = u32::from(flags) & 0b0111;
+ let mut delta = (flags & 0b0111) as u32;
if delta < 3 {
delta += 7;
}
@@ -139,7 +148,7 @@ impl YearFlags {
}
#[inline]
- pub fn nisoweeks(&self) -> u32 {
+ pub(super) const fn nisoweeks(&self) -> u32 {
let YearFlags(flags) = *self;
52 + ((0b0000_0100_0000_0110 >> flags as usize) & 1)
}
@@ -170,13 +179,13 @@ impl fmt::Debug for YearFlags {
}
}
-pub const MIN_OL: u32 = 1 << 1;
-pub const MAX_OL: u32 = 366 << 1; // larger than the non-leap last day `(365 << 1) | 1`
-pub const MIN_MDL: u32 = (1 << 6) | (1 << 1);
-pub const MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1;
+// 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;
const XX: i8 = -128;
-static MDL_TO_OL: [i8; MAX_MDL as usize + 1] = [
+const MDL_TO_OL: &[i8; MAX_MDL as usize + 1] = &[
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0
@@ -219,7 +228,7 @@ static MDL_TO_OL: [i8; MAX_MDL as usize + 1] = [
100, // 12
];
-static OL_TO_MDL: [u8; MAX_OL as usize + 1] = [
+const OL_TO_MDL: &[u8; MAX_OL as usize + 1] = &[
0, 0, // 0
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
@@ -264,95 +273,106 @@ static OL_TO_MDL: [u8; MAX_OL as usize + 1] = [
///
/// 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 struct Of(pub u32);
+pub(super) struct Of(u32);
impl Of {
#[inline]
- fn clamp_ordinal(ordinal: u32) -> u32 {
- if ordinal > 366 {
- 0
- } else {
- ordinal
- }
+ pub(super) const fn new(ordinal: u32, YearFlags(flags): YearFlags) -> Option<Of> {
+ let of = Of((ordinal << 4) | flags as u32);
+ of.validate()
}
- #[inline]
- pub fn new(ordinal: u32, YearFlags(flags): YearFlags) -> Of {
- let ordinal = Of::clamp_ordinal(ordinal);
- Of((ordinal << 4) | u32::from(flags))
+ pub(super) const fn from_date_impl(date_impl: DateImpl) -> Of {
+ // We assume the value in the `DateImpl` is valid.
+ Of((date_impl & 0b1_1111_1111_1111) as u32)
}
#[inline]
- pub fn from_mdf(Mdf(mdf): Mdf) -> Of {
+ pub(super) const fn from_mdf(Mdf(mdf): Mdf) -> Option<Of> {
let mdl = mdf >> 3;
- match MDL_TO_OL.get(mdl as usize) {
- Some(&v) => Of(mdf.wrapping_sub((i32::from(v) as u32 & 0x3ff) << 3)),
- None => Of(0),
+ 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 fn valid(&self) -> bool {
- let Of(of) = *self;
- let ol = of >> 3;
- MIN_OL <= ol && ol <= MAX_OL
+ pub(super) const fn inner(&self) -> u32 {
+ self.0
}
+ /// Returns `(ordinal << 1) | leap-year-flag`.
#[inline]
- pub fn ordinal(&self) -> u32 {
- let Of(of) = *self;
- of >> 4
+ const fn ol(&self) -> u32 {
+ self.0 >> 3
}
#[inline]
- pub fn with_ordinal(&self, ordinal: u32) -> Of {
- let ordinal = Of::clamp_ordinal(ordinal);
- let Of(of) = *self;
- Of((of & 0b1111) | (ordinal << 4))
+ const fn validate(self) -> Option<Of> {
+ let ol = self.ol();
+ match ol >= MIN_OL && ol <= MAX_OL {
+ true => Some(self),
+ false => None,
+ }
}
#[inline]
- pub fn flags(&self) -> YearFlags {
- let Of(of) = *self;
- YearFlags((of & 0b1111) as u8)
+ pub(super) const fn ordinal(&self) -> u32 {
+ self.0 >> 4
}
#[inline]
- pub fn with_flags(&self, YearFlags(flags): YearFlags) -> Of {
- let Of(of) = *self;
- Of((of & !0b1111) | u32::from(flags))
+ pub(super) const fn with_ordinal(&self, ordinal: u32) -> Option<Of> {
+ let of = Of((ordinal << 4) | (self.0 & 0b1111));
+ of.validate()
+ }
+
+ #[inline]
+ pub(super) const fn flags(&self) -> YearFlags {
+ YearFlags((self.0 & 0b1111) as u8)
}
#[inline]
- pub fn weekday(&self) -> Weekday {
+ pub(super) const fn weekday(&self) -> Weekday {
let Of(of) = *self;
- Weekday::from_u32(((of >> 4) + (of & 0b111)) % 7).unwrap()
+ weekday_from_u32_mod7((of >> 4) + (of & 0b111))
}
#[inline]
- pub fn isoweekdate_raw(&self) -> (u32, Weekday) {
+ 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(weekord % 7).unwrap())
+ (weekord / 7, weekday_from_u32_mod7(weekord))
}
+ #[cfg_attr(feature = "cargo-clippy", allow(clippy::wrong_self_convention))]
#[inline]
- pub fn to_mdf(&self) -> Mdf {
+ pub(super) const fn to_mdf(&self) -> Mdf {
Mdf::from_of(*self)
}
+ /// Returns an `Of` with the next day, or `None` if this is the last day of the year.
#[inline]
- pub fn succ(&self) -> Of {
- let Of(of) = *self;
- Of(of + (1 << 4))
+ pub(super) const fn succ(&self) -> Option<Of> {
+ let of = Of(self.0 + (1 << 4));
+ of.validate()
}
+ /// Returns an `Of` with the previous day, or `None` if this is the first day of the year.
#[inline]
- pub fn pred(&self) -> Of {
- let Of(of) = *self;
- Of(of - (1 << 4))
+ pub(super) const fn pred(&self) -> Option<Of> {
+ match self.ordinal() {
+ 1 => None,
+ _ => Some(Of(self.0 - (1 << 4))),
+ }
}
}
@@ -374,94 +394,88 @@ impl fmt::Debug for Of {
/// The whole bits except for the least 3 bits are referred as `Mdl`
/// (month, day of month and leap flag),
/// which is an index to the `MDL_TO_OL` lookup table.
+///
+/// The methods implemented on `Mdf` do not always return a valid value.
+/// Dates that can't exist, like February 30, can still be represented.
+/// Use `Mdl::valid` to check whether the date is valid.
#[derive(PartialEq, PartialOrd, Copy, Clone)]
-pub struct Mdf(pub u32);
+pub(super) struct Mdf(u32);
impl Mdf {
#[inline]
- fn clamp_month(month: u32) -> u32 {
- if month > 12 {
- 0
- } else {
- month
+ pub(super) const fn new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Option<Mdf> {
+ match month >= 1 && month <= 12 && day >= 1 && day <= 31 {
+ true => Some(Mdf((month << 9) | (day << 4) | flags as u32)),
+ false => None,
}
}
#[inline]
- fn clamp_day(day: u32) -> u32 {
- if day > 31 {
- 0
- } else {
- day
- }
- }
-
- #[inline]
- pub fn new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Mdf {
- let month = Mdf::clamp_month(month);
- let day = Mdf::clamp_day(day);
- Mdf((month << 9) | (day << 4) | u32::from(flags))
- }
-
- #[inline]
- pub fn from_of(Of(of): Of) -> Mdf {
+ pub(super) const fn from_of(Of(of): Of) -> Mdf {
let ol = of >> 3;
- match OL_TO_MDL.get(ol as usize) {
- Some(&v) => Mdf(of + (u32::from(v) << 3)),
- None => Mdf(0),
+ if ol <= MAX_OL {
+ // Array is indexed from `[1..=MAX_OL]`, with a `0` index having a meaningless value.
+ Mdf(of + ((OL_TO_MDL[ol as usize] as u32) << 3))
+ } else {
+ // Panicking here would be reasonable, but we are just going on with a safe value.
+ Mdf(0)
}
}
- #[inline]
- pub fn valid(&self) -> bool {
+ #[cfg(test)]
+ pub(super) const fn valid(&self) -> bool {
let Mdf(mdf) = *self;
let mdl = mdf >> 3;
- match MDL_TO_OL.get(mdl as usize) {
- Some(&v) => v >= 0,
- None => false,
+ if mdl <= MAX_MDL {
+ // Array is indexed from `[1..=MAX_MDL]`, with a `0` index having a meaningless value.
+ MDL_TO_OL[mdl as usize] >= 0
+ } else {
+ // Panicking here would be reasonable, but we are just going on with a safe value.
+ false
}
}
#[inline]
- pub fn month(&self) -> u32 {
+ pub(super) const fn month(&self) -> u32 {
let Mdf(mdf) = *self;
mdf >> 9
}
#[inline]
- pub fn with_month(&self, month: u32) -> Mdf {
- let month = Mdf::clamp_month(month);
+ pub(super) const fn with_month(&self, month: u32) -> Option<Mdf> {
+ if month > 12 {
+ return None;
+ }
+
let Mdf(mdf) = *self;
- Mdf((mdf & 0b1_1111_1111) | (month << 9))
+ Some(Mdf((mdf & 0b1_1111_1111) | (month << 9)))
}
#[inline]
- pub fn day(&self) -> u32 {
+ pub(super) const fn day(&self) -> u32 {
let Mdf(mdf) = *self;
(mdf >> 4) & 0b1_1111
}
#[inline]
- pub fn with_day(&self, day: u32) -> Mdf {
- let day = Mdf::clamp_day(day);
- let Mdf(mdf) = *self;
- Mdf((mdf & !0b1_1111_0000) | (day << 4))
- }
+ pub(super) const fn with_day(&self, day: u32) -> Option<Mdf> {
+ if day > 31 {
+ return None;
+ }
- #[inline]
- pub fn flags(&self) -> YearFlags {
let Mdf(mdf) = *self;
- YearFlags((mdf & 0b1111) as u8)
+ Some(Mdf((mdf & !0b1_1111_0000) | (day << 4)))
}
#[inline]
- pub fn with_flags(&self, YearFlags(flags): YearFlags) -> Mdf {
+ pub(super) const fn with_flags(&self, YearFlags(flags): YearFlags) -> Mdf {
let Mdf(mdf) = *self;
- Mdf((mdf & !0b1111) | u32::from(flags))
+ Mdf((mdf & !0b1111) | flags as u32)
}
+ #[cfg_attr(feature = "cargo-clippy", allow(clippy::wrong_self_convention))]
#[inline]
- pub fn to_of(&self) -> Of {
+ pub(super) const fn to_of(&self) -> Option<Of> {
Of::from_mdf(*self)
}
}
@@ -480,16 +494,27 @@ 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 {
- #[cfg(test)]
- extern crate num_iter;
-
- use self::num_iter::range_inclusive;
+ 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 std::u32;
- use Weekday;
+ 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];
@@ -533,10 +558,15 @@ mod tests {
#[test]
fn test_of() {
fn check(expected: bool, flags: YearFlags, ordinal1: u32, ordinal2: u32) {
- for ordinal in range_inclusive(ordinal1, ordinal2) {
- let of = Of::new(ordinal, flags);
+ 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.valid() == expected,
+ of.validate().is_some() == expected,
"ordinal {} = {:?} should be {} for dominical year {:?}",
ordinal,
of,
@@ -564,9 +594,14 @@ mod tests {
#[test]
fn test_mdf_valid() {
fn check(expected: bool, flags: YearFlags, month1: u32, day1: u32, month2: u32, day2: u32) {
- for month in range_inclusive(month1, month2) {
- for day in range_inclusive(day1, day2) {
- let mdf = Mdf::new(month, day, flags);
+ for month in month1..=month2 {
+ for day in day1..=day2 {
+ let mdf = match Mdf::new(month, day, flags) {
+ Some(mdf) => mdf,
+ None if !expected => continue,
+ None => panic!("Mdf::new({}, {}, {:?}) returned None", month, day, flags),
+ };
+
assert!(
mdf.valid() == expected,
"month {} day {} = {:?} should be {} for dominical year {:?}",
@@ -650,9 +685,8 @@ mod tests {
#[test]
fn test_of_fields() {
for &flags in FLAGS.iter() {
- for ordinal in range_inclusive(1u32, 366) {
- let of = Of::new(ordinal, flags);
- if of.valid() {
+ for ordinal in 1u32..=366 {
+ if let Some(of) = Of::new(ordinal, flags) {
assert_eq!(of.ordinal(), ordinal);
}
}
@@ -662,12 +696,12 @@ mod tests {
#[test]
fn test_of_with_fields() {
fn check(flags: YearFlags, ordinal: u32) {
- let of = Of::new(ordinal, flags);
+ let of = Of::new(ordinal, flags).unwrap();
- for ordinal in range_inclusive(0u32, 1024) {
+ for ordinal in 0u32..=1024 {
let of = of.with_ordinal(ordinal);
- assert_eq!(of.valid(), Of::new(ordinal, flags).valid());
- if of.valid() {
+ assert_eq!(of, Of::new(ordinal, flags));
+ if let Some(of) = of {
assert_eq!(of.ordinal(), ordinal);
}
}
@@ -685,25 +719,25 @@ mod tests {
#[test]
fn test_of_weekday() {
- assert_eq!(Of::new(1, A).weekday(), Weekday::Sun);
- assert_eq!(Of::new(1, B).weekday(), Weekday::Sat);
- assert_eq!(Of::new(1, C).weekday(), Weekday::Fri);
- assert_eq!(Of::new(1, D).weekday(), Weekday::Thu);
- assert_eq!(Of::new(1, E).weekday(), Weekday::Wed);
- assert_eq!(Of::new(1, F).weekday(), Weekday::Tue);
- assert_eq!(Of::new(1, G).weekday(), Weekday::Mon);
- assert_eq!(Of::new(1, AG).weekday(), Weekday::Sun);
- assert_eq!(Of::new(1, BA).weekday(), Weekday::Sat);
- assert_eq!(Of::new(1, CB).weekday(), Weekday::Fri);
- assert_eq!(Of::new(1, DC).weekday(), Weekday::Thu);
- assert_eq!(Of::new(1, ED).weekday(), Weekday::Wed);
- assert_eq!(Of::new(1, FE).weekday(), Weekday::Tue);
- assert_eq!(Of::new(1, GF).weekday(), Weekday::Mon);
+ 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).weekday();
- for ordinal in range_inclusive(2u32, flags.ndays()) {
- let of = Of::new(ordinal, flags);
+ 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;
@@ -714,9 +748,13 @@ mod tests {
#[test]
fn test_mdf_fields() {
for &flags in FLAGS.iter() {
- for month in range_inclusive(1u32, 12) {
- for day in range_inclusive(1u32, 31) {
- let mdf = Mdf::new(month, day, flags);
+ for month in 1u32..=12 {
+ for day in 1u32..31 {
+ let mdf = match Mdf::new(month, day, flags) {
+ Some(mdf) => mdf,
+ None => continue,
+ };
+
if mdf.valid() {
assert_eq!(mdf.month(), month);
assert_eq!(mdf.day(), day);
@@ -729,20 +767,28 @@ mod tests {
#[test]
fn test_mdf_with_fields() {
fn check(flags: YearFlags, month: u32, day: u32) {
- let mdf = Mdf::new(month, day, flags);
+ let mdf = Mdf::new(month, day, flags).unwrap();
+
+ for month in 0u32..=16 {
+ let mdf = match mdf.with_month(month) {
+ Some(mdf) => mdf,
+ None if month > 12 => continue,
+ None => panic!("failed to create Mdf with month {}", month),
+ };
- for month in range_inclusive(0u32, 16) {
- let mdf = mdf.with_month(month);
- assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid());
if mdf.valid() {
assert_eq!(mdf.month(), month);
assert_eq!(mdf.day(), day);
}
}
- for day in range_inclusive(0u32, 1024) {
- let mdf = mdf.with_day(day);
- assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid());
+ for day in 0u32..=1024 {
+ let mdf = match mdf.with_day(day) {
+ Some(mdf) => mdf,
+ None if day > 31 => continue,
+ None => panic!("failed to create Mdf with month {}", month),
+ };
+
if mdf.valid() {
assert_eq!(mdf.month(), month);
assert_eq!(mdf.day(), day);
@@ -772,44 +818,81 @@ mod tests {
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).isoweekdate_raw();
+ let (week, _) = Of::new(4 /* January 4 */, flags).unwrap().isoweekdate_raw();
assert_eq!(week, 1);
}
}
#[test]
fn test_of_to_mdf() {
- for i in range_inclusive(0u32, 8192) {
- let of = Of(i);
- assert_eq!(of.valid(), of.to_mdf().valid());
+ 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 range_inclusive(0u32, 8192) {
+ for i in 0u32..=8192 {
let mdf = Mdf(i);
- assert_eq!(mdf.valid(), mdf.to_of().valid());
+ assert_eq!(mdf.valid(), mdf.to_of().is_some());
}
}
#[test]
fn test_of_to_mdf_to_of() {
- for i in range_inclusive(0u32, 8192) {
- let of = Of(i);
- if of.valid() {
- assert_eq!(of, 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 range_inclusive(0u32, 8192) {
+ for i in 0u32..=8192 {
let mdf = Mdf(i);
if mdf.valid() {
- assert_eq!(mdf, mdf.to_of().to_mdf());
+ 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!(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());
+ 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());
+
+ assert!(Of::new(365, regular_year).unwrap().succ().is_none());
+ assert!(Of::new(365, leap_year).unwrap().succ().is_some());
+ assert!(Of::new(366, leap_year).unwrap().succ().is_none());
+
+ assert!(Of::new(1, regular_year).unwrap().pred().is_none());
+ assert!(Of::new(1, leap_year).unwrap().pred().is_none());
+ }
+
+ #[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);
+ }
}