From 5bd81694c67a605569fdb86e42f616e7ddabe19f Mon Sep 17 00:00:00 2001 From: Vincent Gao Date: Wed, 24 Jun 2026 22:11:40 +0200 Subject: [PATCH 1/2] Fix parse_date and parse_time to raise ParseError instead of ValueError When parse_date or parse_time receives an input string whose numbers produce an out-of-range date or time (e.g. month 13, hour 25), the final datetime.date / datetime.time constructor raises a raw ValueError. Callers that catch ParseError (the documented error type for this module) never see the exception. Wrap both constructors in try/except and re-raise as ParseError with a message that includes the input string and the expected format pattern. Fixes #1178. --- babel/dates.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/babel/dates.py b/babel/dates.py index 69610a7f0..692493e89 100644 --- a/babel/dates.py +++ b/babel/dates.py @@ -1330,7 +1330,10 @@ def parse_date( day = int(numbers[indexes['D']]) if month > 12: month, day = day, month - return datetime.date(year, month, day) + try: + return datetime.date(year, month, day) + except ValueError as e: + raise ParseError(f"String '{string}' does not match format '{fmt.pattern}' ({e})") from None def parse_time( @@ -1395,7 +1398,10 @@ def parse_time( minute = int(numbers[indexes['M']]) if len(numbers) > 2: second = int(numbers[indexes['S']]) - return datetime.time(hour, minute, second) + try: + return datetime.time(hour, minute, second) + except ValueError as e: + raise ParseError(f"String '{string}' does not match format '{fmt.pattern}' ({e})") from None class DateTimePattern: From 0e92abc6d63daccfe1cb5d279b452d4221ae5bc1 Mon Sep 17 00:00:00 2001 From: Vincent Gao Date: Fri, 31 Jul 2026 21:06:35 +0200 Subject: [PATCH 2/2] Address review: chain ValueError, reword message, add out-of-range tests --- babel/dates.py | 8 ++++++-- tests/test_dates.py | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/babel/dates.py b/babel/dates.py index 692493e89..c99e6401e 100644 --- a/babel/dates.py +++ b/babel/dates.py @@ -1333,7 +1333,9 @@ def parse_date( try: return datetime.date(year, month, day) except ValueError as e: - raise ParseError(f"String '{string}' does not match format '{fmt.pattern}' ({e})") from None + raise ParseError( + f"'{string}' does not represent a valid date for format '{fmt.pattern}'" + ) from e def parse_time( @@ -1401,7 +1403,9 @@ def parse_time( try: return datetime.time(hour, minute, second) except ValueError as e: - raise ParseError(f"String '{string}' does not match format '{fmt.pattern}' ({e})") from None + raise ParseError( + f"'{string}' does not represent a valid time for format '{fmt.pattern}'" + ) from e class DateTimePattern: diff --git a/tests/test_dates.py b/tests/test_dates.py index 12bb23433..ecd4ec742 100644 --- a/tests/test_dates.py +++ b/tests/test_dates.py @@ -724,6 +724,20 @@ def test_parse_errors(case, func): func(case, locale='en_US') +def test_parse_date_out_of_range(): + with pytest.raises(dates.ParseError) as excinfo: + dates.parse_date('2005-02-30', format='yyyy-MM-dd') + assert 'does not represent a valid date' in str(excinfo.value) + assert isinstance(excinfo.value.__cause__, ValueError) + + +def test_parse_time_out_of_range(): + with pytest.raises(dates.ParseError) as excinfo: + dates.parse_time('25:00', format='HH:mm') + assert 'does not represent a valid time' in str(excinfo.value) + assert isinstance(excinfo.value.__cause__, ValueError) + + def test_datetime_format_get_week_number(): format = dates.DateTimeFormat(date(2006, 1, 8), Locale.parse('de_DE')) assert format.get_week_number(6) == 1