diff --git a/arrow/parser.py b/arrow/parser.py index fc3774b0..052e1f09 100644 --- a/arrow/parser.py +++ b/arrow/parser.py @@ -758,6 +758,13 @@ def _build_datetime(parts: _Parts) -> datetime: f"The provided day of year {day_of_year!r} is invalid." ) + # strptime accepts day 366 in a non-leap year and rolls the result + # over into the next year instead of rejecting it; guard that here. + if dt.year != int(_year): + raise ParserError( + f"The provided day of year {day_of_year!r} is invalid." + ) + parts["year"] = dt.year parts["month"] = dt.month parts["day"] = dt.day diff --git a/tests/test_parser.py b/tests/test_parser.py index 7038d880..588c570d 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -939,8 +939,10 @@ def test_YYYY_DDDD(self): # Since 2016 is a leap year, the 366th day falls in the same year assert self.parser.parse_iso("2016-366") == datetime(2016, 12, 31) - # Since 2017 is not a leap year, the 366th day falls in the next year - assert self.parser.parse_iso("2017-366") == datetime(2018, 1, 1) + # 2017 is not a leap year, so its 366th day does not exist and is + # rejected rather than rolling over into the next year + with pytest.raises(ParserError): + self.parser.parse_iso("2017-366") def test_YYYY_DDDD_HH_mm_ssZ(self): assert self.parser.parse_iso("2013-036 04:05:06+01:00") == datetime(