Skip to content

Commit

Permalink
Move NaiveWeek to separate module
Browse files Browse the repository at this point in the history
  • Loading branch information
pitdicker committed Feb 12, 2024
1 parent c8c8ec4 commit 7834d6d
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 128 deletions.
101 changes: 3 additions & 98 deletions src/naive/date/mod.rs
Expand Up @@ -16,7 +16,7 @@
#[cfg(feature = "alloc")]
use core::borrow::Borrow;
use core::iter::FusedIterator;
use core::ops::{Add, AddAssign, RangeInclusive, Sub, SubAssign};
use core::ops::{Add, AddAssign, Sub, SubAssign};
use core::{fmt, str};

#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))]
Expand All @@ -33,7 +33,7 @@ use crate::format::{
Parsed, StrftimeItems,
};
use crate::month::Months;
use crate::naive::{IsoWeek, NaiveDateTime, NaiveTime};
use crate::naive::{IsoWeek, NaiveDateTime, NaiveTime, NaiveWeek};
use crate::{expect, try_opt};
use crate::{Datelike, TimeDelta, Weekday};

Expand All @@ -48,101 +48,6 @@ pub(crate) mod serde;
#[cfg(test)]
mod tests;

/// A week represented by a [`NaiveDate`] and a [`Weekday`] which is the first
/// day of the week.
#[derive(Debug)]
pub struct NaiveWeek {
date: NaiveDate,
start: Weekday,
}

impl NaiveWeek {
/// Create a new `NaiveWeek`
pub(crate) const fn new(date: NaiveDate, start: Weekday) -> Self {
Self { date, start }
}

/// Returns a date representing the first day of the week.
///
/// # Panics
///
/// Panics if the first day of the week happens to fall just out of range of `NaiveDate`
/// (more than ca. 262,000 years away from common era).
///
/// # Examples
///
/// ```
/// use chrono::{NaiveDate, Weekday};
///
/// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap();
/// let week = date.week(Weekday::Mon);
/// assert!(week.first_day() <= date);
/// ```
#[inline]
#[must_use]
pub const fn first_day(&self) -> NaiveDate {
let start = self.start.num_days_from_monday() as i32;
let ref_day = self.date.weekday().num_days_from_monday() as i32;
// Calculate the number of days to subtract from `self.date`.
// Do not construct an intermediate date beyond `self.date`, because that may be out of
// range if `date` is close to `NaiveDate::MAX`.
let days = start - ref_day - if start > ref_day { 7 } else { 0 };
expect!(self.date.add_days(days), "first weekday out of range for `NaiveDate`")
}

/// Returns a date representing the last day of the week.
///
/// # Panics
///
/// Panics if the last day of the week happens to fall just out of range of `NaiveDate`
/// (more than ca. 262,000 years away from common era).
///
/// # Examples
///
/// ```
/// use chrono::{NaiveDate, Weekday};
///
/// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap();
/// let week = date.week(Weekday::Mon);
/// assert!(week.last_day() >= date);
/// ```
#[inline]
#[must_use]
pub const fn last_day(&self) -> NaiveDate {
let end = self.start.pred().num_days_from_monday() as i32;
let ref_day = self.date.weekday().num_days_from_monday() as i32;
// Calculate the number of days to add to `self.date`.
// Do not construct an intermediate date before `self.date` (like with `first_day()`),
// because that may be out of range if `date` is close to `NaiveDate::MIN`.
let days = end - ref_day + if end < ref_day { 7 } else { 0 };
expect!(self.date.add_days(days), "last weekday out of range for `NaiveDate`")
}

/// Returns a [`RangeInclusive<T>`] representing the whole week bounded by
/// [first_day](NaiveWeek::first_day) and [last_day](NaiveWeek::last_day) functions.
///
/// # Panics
///
/// Panics if the either the first or last day of the week happens to fall just out of range of
/// `NaiveDate` (more than ca. 262,000 years away from common era).
///
/// # Examples
///
/// ```
/// use chrono::{NaiveDate, Weekday};
///
/// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap();
/// let week = date.week(Weekday::Mon);
/// let days = week.days();
/// assert!(days.contains(&date));
/// ```
#[inline]
#[must_use]
pub const fn days(&self) -> RangeInclusive<NaiveDate> {
self.first_day()..=self.last_day()
}
}

