From 1b4c6aec6d4d16057e510d9b350714248028fe02 Mon Sep 17 00:00:00 2001 From: Adrian Chaves Date: Wed, 23 Sep 2026 12:56:13 +0200 Subject: [PATCH 1/2] Parse "the Nth of the month" as a day of the current month --- dateparser/data/date_translation_data/en.py | 3 +++ dateparser/freshness_date_parser.py | 13 +++++++++++++ .../date_translation_data/en.yaml | 1 + tests/test_freshness_date_parser.py | 2 ++ 4 files changed, 19 insertions(+) diff --git a/dateparser/data/date_translation_data/en.py b/dateparser/data/date_translation_data/en.py index 13c72da42..922a80a93 100644 --- a/dateparser/data/date_translation_data/en.py +++ b/dateparser/data/date_translation_data/en.py @@ -1162,6 +1162,9 @@ { "from\\s+now": "in" }, + { + "of the month": "of this month" + }, { "less than 1 minute ago": "45 second ago" }, diff --git a/dateparser/freshness_date_parser.py b/dateparser/freshness_date_parser.py index a96136baf..f6a40e6d8 100644 --- a/dateparser/freshness_date_parser.py +++ b/dateparser/freshness_date_parser.py @@ -11,6 +11,8 @@ _UNITS = r"decade|year|month|week|day|hour|minute|second" PATTERN = re.compile(r"([+-]?\s*\d++[.,]?\d*+)\s*(%s)\b" % _UNITS, re.I | re.S | re.U) +# "the 1st of the month" translates to " 1 0 month ago". +_DAY_OF_THIS_MONTH = re.compile(r"^\s*(\d{1,2})\s+(?=0\s+month\s+ago\b)") class FreshnessDateDataParser: @@ -40,6 +42,10 @@ def get_local_tz(self): def parse(self, date_string, settings): date_string = strip_braces(date_string) date_string, ptz = pop_tz_offset_from_string(date_string) + day = None + if match := _DAY_OF_THIS_MONTH.match(date_string): + day = int(match[1]) + date_string = date_string[match.end() :] _time = self._parse_time(date_string, settings) _settings_tz = settings.TIMEZONE.lower() @@ -90,6 +96,13 @@ def apply_time(dateobj, timeobj): date, period = self._parse_date(date_string, now, settings.PREFER_DATES_FROM) + if date and day is not None: + try: + date = date.replace(day=day) + except ValueError: + return None, None + period = "day" + if date: old_date = date date = apply_time(date, _time) diff --git a/dateparser_data/supplementary_language_data/date_translation_data/en.yaml b/dateparser_data/supplementary_language_data/date_translation_data/en.yaml index 743d5e04b..cd1cf50ab 100644 --- a/dateparser_data/supplementary_language_data/date_translation_data/en.yaml +++ b/dateparser_data/supplementary_language_data/date_translation_data/en.yaml @@ -92,6 +92,7 @@ simplifications: - (?:12\s+)?midnight: '00:00' - (\d+[.,]?\d*)h(\d+[.,]?\d*): \1:\2 - from\s+now: in + - of the month: of this month - less than 1 minute ago: 45 second ago - (\d+[.,]?\d*|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve) (decade|year|month|week|day|hour|minute|second)s? later: in \1 \2 - one: '1' diff --git a/tests/test_freshness_date_parser.py b/tests/test_freshness_date_parser.py index ef1477e9c..3004af71a 100644 --- a/tests/test_freshness_date_parser.py +++ b/tests/test_freshness_date_parser.py @@ -2354,6 +2354,7 @@ def test_relative_future_dates(self, date_string, in_future, period): [ param("15th of Aug, 2014 Diane Bennett"), param("4 heures ago"), + param("the 31st of the month"), ] ) def test_insane_dates(self, date_string): @@ -2589,6 +2590,7 @@ def test_freshness_date_with_timezone_utc_offset( param("in 10.75 minutes", date(2010, 6, 4), time(13, 25, 45)), param("in 1.5 days", date(2010, 6, 6), time(1, 15)), param("0,5 hours ago", date(2010, 6, 4), time(12, 45)), + param("the 1st of the month", date(2010, 6, 1), time(13, 15)), ] ) def test_freshness_date_with_relative_base(self, date_string, date, time): From 79a95d3152f06fb4ddd37595bfbc693235d16032 Mon Sep 17 00:00:00 2001 From: Adrian Chaves Date: Wed, 30 Sep 2026 13:53:45 +0200 Subject: [PATCH 2/2] Only simplify "of the month" after a day number --- dateparser/data/date_translation_data/en.py | 2 +- .../date_translation_data/en.yaml | 2 +- tests/test_search.py | 12 ++++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/dateparser/data/date_translation_data/en.py b/dateparser/data/date_translation_data/en.py index b0d9593a3..8657796fa 100644 --- a/dateparser/data/date_translation_data/en.py +++ b/dateparser/data/date_translation_data/en.py @@ -1163,7 +1163,7 @@ "from\\s+now": "in" }, { - "of the month": "of this month" + "(\\d+(?:st|nd|rd|th)?) of the month": "\\1 of this month" }, { "less than 1 minute ago": "45 second ago" diff --git a/dateparser_data/supplementary_language_data/date_translation_data/en.yaml b/dateparser_data/supplementary_language_data/date_translation_data/en.yaml index 923a5e246..a7a6584ed 100644 --- a/dateparser_data/supplementary_language_data/date_translation_data/en.yaml +++ b/dateparser_data/supplementary_language_data/date_translation_data/en.yaml @@ -92,7 +92,7 @@ simplifications: - (?:12\s+)?midnight: '00:00' - (\d+[.,]?\d*)h(\d+[.,]?\d*): \1:\2 - from\s+now: in - - of the month: of this month + - (\d+(?:st|nd|rd|th)?) of the month: \1 of this month - less than 1 minute ago: 45 second ago - (\d+(?:st|nd|rd|th)) day: \1 - (\d+[.,]?\d*|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve) (decade|year|month|week|day|hour|minute|second)s? later: in \1 \2 diff --git a/tests/test_search.py b/tests/test_search.py index 9d6a4728f..21d0962ed 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -1071,6 +1071,18 @@ def test_detection(self, shortname: str, text: str) -> None: ], ), param(text="a Americ", languages=None, settings=None, expected=None), + param( + text="Photo of the month, 12 March 1999", + languages=["en"], + settings=None, + expected=[("12 March 1999", datetime.datetime(1999, 3, 12, 0, 0))], + ), + param( + text="Employee of the month", + languages=["en"], + settings=None, + expected=None, + ), # Date with comma and apostrophe param( text="9/3/2017 , ",