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

Deprecate panicking constructors of TimeDelta #1450

Merged
merged 13 commits into from
Mar 6, 2024
Merged
Prev Previous commit
Next Next commit
Use simplified resolve_week_date
  • Loading branch information
pitdicker committed Mar 1, 2024
commit 1a5e7f6bf5a6f7fc6125b50949f5f10f0e8fe709
98 changes: 34 additions & 64 deletions src/format/parsed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -694,71 694,15 @@ impl Parsed {
(verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
}

(
Some(year),
_,
&Parsed { week_from_sun: Some(week_from_sun), weekday: Some(weekday), .. },
) => {
(Some(year), _, &Parsed { week_from_sun: Some(week), weekday: Some(weekday), .. }) => {
// year, week (starting at 1st Sunday), day of the week
let newyear = NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE)?;
let firstweek = match newyear.weekday() {
Weekday::Sun => 0,
Weekday::Mon => 6,
Weekday::Tue => 5,
Weekday::Wed => 4,
Weekday::Thu => 3,
Weekday::Fri => 2,
Weekday::Sat => 1,
};

// `firstweek 1`-th day of January is the beginning of the week 1.
if week_from_sun > 53 {
return Err(OUT_OF_RANGE);
} // can it overflow?
let ndays = firstweek
(week_from_sun as i32 - 1) * 7
weekday.num_days_from_sunday() as i32;
let date = newyear
.checked_add_signed(TimeDelta::days(i64::from(ndays)))
.ok_or(OUT_OF_RANGE)?;
if date.year() != year {
return Err(OUT_OF_RANGE);
} // early exit for correct error

let date = resolve_week_date(year, week, weekday, Weekday::Sun)?;
(verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
}

(
Some(year),
_,
&Parsed { week_from_mon: Some(week_from_mon), weekday: Some(weekday), .. },
) => {
(Some(year), _, &Parsed { week_from_mon: Some(week), weekday: Some(weekday), .. }) => {
// year, week (starting at 1st Monday), day of the week
let newyear = NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE)?;
let firstweek = match newyear.weekday() {
Weekday::Sun => 1,
Weekday::Mon => 0,
Weekday::Tue => 6,
Weekday::Wed => 5,
Weekday::Thu => 4,
Weekday::Fri => 3,
Weekday::Sat => 2,
};

// `firstweek 1`-th day of January is the beginning of the week 1.
if week_from_mon > 53 {
return Err(OUT_OF_RANGE);
} // can it overflow?
let ndays = firstweek
(week_from_mon as i32 - 1) * 7
weekday.num_days_from_monday() as i32;
let date = newyear
.checked_add_signed(TimeDelta::days(i64::from(ndays)))
.ok_or(OUT_OF_RANGE)?;
if date.year() != year {
return Err(OUT_OF_RANGE);
} // early exit for correct error

let date = resolve_week_date(year, week, weekday, Weekday::Mon)?;
(verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
}

Expand Down Expand Up @@ -1038,6 982,32 @@ impl Parsed {
}
}

/// Create a `NaiveDate` when given a year, week, weekday, and the definition at which day of the
/// week a week starts.
///
/// Returns `IMPOSSIBLE` if `week` is `0` or `53` and the `weekday` falls outside the year.
fn resolve_week_date(
year: i32,
week: u32,
weekday: Weekday,
week_start_day: Weekday,
) -> ParseResult<NaiveDate> {
if week > 53 {
return Err(OUT_OF_RANGE);
}

let first_day_of_year = NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE)?;
// Ordinal of the day at which week 1 starts.
let first_week_start = 1 week_start_day.num_days_from(first_day_of_year.weekday()) as i32;
// Number of the `weekday`, which is 0 for the first day of the week.
let weekday = weekday.num_days_from(week_start_day) as i32;
let ordinal = first_week_start (week as i32 - 1) * 7 weekday;
if ordinal <= 0 {
return Err(IMPOSSIBLE);
}
first_day_of_year.with_ordinal(ordinal as u32).ok_or(IMPOSSIBLE)
}

#[cfg(test)]
mod tests {
use super::super::{IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE};
Expand Down Expand Up @@ -1216,8 1186,8 @@ mod tests {
assert_eq!(parse!(year: 2000, week_from_mon: 0), Err(NOT_ENOUGH));
assert_eq!(parse!(year: 2000, week_from_sun: 0), Err(NOT_ENOUGH));
assert_eq!(parse!(year: 2000, weekday: Sun), Err(NOT_ENOUGH));
assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Fri), Err(OUT_OF_RANGE));
assert_eq!(parse!(year: 2000, week_from_sun: 0, weekday: Fri), Err(OUT_OF_RANGE));
assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Fri), Err(IMPOSSIBLE));
assert_eq!(parse!(year: 2000, week_from_sun: 0, weekday: Fri), Err(IMPOSSIBLE));
assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Sat), ymd(2000, 1, 1));
assert_eq!(parse!(year: 2000, week_from_sun: 0, weekday: Sat), ymd(2000, 1, 1));
assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Sun), ymd(2000, 1, 2));
Expand All @@ -1231,9 1201,9 @@ mod tests {
assert_eq!(parse!(year: 2000, week_from_mon: 2, weekday: Mon), ymd(2000, 1, 10));
assert_eq!(parse!(year: 2000, week_from_sun: 52, weekday: Sat), ymd(2000, 12, 30));
assert_eq!(parse!(year: 2000, week_from_sun: 53, weekday: Sun), ymd(2000, 12, 31));
assert_eq!(parse!(year: 2000, week_from_sun: 53, weekday: Mon), Err(OUT_OF_RANGE));
assert_eq!(parse!(year: 2000, week_from_sun: 53, weekday: Mon), Err(IMPOSSIBLE));
assert_eq!(parse!(year: 2000, week_from_sun: 0xffffffff, weekday: Mon), Err(OUT_OF_RANGE));
assert_eq!(parse!(year: 2006, week_from_sun: 0, weekday: Sat), Err(OUT_OF_RANGE));
assert_eq!(parse!(year: 2006, week_from_sun: 0, weekday: Sat), Err(IMPOSSIBLE));
assert_eq!(parse!(year: 2006, week_from_sun: 1, weekday: Sun), ymd(2006, 1, 1));

// weekdates: conflicting inputs
Expand Down