diff --git a/src/pendulum/parser.py b/src/pendulum/parser.py index 833bae3c..9500e810 100644 --- a/src/pendulum/parser.py +++ b/src/pendulum/parser.py @@ -9,6 +9,7 @@ from pendulum.duration import Duration from pendulum.parsing import _Interval from pendulum.parsing import parse as base_parse +from pendulum.parsing.exceptions import ParserError from pendulum.tz.timezone import UTC @@ -109,14 +110,21 @@ def _parse( dt, ) - return pendulum.interval( - pendulum.instance( - t.cast("datetime.datetime", parsed.start), tz=options.get("tz", UTC) - ), - pendulum.instance( - t.cast("datetime.datetime", parsed.end), tz=options.get("tz", UTC) - ), - ) + try: + return pendulum.interval( + pendulum.instance( + t.cast("datetime.datetime", parsed.start), tz=options.get("tz", UTC) + ), + pendulum.instance( + t.cast("datetime.datetime", parsed.end), tz=options.get("tz", UTC) + ), + ) + except TypeError as e: + # An interval whose ends are not both dates or both datetimes (e.g. + # "2020-01-01/12:30:00", a date and a bare time) has an undefined + # difference; report it as an invalid string instead of letting a + # TypeError escape parse(). + raise ParserError(f"Invalid interval string: {text!r}") from e if isinstance(parsed, Duration): return parsed diff --git a/tests/test_parsing.py b/tests/test_parsing.py index 34673c40..309074bc 100644 --- a/tests/test_parsing.py +++ b/tests/test_parsing.py @@ -1,7 +1,10 @@ from __future__ import annotations +import pytest + import pendulum +from pendulum.parsing.exceptions import ParserError from tests.conftest import assert_date from tests.conftest import assert_datetime from tests.conftest import assert_duration @@ -127,6 +130,18 @@ def test_parse_interval() -> None: assert interval.end.offset == 0 +def test_parse_interval_mismatched_ends() -> None: + # An interval whose ends are not both dates or both datetimes has an + # undefined difference; this used to raise a bare TypeError. + for text in ( + "2020-01-01/12:30:00", + "12:30:00/2020-01-01", + "12:00:00/13:00:00", + ): + with pytest.raises(ParserError): + pendulum.parse(text) + + def test_parse_now() -> None: assert pendulum.parse("now").timezone_name == "UTC" assert (