-
Notifications
You must be signed in to change notification settings - Fork 518
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
139 additions
and
128 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} |