Skip to content

Commit

Permalink
Convert NaiveDate::from_isoywd to return Result
Browse files Browse the repository at this point in the history
  • Loading branch information
pitdicker committed Feb 25, 2024
1 parent d2a387f commit b3387d6
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 69 deletions.
4 changes: 2 additions & 2 deletions src/format/parsed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,8 +470,8 @@ impl Parsed {

(_, Some(isoyear), &Parsed { isoweek: Some(isoweek), weekday: Some(weekday), .. }) => {
// ISO year, week, day of the week
let date = NaiveDate::from_isoywd(isoyear, isoweek, weekday);
let date = date.ok_or(OUT_OF_RANGE)?;
let date =
NaiveDate::from_isoywd(isoyear, isoweek, weekday).map_err(|_| OUT_OF_RANGE)?;
(verify_ymd(date) && verify_ordinal(date), date)
}

Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@
//! // July 8 is 188th day of the year 2014 (`o` for "ordinal")
//! assert_eq!(dt, NaiveDate::from_yo(2014, 189).unwrap().and_hms(9, 10, 11).unwrap().and_utc());
//! // July 8 is Tuesday in ISO week 28 of the year 2014.
//! assert_eq!(dt, NaiveDate::from_isoywd(2014, 28, Weekday::Tue)?.and_hms(9, 10, 11).unwrap().and_utc());
//! assert_eq!(dt, NaiveDate::from_isoywd(2014, 28, Weekday::Tue).unwrap().and_hms(9, 10, 11).unwrap().and_utc());
//!
//! let dt = NaiveDate::from_ymd(2014, 7, 8).unwrap().and_hms_milli(9, 10, 11, 12).unwrap().and_local_timezone(Utc).unwrap(); // `2014-07-08T09:10:11.012Z`
//! assert_eq!(dt, NaiveDate::from_ymd(2014, 7, 8).unwrap().and_hms_micro(9, 10, 11, 12_000).unwrap().and_local_timezone(Utc).unwrap());
Expand Down
90 changes: 47 additions & 43 deletions src/naive/date/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,79 +217,83 @@ impl NaiveDate {
///
/// # Errors
///
/// Returns `None` if:
/// - The specified week does not exist in that year (for example 2023 week 53).
/// - The value for `week` is invalid (for example: `0`, `60`).
/// - If the resulting date is out of range for `NaiveDate`.
/// This method returns:
/// - [`Error::DoesNotExist`] if specified week does not exist in that year (for example 2023
/// week 53).
/// - [`Error::InvalidArgument`] if the value for `week` is invalid (for example: `0`, `60`).
/// - [`Error::OutOfRange`] if the resulting date is out of range for `NaiveDate`.
///
/// # Example
///
/// ```
/// use chrono::{NaiveDate, Weekday};
/// use chrono::{Error, NaiveDate, Weekday};
///
/// let from_ymd = |y, m, d| NaiveDate::from_ymd(y, m, d).unwrap();
/// let from_isoywd = NaiveDate::from_isoywd;
///
/// assert_eq!(from_isoywd(2015, 0, Weekday::Sun), None);
/// assert_eq!(from_isoywd(2015, 10, Weekday::Sun), Some(from_ymd(2015, 3, 8)));
/// assert_eq!(from_isoywd(2015, 30, Weekday::Mon), Some(from_ymd(2015, 7, 20)));
/// assert_eq!(from_isoywd(2015, 60, Weekday::Mon), None);
/// assert_eq!(from_isoywd(2015, 0, Weekday::Sun), Err(Error::InvalidArgument));
/// assert_eq!(from_isoywd(2015, 10, Weekday::Sun), Ok(from_ymd(2015, 3, 8)));
/// assert_eq!(from_isoywd(2015, 30, Weekday::Mon), Ok(from_ymd(2015, 7, 20)));
/// assert_eq!(from_isoywd(2015, 60, Weekday::Mon), Err(Error::InvalidArgument));
///
/// assert_eq!(from_isoywd(400000, 10, Weekday::Fri), None);
/// assert_eq!(from_isoywd(-400000, 10, Weekday::Sat), None);
/// assert_eq!(from_isoywd(400000, 10, Weekday::Fri), Err(Error::OutOfRange));
/// assert_eq!(from_isoywd(-400000, 10, Weekday::Sat), Err(Error::OutOfRange));
/// ```
///
/// The year number of ISO week date may differ from that of the calendar date.
///
/// ```
/// # use chrono::{NaiveDate, Weekday};
/// # use chrono::{Error, NaiveDate, Weekday};
/// # let from_ymd = |y, m, d| NaiveDate::from_ymd(y, m, d).unwrap();
/// # let from_isoywd = NaiveDate::from_isoywd;
/// // Mo Tu We Th Fr Sa Su
/// // 2014-W52 22 23 24 25 26 27 28 has 4+ days of new year,
/// // 2015-W01 29 30 31 1 2 3 4 <- so this is the first week
/// assert_eq!(from_isoywd(2014, 52, Weekday::Sun), Some(from_ymd(2014, 12, 28)));
/// assert_eq!(from_isoywd(2014, 53, Weekday::Mon), None);
/// assert_eq!(from_isoywd(2015, 1, Weekday::Mon), Some(from_ymd(2014, 12, 29)));
/// assert_eq!(from_isoywd(2014, 52, Weekday::Sun), Ok(from_ymd(2014, 12, 28)));
/// assert_eq!(from_isoywd(2014, 53, Weekday::Mon), Err(Error::DoesNotExist));
/// assert_eq!(from_isoywd(2015, 1, Weekday::Mon), Ok(from_ymd(2014, 12, 29)));
///
/// // 2015-W52 21 22 23 24 25 26 27 has 4+ days of old year,
/// // 2015-W53 28 29 30 31 1 2 3 <- so this is the last week
/// // 2016-W01 4 5 6 7 8 9 10
/// assert_eq!(from_isoywd(2015, 52, Weekday::Sun), Some(from_ymd(2015, 12, 27)));
/// assert_eq!(from_isoywd(2015, 53, Weekday::Sun), Some(from_ymd(2016, 1, 3)));
/// assert_eq!(from_isoywd(2015, 54, Weekday::Mon), None);
/// assert_eq!(from_isoywd(2016, 1, Weekday::Mon), Some(from_ymd(2016, 1, 4)));
/// assert_eq!(from_isoywd(2015, 52, Weekday::Sun), Ok(from_ymd(2015, 12, 27)));
/// assert_eq!(from_isoywd(2015, 53, Weekday::Sun), Ok(from_ymd(2016, 1, 3)));
/// assert_eq!(from_isoywd(2015, 54, Weekday::Mon), Err(Error::InvalidArgument));
/// assert_eq!(from_isoywd(2016, 1, Weekday::Mon), Ok(from_ymd(2016, 1, 4)));
/// ```
#[must_use]
pub const fn from_isoywd(year: i32, week: u32, weekday: Weekday) -> Option<NaiveDate> {
pub const fn from_isoywd(year: i32, week: u32, weekday: Weekday) -> Result<NaiveDate, Error> {
let flags = YearFlags::from_year(year);
let nweeks = flags.nisoweeks();
if 1 <= week && week <= nweeks {
// ordinal = week ordinal - delta
let weekord = week * 7 + weekday as u32;
let delta = flags.isoweek_delta();
if weekord <= delta {
// ordinal < 1, previous year
let prevflags = YearFlags::from_year(year - 1);
ok!(NaiveDate::from_ordinal_and_flags(
year - 1,
weekord + prevflags.ndays() - delta,
prevflags,
))
if week == 0 || week > nweeks {
if week == 0 || week > 53 {
return Err(Error::InvalidArgument);
} else {
let ordinal = weekord - delta;
let ndays = flags.ndays();
if ordinal <= ndays {
// this year
ok!(NaiveDate::from_ordinal_and_flags(year, ordinal, flags))
} else {
// ordinal > ndays, next year
let nextflags = YearFlags::from_year(year + 1);
ok!(NaiveDate::from_ordinal_and_flags(year + 1, ordinal - ndays, nextflags))
}
return Err(Error::DoesNotExist);
}
}
// ordinal = week ordinal - delta
let weekord = week * 7 + weekday as u32;
let delta = flags.isoweek_delta();
if weekord <= delta {
// ordinal < 1, previous year
let prevflags = YearFlags::from_year(year - 1);
NaiveDate::from_ordinal_and_flags(
year - 1,
weekord + prevflags.ndays() - delta,
prevflags,
)
} else {
None
let ordinal = weekord - delta;
let ndays = flags.ndays();
if ordinal <= ndays {
// this year
NaiveDate::from_ordinal_and_flags(year, ordinal, flags)
} else {
// ordinal > ndays, next year
let nextflags = YearFlags::from_year(year + 1);
NaiveDate::from_ordinal_and_flags(year + 1, ordinal - ndays, nextflags)
}
}
}

Expand Down
46 changes: 23 additions & 23 deletions src/naive/date/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,28 +201,28 @@ fn test_date_from_isoywd() {
let from_isoywd = NaiveDate::from_isoywd;
let ymd = |y, m, d| NaiveDate::from_ymd(y, m, d).unwrap();

assert_eq!(from_isoywd(2004, 0, Weekday::Sun), None);
assert_eq!(from_isoywd(2004, 1, Weekday::Mon), Some(ymd(2003, 12, 29)));
assert_eq!(from_isoywd(2004, 1, Weekday::Sun), Some(ymd(2004, 1, 4)));
assert_eq!(from_isoywd(2004, 2, Weekday::Mon), Some(ymd(2004, 1, 5)));
assert_eq!(from_isoywd(2004, 2, Weekday::Sun), Some(ymd(2004, 1, 11)));
assert_eq!(from_isoywd(2004, 52, Weekday::Mon), Some(ymd(2004, 12, 20)));
assert_eq!(from_isoywd(2004, 52, Weekday::Sun), Some(ymd(2004, 12, 26)));
assert_eq!(from_isoywd(2004, 53, Weekday::Mon), Some(ymd(2004, 12, 27)));
assert_eq!(from_isoywd(2004, 53, Weekday::Sun), Some(ymd(2005, 1, 2)));
assert_eq!(from_isoywd(2004, 54, Weekday::Mon), None);

assert_eq!(from_isoywd(2011, 0, Weekday::Sun), None);
assert_eq!(from_isoywd(2011, 1, Weekday::Mon), Some(ymd(2011, 1, 3)));
assert_eq!(from_isoywd(2011, 1, Weekday::Sun), Some(ymd(2011, 1, 9)));
assert_eq!(from_isoywd(2011, 2, Weekday::Mon), Some(ymd(2011, 1, 10)));
assert_eq!(from_isoywd(2011, 2, Weekday::Sun), Some(ymd(2011, 1, 16)));

assert_eq!(from_isoywd(2018, 51, Weekday::Mon), Some(ymd(2018, 12, 17)));
assert_eq!(from_isoywd(2018, 51, Weekday::Sun), Some(ymd(2018, 12, 23)));
assert_eq!(from_isoywd(2018, 52, Weekday::Mon), Some(ymd(2018, 12, 24)));
assert_eq!(from_isoywd(2018, 52, Weekday::Sun), Some(ymd(2018, 12, 30)));
assert_eq!(from_isoywd(2018, 53, Weekday::Mon), None);
assert_eq!(from_isoywd(2004, 0, Weekday::Sun), Err(Error::InvalidArgument));
assert_eq!(from_isoywd(2004, 1, Weekday::Mon), Ok(ymd(2003, 12, 29)));
assert_eq!(from_isoywd(2004, 1, Weekday::Sun), Ok(ymd(2004, 1, 4)));
assert_eq!(from_isoywd(2004, 2, Weekday::Mon), Ok(ymd(2004, 1, 5)));
assert_eq!(from_isoywd(2004, 2, Weekday::Sun), Ok(ymd(2004, 1, 11)));
assert_eq!(from_isoywd(2004, 52, Weekday::Mon), Ok(ymd(2004, 12, 20)));
assert_eq!(from_isoywd(2004, 52, Weekday::Sun), Ok(ymd(2004, 12, 26)));
assert_eq!(from_isoywd(2004, 53, Weekday::Mon), Ok(ymd(2004, 12, 27)));
assert_eq!(from_isoywd(2004, 53, Weekday::Sun), Ok(ymd(2005, 1, 2)));
assert_eq!(from_isoywd(2004, 54, Weekday::Mon), Err(Error::InvalidArgument));

assert_eq!(from_isoywd(2011, 0, Weekday::Sun), Err(Error::InvalidArgument));
assert_eq!(from_isoywd(2011, 1, Weekday::Mon), Ok(ymd(2011, 1, 3)));
assert_eq!(from_isoywd(2011, 1, Weekday::Sun), Ok(ymd(2011, 1, 9)));
assert_eq!(from_isoywd(2011, 2, Weekday::Mon), Ok(ymd(2011, 1, 10)));
assert_eq!(from_isoywd(2011, 2, Weekday::Sun), Ok(ymd(2011, 1, 16)));

assert_eq!(from_isoywd(2018, 51, Weekday::Mon), Ok(ymd(2018, 12, 17)));
assert_eq!(from_isoywd(2018, 51, Weekday::Sun), Ok(ymd(2018, 12, 23)));
assert_eq!(from_isoywd(2018, 52, Weekday::Mon), Ok(ymd(2018, 12, 24)));
assert_eq!(from_isoywd(2018, 52, Weekday::Sun), Ok(ymd(2018, 12, 30)));
assert_eq!(from_isoywd(2018, 53, Weekday::Mon), Err(Error::DoesNotExist));
}

#[test]
Expand All @@ -241,7 +241,7 @@ fn test_date_from_isoywd_and_iso_week() {
.iter()
{
let d = NaiveDate::from_isoywd(year, week, weekday);
if let Some(d) = d {
if let Ok(d) = d {
assert_eq!(d.weekday(), weekday);
let w = d.iso_week();
assert_eq!(w.year(), year);
Expand Down

0 comments on commit b3387d6

Please sign in to comment.