Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion babel/dates.py
Original file line number Diff line number Diff line change
Expand Up @@ -1366,6 +1366,21 @@ class ParseError(ValueError):
pass


def _expand_two_digit_year(year: int) -> int:
"""Place a two-digit year within 80 years before and 20 years after today.

This is the rule ICU documents for parsing abbreviated years, see
https://unicode-org.github.io/icu-docs/apidoc/released/icu4j/com/ibm/icu/text/SimpleDateFormat.html
"""
this_year = datetime.date.today().year
expanded = this_year - this_year % 100 + year
if expanded > this_year + 20:
return expanded - 100
if expanded <= this_year - 80:
return expanded + 100
return expanded


def parse_date(
string: str,
locale: Locale | str | None = None,
Expand Down Expand Up @@ -1395,6 +1410,9 @@ def parse_date(
>>> parse_date('01.04.04', locale='de_DE', format='short')
datetime.date(2004, 4, 1)

Two-digit years are placed within 80 years before and 20 years after
the current year.

:param string: the string containing the date
:param locale: a `Locale` object or a locale identifier
:param locale: a `Locale` object or a locale identifier. Defaults to the system time locale.
Expand Down Expand Up @@ -1438,7 +1456,7 @@ def parse_date(
# names, both in the requested locale, and english

year = numbers[indexes['Y']]
year = 2000 + int(year) if len(year) == 2 else int(year)
year = _expand_two_digit_year(int(year)) if len(year) == 2 else int(year)
month = int(numbers[indexes['M']])
day = int(numbers[indexes['D']])
if month > 12:
Expand Down
13 changes: 13 additions & 0 deletions tests/test_dates.py
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,19 @@ def test_parse_date():
assert dates.parse_date('2004-04-01', locale='sv_SE', format='short') == date(2004, 4, 1)


@pytest.mark.parametrize(('today', 'yy', 'expected'), [
('2026-11-09', '99', 1999),
('2026-11-09', '46', 2046),
('2026-11-09', '47', 1947),
('2090-06-01', '05', 2105),
('2090-06-01', '10', 2110),
('2090-06-01', '11', 2011),
])
def test_parse_date_two_digit_year(today, yy, expected):
with freezegun.freeze_time(today):
assert dates.parse_date(f'1/1/{yy}', locale='en_US').year == expected


def test_parse_date_custom_format():
assert dates.parse_date('1.4.2024', format='dd.mm.yyyy') == date(2024, 4, 1)
assert dates.parse_date('2024.4.1', format='yyyy.mm.dd') == date(2024, 4, 1)
Expand Down
Loading