Skip to content

Commit

Permalink
[#654] Get week start and end days (#666)
Browse files Browse the repository at this point in the history
Co-authored-by: David Mazarro <dmunuera@stackbuilders.com>
Co-authored-by: Jorge Guerra <jguerra@stackbuilders.com>
  • Loading branch information
3 people committed Jun 15, 2022
1 parent 3c0d274 commit 8dd7245
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 4 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -1,2 +1,3 @@
target
Cargo.lock
.tool-versions
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -30,6 +30,7 @@ Versions with only mechanical changes will be omitted from the following list.
* Make `ParseErrorKind` public and available through `ParseError::kind()` (#588)
* Implement `DoubleEndedIterator` for `NaiveDateDaysIterator` and `NaiveDateWeeksIterator`
* Fix panicking when parsing a `DateTime` (@botahamec)
* Add support for getting week bounds based on a specific `NaiveDate` and a `Weekday` (#666)

## 0.4.19

Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Expand Up @@ -487,7 +487,7 @@ pub use format::{ParseError, ParseResult};

pub mod naive;
#[doc(no_inline)]
pub use naive::{IsoWeek, NaiveDate, NaiveDateTime, NaiveTime};
pub use naive::{IsoWeek, NaiveDate, NaiveDateTime, NaiveTime, NaiveWeek};

pub mod offset;
#[cfg(feature = "clock")]
Expand Down
96 changes: 94 additions & 2 deletions src/naive/date.rs
Expand Up @@ -5,7 +5,7 @@

#[cfg(any(feature = "alloc", feature = "std", test))]
use core::borrow::Borrow;
use core::ops::{Add, AddAssign, Sub, SubAssign};
use core::ops::{Add, AddAssign, RangeInclusive, Sub, SubAssign};
use core::{fmt, str};

use num_integer::div_mod_floor;
Expand All @@ -19,7 +19,7 @@ use crate::format::{parse, ParseError, ParseResult, Parsed, StrftimeItems};
use crate::format::{Item, Numeric, Pad};
use crate::naive::{IsoWeek, NaiveDateTime, NaiveTime};
use crate::oldtime::Duration as OldDuration;
use crate::{Datelike, Weekday};
use crate::{Datelike, Duration, Weekday};

use super::internals::{self, DateImpl, Mdf, Of, YearFlags};
use super::isoweek;
Expand Down Expand Up @@ -50,6 +50,70 @@ const MIN_DAYS_FROM_YEAR_0: i32 = (MIN_YEAR + 400_000) * 365 + (MIN_YEAR + 400_0
#[cfg(test)] // only used for testing, but duplicated in naive::datetime
const MAX_BITS: usize = 44;

/// 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 {
/// Returns a date representing the first day of the week.
///
/// # Examples
///
/// ```
/// use chrono::{NaiveDate, Weekday};
///
/// let date = NaiveDate::from_ymd(2022, 4, 18);
/// let week = date.week(Weekday::Mon);
/// assert!(week.first_day() <= date);
/// ```
#[inline]
pub fn first_day(&self) -> NaiveDate {
let start = self.start.num_days_from_monday();
let end = self.date.weekday().num_days_from_monday();
let days = if start > end { 7 - start + end } else { end - start };
self.date - Duration::days(days.into())
}

/// Returns a date representing the last day of the week.
///
/// # Examples
///
/// ```
/// use chrono::{NaiveDate, Weekday};
///
/// let date = NaiveDate::from_ymd(2022, 4, 18);
/// let week = date.week(Weekday::Mon);
/// assert!(week.last_day() >= date);
/// ```
#[inline]
pub fn last_day(&self) -> NaiveDate {
self.first_day() + Duration::days(6)
}

/// Returns a [`RangeInclusive<T>`] representing the whole week bounded by
/// [first_day](./struct.NaiveWeek.html#method.first_day) and
/// [last_day](./struct.NaiveWeek.html#method.last_day) functions.
///
/// # Examples
///
/// ```
/// use chrono::{NaiveDate, Weekday};
///
/// let date = NaiveDate::from_ymd(2022, 4, 18);
/// let week = date.week(Weekday::Mon);
/// let days = week.days();
/// assert!(days.contains(&date));
/// ```
#[inline]
pub fn days(&self) -> RangeInclusive<NaiveDate> {
self.first_day()..=self.last_day()
}
}

/// ISO 8601 calendar date without timezone.
/// Allows for every [proleptic Gregorian date](#calendar-date)
/// from Jan 1, 262145 BCE to Dec 31, 262143 CE.
Expand Down Expand Up @@ -1105,6 +1169,13 @@ impl NaiveDate {
pub fn iter_weeks(&self) -> NaiveDateWeeksIterator {
NaiveDateWeeksIterator { value: *self }
}

/// Returns the [`NaiveWeek`] that the date belongs to, starting with the [`Weekday`]
/// specified.
#[inline]
pub fn week(&self, start: Weekday) -> NaiveWeek {
NaiveWeek { date: *self, start }
}
}

impl Datelike for NaiveDate {
Expand Down Expand Up @@ -2445,4 +2516,25 @@ mod tests {
assert_eq!(NaiveDate::from_ymd(262143, 12, 12).iter_weeks().take(4).count(), 2);
assert_eq!(NaiveDate::from_ymd(-262144, 1, 15).iter_weeks().rev().take(4).count(), 2);
}

#[test]
fn test_naiveweek() {
let date = NaiveDate::from_ymd(2022, 5, 18);
let asserts = vec![
(Weekday::Mon, "2022-05-16", "2022-05-22"),
(Weekday::Tue, "2022-05-17", "2022-05-23"),
(Weekday::Wed, "2022-05-18", "2022-05-24"),
(Weekday::Thu, "2022-05-12", "2022-05-18"),
(Weekday::Fri, "2022-05-13", "2022-05-19"),
(Weekday::Sat, "2022-05-14", "2022-05-20"),
(Weekday::Sun, "2022-05-15", "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, "%Y-%m-%d"));
assert_eq!(Ok(week.last_day()), NaiveDate::parse_from_str(last_day, "%Y-%m-%d"));
assert!(days.contains(&date));
}
}
}
2 changes: 1 addition & 1 deletion src/naive/mod.rs
Expand Up @@ -10,7 +10,7 @@ mod internals;
mod isoweek;
mod time;

pub use self::date::{NaiveDate, MAX_DATE, MIN_DATE};
pub use self::date::{NaiveDate, NaiveWeek, MAX_DATE, MIN_DATE};
#[cfg(feature = "rustc-serialize")]
#[allow(deprecated)]
pub use self::datetime::rustc_serialize::TsSeconds;
Expand Down

0 comments on commit 8dd7245

Please sign in to comment.