Skip to content

Commit

Permalink
fix(datetime): Verify leap years
Browse files Browse the repository at this point in the history
Fixes #188
  • Loading branch information
epage committed Oct 23, 2023
1 parent 3e44692 commit 045e896
Show file tree
Hide file tree
Showing 10 changed files with 77 additions and 27 deletions.
11 changes: 1 addition & 10 deletions crates/toml/tests/decoder_compliance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,7 @@ fn main() {
let decoder = decoder::Decoder;
let mut harness = toml_test_harness::DecoderHarness::new(decoder);
harness.version("1.0.0");
harness
.ignore([
"invalid/datetime/feb-30.toml",
"invalid/datetime/feb-29.toml",
"invalid/local-date/feb-30.toml",
"invalid/local-date/feb-29.toml",
"invalid/local-datetime/feb-29.toml",
"invalid/local-datetime/feb-30.toml",
])
.unwrap();
harness.ignore([]).unwrap();
harness.test();
}

Expand Down
10 changes: 9 additions & 1 deletion crates/toml_datetime/src/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,15 @@ impl FromStr for Datetime {
if date.month < 1 || date.month > 12 {
return Err(DatetimeParseError {});
}
if date.day < 1 || date.day > 31 {
let is_leap_year =
(date.year % 4 == 0) && ((date.year % 100 != 0) || (date.year % 400 == 0));
let max_days_in_month = match date.month {
2 if is_leap_year => 29,
2 => 28,
4 | 6 | 9 | 11 => 30,
_ => 31,
};
if date.day < 1 || date.day > max_days_in_month {
return Err(DatetimeParseError {});
}

Expand Down
36 changes: 30 additions & 6 deletions crates/toml_edit/src/parser/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use winnow::combinator::alt;
use winnow::combinator::cut_err;
use winnow::combinator::opt;
use winnow::combinator::preceded;
use winnow::stream::Stream as _;
use winnow::token::one_of;
use winnow::token::take_while;
use winnow::trace::trace;
Expand Down Expand Up @@ -53,12 +54,35 @@ pub(crate) fn date_time(input: &mut Input<'_>) -> PResult<Datetime> {

// full-date = date-fullyear "-" date-month "-" date-mday
pub(crate) fn full_date(input: &mut Input<'_>) -> PResult<Date> {
trace(
"full-date",
(date_fullyear, b'-', cut_err((date_month, b'-', date_mday)))
.map(|(year, _, (month, _, day))| Date { year, month, day }),
)
.parse_next(input)
trace("full-date", full_date_).parse_next(input)
}

fn full_date_(input: &mut Input<'_>) -> PResult<Date> {
let year = date_fullyear.parse_next(input)?;
let _ = b'-'.parse_next(input)?;
let month = cut_err(date_month).parse_next(input)?;
let _ = cut_err(b'-').parse_next(input)?;
let day_start = input.checkpoint();
let day = cut_err(date_mday).parse_next(input)?;

let is_leap_year = (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
let max_days_in_month = match month {
2 if is_leap_year => 29,
2 => 28,
4 | 6 | 9 | 11 => 30,
_ => 31,
};
if max_days_in_month < day {
input.reset(day_start);
return Err(winnow::error::ErrMode::from_external_error(
input,
winnow::error::ErrorKind::Verify,
CustomError::OutOfRange,
)
.cut());
}

Ok(Date { year, month, day })
}

// partial-time = time-hour ":" time-minute ":" time-second [time-secfrac]
Expand Down
11 changes: 1 addition & 10 deletions crates/toml_edit/tests/decoder_compliance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,6 @@ fn main() {
let decoder = decoder::Decoder;
let mut harness = toml_test_harness::DecoderHarness::new(decoder);
harness.version("1.0.0");
harness
.ignore([
"invalid/datetime/feb-30.toml",
"invalid/datetime/feb-29.toml",
"invalid/local-date/feb-30.toml",
"invalid/local-date/feb-29.toml",
"invalid/local-datetime/feb-29.toml",
"invalid/local-datetime/feb-30.toml",
])
.unwrap();
harness.ignore([]).unwrap();
harness.test();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
TOML parse error at line 1, column 29
|
1 | "not a leap year" = 2100-02-29T15:15:15Z
| ^
invalid date-time
value is out of range
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
TOML parse error at line 1, column 44
|
1 | "only 28 or 29 days in february" = 1988-02-30T15:15:15Z
| ^
invalid date-time
value is out of range
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
TOML parse error at line 1, column 29
|
1 | "not a leap year" = 2100-02-29
| ^
invalid date-time
value is out of range
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
TOML parse error at line 1, column 44
|
1 | "only 28 or 29 days in february" = 1988-02-30
| ^
invalid date-time
value is out of range
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
TOML parse error at line 1, column 29
|
1 | "not a leap year" = 2100-02-29T15:15:15
| ^
invalid date-time
value is out of range
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
TOML parse error at line 1, column 44
|
1 | "only 28 or 29 days in february" = 1988-02-30T15:15:15
| ^
invalid date-time
value is out of range

0 comments on commit 045e896

Please sign in to comment.