From db88e26ed3cff6256515f55b333cd07b2ca8c07e Mon Sep 17 00:00:00 2001 From: Alexander Baranov Date: Mon, 26 Oct 2026 12:30:37 +0300 Subject: [PATCH 1/2] fix: reject ambiguous abbreviated slash years --- .../io/github/alox1d/dateparser/internal/EnglishGrammar.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dateparser/src/commonMain/kotlin/io/github/alox1d/dateparser/internal/EnglishGrammar.kt b/dateparser/src/commonMain/kotlin/io/github/alox1d/dateparser/internal/EnglishGrammar.kt index 59b0d38..3dc140c 100644 --- a/dateparser/src/commonMain/kotlin/io/github/alox1d/dateparser/internal/EnglishGrammar.kt +++ b/dateparser/src/commonMain/kotlin/io/github/alox1d/dateparser/internal/EnglishGrammar.kt @@ -127,7 +127,12 @@ internal object EnglishGrammar { first == second && first in 1..12 -> DateOrder.DMY else -> null } - when (order) { + // An abbreviated year such as 26 must not silently become AD 26. + val explicitYear = if (order == DateOrder.YMD) m.groupValues[1] else third + if (explicitYear.isNotEmpty() && explicitYear.length != 4) { + rejected = true + REJECTED + } else when (order) { DateOrder.DMY -> date(m.groupValues[1], second, third) DateOrder.MDY -> date(m.groupValues[2], first, third) DateOrder.YMD -> if (third.isEmpty()) { rejected = true; REJECTED } else date(third, second, m.groupValues[1]) From 52c98e7b7f3625a028d01b3538d26d5821f04c65 Mon Sep 17 00:00:00 2001 From: Alexander Baranov Date: Mon, 26 Oct 2026 12:31:07 +0300 Subject: [PATCH 2/2] test: cover short slash years and full-year inputs --- .../github/alox1d/dateparser/EnglishGrammarTest.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/dateparser/src/commonTest/kotlin/io/github/alox1d/dateparser/EnglishGrammarTest.kt b/dateparser/src/commonTest/kotlin/io/github/alox1d/dateparser/EnglishGrammarTest.kt index ca83164..b71c3e5 100644 --- a/dateparser/src/commonTest/kotlin/io/github/alox1d/dateparser/EnglishGrammarTest.kt +++ b/dateparser/src/commonTest/kotlin/io/github/alox1d/dateparser/EnglishGrammarTest.kt @@ -39,6 +39,18 @@ class EnglishGrammarTest { assertTrue(parse("03/04").isEmpty()) assertEquals(LocalDate(2026,4,3), parse("03.04.2026",DateOrder.MDY).single().targetDate.date) } + @Test fun shortSlashYearsCannotSilentlyBecomeAncientDates() { + val parser = DateParser() + for (text in listOf("13/04/26", "04/13/26")) { + assertTrue(parser.parse(text, reference, TimeZone.UTC).isEmpty(), text) + } + assertTrue(parser.parse("13/04/26", DateOrder.DMY, reference, TimeZone.UTC).isEmpty()) + assertTrue(parser.parse("04/13/26", DateOrder.MDY, reference, TimeZone.UTC).isEmpty()) + assertTrue(parser.parse("26/04/13", DateOrder.YMD, reference, TimeZone.UTC).isEmpty()) + assertEquals(LocalDate(2026, 4, 13), parser.parse("13/04/2026", reference, TimeZone.UTC).single().targetDate.date) + assertEquals(LocalDate(2026, 4, 13), parser.parse("2026/04/13", DateOrder.YMD, reference, TimeZone.UTC).single().targetDate.date) + assertEquals(LocalDate(2024, 4, 13), parser.parse("13/04", reference, TimeZone.UTC).single().targetDate.date) + } @Test fun invalidDateAndClockTokensNeverBecomePartialMatches() { for (text in listOf("February 30, 2026", "29 February 2025", "31 April", "February 29, 0000", "13pm", "0AM", "5:60PM", "12:10:60am", "2026-02-30", "31/04/2026", "03/04/2026 at 5pm", "tomorrow at 13pm", "tomorrow at 1:2:3:4pm", "March 12, 10000", "tomorrow at 999999999999999999999pm", "01/02/03/04", "2026-02-29")) { assertTrue(parse(text).isEmpty(), text)