Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert NaiveDate::{from_yo|from_isoywd} to return Result #1460

Merged
merged 2 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/format/parsed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@ impl Parsed {

(Some(year), _, &Parsed { ordinal: Some(ordinal), .. }) => {
// year, day of the year
let date = NaiveDate::from_yo(year, ordinal).ok_or(OUT_OF_RANGE)?;
let date = NaiveDate::from_yo(year, ordinal).map_err(|_| OUT_OF_RANGE)?;
(verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
}

Expand All @@ -696,7 +696,7 @@ impl Parsed {
&Parsed { week_from_sun: Some(week_from_sun), weekday: Some(weekday), .. },
) => {
// year, week (starting at 1st Sunday), day of the week
let newyear = NaiveDate::from_yo(year, 1).ok_or(OUT_OF_RANGE)?;
let newyear = NaiveDate::from_yo(year, 1).map_err(|_| OUT_OF_RANGE)?;
let firstweek = match newyear.weekday() {
Weekday::Sun => 0,
Weekday::Mon => 6,
Expand Down Expand Up @@ -730,7 +730,7 @@ impl Parsed {
&Parsed { week_from_mon: Some(week_from_mon), weekday: Some(weekday), .. },
) => {
// year, week (starting at 1st Monday), day of the week
let newyear = NaiveDate::from_yo(year, 1).ok_or(OUT_OF_RANGE)?;
let newyear = NaiveDate::from_yo(year, 1).map_err(|_| OUT_OF_RANGE)?;
let firstweek = match newyear.weekday() {
Weekday::Sun => 1,
Weekday::Mon => 0,
Expand Down Expand Up @@ -760,8 +760,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
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,9 @@
//! assert_eq!(dt, NaiveDate::from_ymd(2014, 7, 8).unwrap().and_hms(9, 10, 11).unwrap().and_local_timezone(Utc).unwrap());
//!
//! // July 8 is 188th day of the year 2014 (`o` for "ordinal")
//! assert_eq!(dt, NaiveDate::from_yo(2014, 189)?.and_hms(9, 10, 11).unwrap().and_utc());
//! 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
101 changes: 56 additions & 45 deletions src/naive/date/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
let year = u.int_in_range(MIN_YEAR..=MAX_YEAR)?;
let max_days = YearFlags::from_year(year).ndays();
let ord = u.int_in_range(1..=max_days)?;
NaiveDate::from_yo(year, ord).ok_or(arbitrary::Error::IncorrectFormat)
NaiveDate::from_yo(year, ord).map_err(|_| arbitrary::Error::IncorrectFormat)

Check warning on line 112 in src/naive/date/mod.rs

View check run for this annotation

Codecov / codecov/patch

src/naive/date/mod.rs#L112

Added line #L112 was not covered by tests
}
}

Expand All @@ -124,18 +124,18 @@
year: i32,
ordinal: u32,
flags: YearFlags,
) -> Option<NaiveDate> {
) -> Result<NaiveDate, Error> {
if year < MIN_YEAR || year > MAX_YEAR {
return None; // Out-of-range
return Err(Error::OutOfRange);
}
if ordinal == 0 || ordinal > 366 {
return None; // Invalid
return Err(Error::InvalidArgument);
}
debug_assert!(YearFlags::from_year(year).0 == flags.0);
let yof = (year << 13) | (ordinal << 4) as i32 | flags.0 as i32;
match yof & OL_MASK <= MAX_OL {
true => Some(NaiveDate::from_yof(yof)),
false => None, // Does not exist: Ordinal 366 in a common year.
true => Ok(NaiveDate::from_yof(yof)),
false => Err(Error::DoesNotExist), // Ordinal 366 in a common year.
}
}

Expand Down Expand Up @@ -184,28 +184,28 @@
///
/// # Errors
///
/// Returns `None` if:
/// - The specified ordinal day does not exist (for example 2023-366).
/// - The value for `ordinal` is invalid (for example: `0`, `400`).
/// - `year` is out of range for `NaiveDate`.
/// This method returns:
/// - [`Error::DoesNotExist`] if the specified ordinal day does not exist (for example
/// 2023-366).
/// - [`Error::InvalidArgument`] if the value for `ordinal` is invalid (for example: `0`, `400`).
/// - [`Error::OutOfRange`] if `year` is out of range for a `NaiveDate`.
///
/// # Example
///
/// ```
/// use chrono::NaiveDate;
/// use chrono::{Error, NaiveDate};
///
/// let from_yo = NaiveDate::from_yo;
///
/// assert!(from_yo(2015, 100).is_some());
/// assert!(from_yo(2015, 0).is_none());
/// assert!(from_yo(2015, 365).is_some());
/// assert!(from_yo(2015, 366).is_none());
/// assert!(from_yo(-4, 366).is_some()); // 5 BCE is a leap year
/// assert!(from_yo(400000, 1).is_none());
/// assert!(from_yo(-400000, 1).is_none());
/// assert!(from_yo(2015, 100).is_ok());
/// assert_eq!(from_yo(2015, 0), Err(Error::InvalidArgument));
/// assert!(from_yo(2015, 365).is_ok());
/// assert_eq!(from_yo(2015, 366), Err(Error::DoesNotExist));
/// assert!(from_yo(-4, 366).is_ok()); // 5 BCE is a leap year
/// assert_eq!(from_yo(400000, 1), Err(Error::OutOfRange));
/// assert_eq!(from_yo(-400000, 1), Err(Error::OutOfRange));
/// ```
#[must_use]
pub const fn from_yo(year: i32, ordinal: u32) -> Option<NaiveDate> {
pub const fn from_yo(year: i32, ordinal: u32) -> Result<NaiveDate, Error> {
let flags = YearFlags::from_year(year);
NaiveDate::from_ordinal_and_flags(year, ordinal, flags)
}
Expand All @@ -216,55 +216,58 @@
///
/// # 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 week == 0 || week > nweeks {
return None;
return Err(match week == 0 || week > 53 {
true => Error::InvalidArgument,
false => Error::DoesNotExist,
});
}
// ordinal = week ordinal - delta
let weekord = week * 7 + weekday as u32;
Expand Down Expand Up @@ -317,7 +320,11 @@
let cycle = days.rem_euclid(146_097);
let (year_mod_400, ordinal) = cycle_to_yo(cycle as u32);
let flags = YearFlags::from_year_mod_400(year_mod_400 as i32);
NaiveDate::from_ordinal_and_flags(year_div_400 * 400 + year_mod_400 as i32, ordinal, flags)
ok!(NaiveDate::from_ordinal_and_flags(
year_div_400 * 400 + year_mod_400 as i32,
ordinal,
flags
))
}

/// Makes a new `NaiveDate` by counting the number of occurrences of a particular day-of-week
Expand Down Expand Up @@ -625,7 +632,11 @@

let (year_mod_400, ordinal) = cycle_to_yo(cycle as u32);
let flags = YearFlags::from_year_mod_400(year_mod_400 as i32);
NaiveDate::from_ordinal_and_flags(year_div_400 * 400 + year_mod_400 as i32, ordinal, flags)
ok!(NaiveDate::from_ordinal_and_flags(
year_div_400 * 400 + year_mod_400 as i32,
ordinal,
flags
))
}

/// Makes a new `NaiveDateTime` from the current date and given `NaiveTime`.
Expand Down Expand Up @@ -828,7 +839,7 @@
let new_ol = (self.yof() & OL_MASK) + (1 << 4);
match new_ol <= MAX_OL {
true => Some(NaiveDate::from_yof(self.yof() & !OL_MASK | new_ol)),
false => NaiveDate::from_yo(self.year() + 1, 1),
false => ok!(NaiveDate::from_yo(self.year() + 1, 1)),
}
}

