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]) 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)