/// A duration in calendar days.
///
/// This is useful because when using `TimeDelta` it is possible that adding `TimeDelta::days(1)`
Expand Down Expand Up @@ -1478,7 +1383,7 @@ impl NaiveDate {
/// Returns the day of week.
// This duplicates `Datelike::weekday()`, because trait methods can't be const yet.
#[inline]
const fn weekday(&self) -> Weekday {
pub(super) const fn weekday(&self) -> Weekday {
match (((self.yof & ORDINAL_MASK) >> 4) + (self.yof & WEEKDAY_FLAGS_MASK)) % 7 {
0 => Weekday::Mon,
1 => Weekday::Tue,
Expand Down
29 changes: 0 additions & 29 deletions src/naive/date/tests.rs
Expand Up @@ -687,35 +687,6 @@ fn test_week_iterator_limit() {
);
}

#[test]
fn test_naiveweek() {
let date = NaiveDate::from_ymd_opt(2022, 5, 18).unwrap();
let asserts = [
(Weekday::Mon, "Mon 2022-05-16", "Sun 2022-05-22"),
(Weekday::Tue, "Tue 2022-05-17", "Mon 2022-05-23"),
(Weekday::Wed, "Wed 2022-05-18", "Tue 2022-05-24"),
(Weekday::Thu, "Thu 2022-05-12", "Wed 2022-05-18"),
(Weekday::Fri, "Fri 2022-05-13", "Thu 2022-05-19"),
(Weekday::Sat, "Sat 2022-05-14", "Fri 2022-05-20"),
(Weekday::Sun, "Sun 2022-05-15", "Sat 2022-05-21"),
];
for (start, first_day, last_day) in asserts {
let week = date.week(start);
let days = week.days();
assert_eq!(Ok(week.first_day()), NaiveDate::parse_from_str(first_day, "%a %Y-%m-%d"));
assert_eq!(Ok(week.last_day()), NaiveDate::parse_from_str(last_day, "%a %Y-%m-%d"));
assert!(days.contains(&date));
}
}

#[test]
fn test_naiveweek_min_max() {
let date_max = NaiveDate::MAX;
assert!(date_max.week(Weekday::Mon).first_day() <= date_max);
let date_min = NaiveDate::MIN;
assert!(date_min.week(Weekday::Mon).last_day() >= date_min);
}

#[test]
fn test_weeks_from() {
// tests per: https://github.com/chronotope/chrono/issues/961
Expand Down
4 changes: 3 additions & 1 deletion src/naive/mod.rs
Expand Up @@ -9,8 +9,9 @@ pub(crate) mod datetime;
mod internals;
pub(crate) mod isoweek;
pub(crate) mod time;
mod week;

pub use self::date::{Days, NaiveDate, NaiveDateDaysIterator, NaiveDateWeeksIterator, NaiveWeek};
pub use self::date::{Days, NaiveDate, NaiveDateDaysIterator, NaiveDateWeeksIterator};
#[allow(deprecated)]
pub use self::date::{MAX_DATE, MIN_DATE};
#[cfg(feature = "rustc-serialize")]
Expand All @@ -20,6 +21,7 @@ pub use self::datetime::rustc_serialize::TsSeconds;
pub use self::datetime::{NaiveDateTime, MAX_DATETIME, MIN_DATETIME};
pub use self::isoweek::IsoWeek;
pub use self::time::NaiveTime;
pub use self::week::NaiveWeek;

#[cfg(feature = "__internal_bench")]
#[doc(hidden)]
Expand Down
133 changes: 133 additions & 0 deletions src/naive/week.rs
@@ -0,0 +1,133 @@
use core::ops::RangeInclusive;

use crate::expect;
use crate::naive::NaiveDate;
use crate::Weekday;

/// A week represented by a [`NaiveDate`] and a [`Weekday`] which is the first
/// day of the week.
#[derive(Debug)]
pub struct NaiveWeek {
date: NaiveDate,
start: Weekday,
}

impl NaiveWeek {
/// Create a new `NaiveWeek`
pub(crate) const fn new(date: NaiveDate, start: Weekday) -> Self {
Self { date, start }
}

/// Returns a date representing the first day of the week.
///
/// # Panics
///
/// Panics if the first day of the week happens to fall just out of range of `NaiveDate`
/// (more than ca. 262,000 years away from common era).
///
/// # Examples
///
/// ```
/// use chrono::{NaiveDate, Weekday};
///
/// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap();
/// let week = date.week(Weekday::Mon);
/// assert!(week.first_day() <= date);
/// ```
#[inline]
#[must_use]
pub const fn first_day(&self) -> NaiveDate {
let start = self.start.num_days_from_monday() as i32;
let ref_day = self.date.weekday().num_days_from_monday() as i32;
// Calculate the number of days to subtract from `self.date`.
// Do not construct an intermediate date beyond `self.date`, because that may be out of
// range if `date` is close to `NaiveDate::MAX`.
let days = start - ref_day - if start > ref_day { 7 } else { 0 };
expect!(self.date.add_days(days), "first weekday out of range for `NaiveDate`")
}

/// Returns a date representing the last day of the week.
///
/// # Panics
///
/// Panics if the last day of the week happens to fall just out of range of `NaiveDate`
/// (more than ca. 262,000 years away from common era).
///
/// # Examples
///
/// ```
/// use chrono::{NaiveDate, Weekday};
///
/// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap();
/// let week = date.week(Weekday::Mon);
/// assert!(week.last_day() >= date);
/// ```
#[inline]
#[must_use]
pub const fn last_day(&self) -> NaiveDate {
let end = self.start.pred().num_days_from_monday() as i32;
let ref_day = self.date.weekday().num_days_from_monday() as i32;
// Calculate the number of days to add to `self.date`.
// Do not construct an intermediate date before `self.date` (like with `first_day()`),
// because that may be out of range if `date` is close to `NaiveDate::MIN`.
let days = end - ref_day + if end < ref_day { 7 } else { 0 };
expect!(self.date.add_days(days), "last weekday out of range for `NaiveDate`")
}

/// Returns a [`RangeInclusive<T>`] representing the whole week bounded by
/// [first_day](NaiveWeek::first_day) and [last_day](NaiveWeek::last_day) functions.
///
/// # Panics
///
/// Panics if the either the first or last day of the week happens to fall just out of range of
/// `NaiveDate` (more than ca. 262,000 years away from common era).
///
/// # Examples
///
/// ```
/// use chrono::{NaiveDate, Weekday};
///
/// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap();
/// let week = date.week(Weekday::Mon);
/// let days = week.days();
/// assert!(days.contains(&date));
/// ```
#[inline]
#[must_use]
pub const fn days(&self) -> RangeInclusive<NaiveDate> {
self.first_day()..=self.last_day()
}
}

#[cfg(test)]
mod test {
use crate::{NaiveDate, Weekday};
#[test]
fn test_naiveweek() {
let date = NaiveDate::from_ymd_opt(2022, 5, 18).unwrap();
let asserts = [
(Weekday::Mon, "Mon 2022-05-16", "Sun 2022-05-22"),
(Weekday::Tue, "Tue 2022-05-17", "Mon 2022-05-23"),
(Weekday::Wed, "Wed 2022-05-18", "Tue 2022-05-24"),
(Weekday::Thu, "Thu 2022-05-12", "Wed 2022-05-18"),
(Weekday::Fri, "Fri 2022-05-13", "Thu 2022-05-19"),
(Weekday::Sat, "Sat 2022-05-14", "Fri 2022-05-20"),
(Weekday::Sun, "Sun 2022-05-15", "Sat 2022-05-21"),
];
for (start, first_day, last_day) in asserts {
let week = date.week(start);
let days = week.days();
assert_eq!(Ok(week.first_day()), NaiveDate::parse_from_str(first_day, "%a %Y-%m-%d"));
assert_eq!(Ok(week.last_day()), NaiveDate::parse_from_str(last_day, "%a %Y-%m-%d"));
assert!(days.contains(&date));
}
}

#[test]
fn test_naiveweek_min_max() {
let date_max = NaiveDate::MAX;
assert!(date_max.week(Weekday::Mon).first_day() <= date_max);
let date_min = NaiveDate::MIN;
assert!(date_min.week(Weekday::Mon).last_day() >= date_min);
}
}

0 comments on commit 7834d6d

Please sign in to comment.