diff --git a/babel/messages/catalog.py b/babel/messages/catalog.py index 5e6c28255..040cc95a4 100644 --- a/babel/messages/catalog.py +++ b/babel/messages/catalog.py @@ -103,7 +103,12 @@ def _has_python_brace_format(string: str) -> bool: return field_name_seen -def _parse_datetime_header(value: str) -> datetime.datetime: +def _parse_datetime_header(value: str) -> datetime.datetime | None: + # Some tools (e.g. Poedit) leave the header's value blank instead of + # eliding the header, or leaving the "YEAR-MO-DA HO:MI+ZONE" placeholder. + if not value.strip(): + return None + match = re.match(r'^(?P.*?)(?P[+-]\d{4})?$', value) dt = datetime.datetime.strptime(match.group('datetime'), '%Y-%m-%d %H:%M') diff --git a/tests/messages/test_catalog.py b/tests/messages/test_catalog.py index 7c730d325..a13bb58b8 100644 --- a/tests/messages/test_catalog.py +++ b/tests/messages/test_catalog.py @@ -15,6 +15,8 @@ import pickle from io import StringIO +import pytest + from babel.dates import UTC, format_datetime from babel.messages import catalog, pofile from babel.util import FixedOffsetTimezone @@ -556,6 +558,14 @@ def test_datetime_parsing(): assert val2.tzinfo is None +@pytest.mark.parametrize('value', ['', ' ']) +def test_datetime_parsing_blank_value_returns_none(value): + # Some tools (e.g. Poedit) leave the header blank instead of eliding + # it or using the "YEAR-MO-DA HO:MI+ZONE" placeholder; this used to + # raise ValueError from strptime instead of being treated as unset. + assert catalog._parse_datetime_header(value) is None + + def test_update_catalog_comments(): # Based on https://web.archive.org/web/20100710131029/http://babel.edgewall.org/attachment/ticket/163/cat-update-comments.py