Expand Down
90 changes: 45 additions & 45 deletions src/naive/date/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,59 +170,59 @@ fn test_date_from_yo() {
let from_yo = NaiveDate::from_yo;
let ymd = |y, m, d| NaiveDate::from_ymd(y, m, d).unwrap();

assert_eq!(from_yo(2012, 0), None);
assert_eq!(from_yo(2012, 1), Some(ymd(2012, 1, 1)));
assert_eq!(from_yo(2012, 2), Some(ymd(2012, 1, 2)));
assert_eq!(from_yo(2012, 32), Some(ymd(2012, 2, 1)));
assert_eq!(from_yo(2012, 60), Some(ymd(2012, 2, 29)));
assert_eq!(from_yo(2012, 61), Some(ymd(2012, 3, 1)));
assert_eq!(from_yo(2012, 100), Some(ymd(2012, 4, 9)));
assert_eq!(from_yo(2012, 200), Some(ymd(2012, 7, 18)));
assert_eq!(from_yo(2012, 300), Some(ymd(2012, 10, 26)));
assert_eq!(from_yo(2012, 366), Some(ymd(2012, 12, 31)));
assert_eq!(from_yo(2012, 367), None);
assert_eq!(from_yo(2012, 1 << 28 | 60), None);

assert_eq!(from_yo(2014, 0), None);
assert_eq!(from_yo(2014, 1), Some(ymd(2014, 1, 1)));
assert_eq!(from_yo(2014, 2), Some(ymd(2014, 1, 2)));
assert_eq!(from_yo(2014, 32), Some(ymd(2014, 2, 1)));
assert_eq!(from_yo(2014, 59), Some(ymd(2014, 2, 28)));
assert_eq!(from_yo(2014, 60), Some(ymd(2014, 3, 1)));
assert_eq!(from_yo(2014, 100), Some(ymd(2014, 4, 10)));
assert_eq!(from_yo(2014, 200), Some(ymd(2014, 7, 19)));
assert_eq!(from_yo(2014, 300), Some(ymd(2014, 10, 27)));
assert_eq!(from_yo(2014, 365), Some(ymd(2014, 12, 31)));
assert_eq!(from_yo(2014, 366), None);
assert_eq!(from_yo(2012, 0), Err(Error::InvalidArgument));
assert_eq!(from_yo(2012, 1), Ok(ymd(2012, 1, 1)));
assert_eq!(from_yo(2012, 2), Ok(ymd(2012, 1, 2)));
assert_eq!(from_yo(2012, 32), Ok(ymd(2012, 2, 1)));
assert_eq!(from_yo(2012, 60), Ok(ymd(2012, 2, 29)));
assert_eq!(from_yo(2012, 61), Ok(ymd(2012, 3, 1)));
assert_eq!(from_yo(2012, 100), Ok(ymd(2012, 4, 9)));
assert_eq!(from_yo(2012, 200), Ok(ymd(2012, 7, 18)));
assert_eq!(from_yo(2012, 300), Ok(ymd(2012, 10, 26)));
assert_eq!(from_yo(2012, 366), Ok(ymd(2012, 12, 31)));
assert_eq!(from_yo(2012, 367), Err(Error::InvalidArgument));
assert_eq!(from_yo(2012, 1 << 28 | 60), Err(Error::InvalidArgument));

assert_eq!(from_yo(2014, 0), Err(Error::InvalidArgument));
assert_eq!(from_yo(2014, 1), Ok(ymd(2014, 1, 1)));
assert_eq!(from_yo(2014, 2), Ok(ymd(2014, 1, 2)));
assert_eq!(from_yo(2014, 32), Ok(ymd(2014, 2, 1)));
assert_eq!(from_yo(2014, 59), Ok(ymd(2014, 2, 28)));
assert_eq!(from_yo(2014, 60), Ok(ymd(2014, 3, 1)));
assert_eq!(from_yo(2014, 100), Ok(ymd(2014, 4, 10)));
assert_eq!(from_yo(2014, 200), Ok(ymd(2014, 7, 19)));
assert_eq!(from_yo(2014, 300), Ok(ymd(2014, 10, 27)));
assert_eq!(from_yo(2014, 365), Ok(ymd(2014, 12, 31)));
assert_eq!(from_yo(2014, 366), Err(Error::DoesNotExist));
}

#[test]
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(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), 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(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), 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(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
Loading