diff --git a/core/common/src/format/DateTimeFormatBuilder.kt b/core/common/src/format/DateTimeFormatBuilder.kt index a235b7de..2230051d 100644 --- a/core/common/src/format/DateTimeFormatBuilder.kt +++ b/core/common/src/format/DateTimeFormatBuilder.kt @@ -130,6 +130,7 @@ public sealed interface DateTimeFormatBuilder { * [am] is used for the AM marker (0-11 hours), [pm] is used for the PM marker (12-23 hours). * * Empty strings can not be used as markers. + * [IllegalArgumentException] is thrown if either [am] or [pm] is empty or if they are equal. * * @see [amPmHour] */ diff --git a/core/common/src/format/LocalDateFormat.kt b/core/common/src/format/LocalDateFormat.kt index 165f9ac2..21484c70 100644 --- a/core/common/src/format/LocalDateFormat.kt +++ b/core/common/src/format/LocalDateFormat.kt @@ -13,7 +13,7 @@ import kotlinx.datetime.internal.format.parser.Copyable /** * A description of how month names are formatted. * - * An empty string can not be a month name. + * An [IllegalArgumentException] will be thrown if some month name is empty or there are duplicate names. */ public class MonthNames( /** @@ -23,7 +23,14 @@ public class MonthNames( ) { init { require(names.size == 12) { "Month names must contain exactly 12 elements" } - names.forEach { require(it.isNotEmpty()) { "A month name can not be empty" } } + names.indices.forEach { ix -> + require(names[ix].isNotEmpty()) { "A month name can not be empty" } + for (ix2 in 0 until ix) { + require(names[ix] != names[ix2]) { + "Month names must be unique, but '${names[ix]}' was repeated" + } + } + } } /** @@ -67,7 +74,7 @@ internal fun MonthNames.toKotlinCode(): String = when (this.names) { /** * A description of how day of week names are formatted. * - * An empty string can not be a day-of-week name. + * An [IllegalArgumentException] will be thrown if some day-of-week name is empty or there are duplicate names. */ public class DayOfWeekNames( /** @@ -77,7 +84,14 @@ public class DayOfWeekNames( ) { init { require(names.size == 7) { "Day of week names must contain exactly 7 elements" } - names.forEach { require(it.isNotEmpty()) { "A month name can not be empty" } } + names.indices.forEach { ix -> + require(names[ix].isNotEmpty()) { "A day-of-week name can not be empty" } + for (ix2 in 0 until ix) { + require(names[ix] != names[ix2]) { + "Day-of-week names must be unique, but '${names[ix]}' was repeated" + } + } + } } /** diff --git a/core/common/src/internal/format/parser/ParserOperation.kt b/core/common/src/internal/format/parser/ParserOperation.kt index 02ba4c76..6ac3a1c2 100644 --- a/core/common/src/internal/format/parser/ParserOperation.kt +++ b/core/common/src/internal/format/parser/ParserOperation.kt @@ -167,6 +167,7 @@ internal class StringSetParserOperation( node.children[searchResult].second } } + require(!node.isTerminal) { "The string '$string' was passed several times" } node.isTerminal = true } fun reduceTrie(trie: TrieNode) { diff --git a/core/common/test/format/LocalDateFormatTest.kt b/core/common/test/format/LocalDateFormatTest.kt index 88758b99..d1363253 100644 --- a/core/common/test/format/LocalDateFormatTest.kt +++ b/core/common/test/format/LocalDateFormatTest.kt @@ -221,6 +221,38 @@ class LocalDateFormatTest { assertEquals("2020 Jan 05", format.format(LocalDate(2020, 1, 5))) } + @Test + fun testEmptyMonthNames() { + val names = MonthNames.ENGLISH_FULL.names + for (i in 0 until 12) { + val newNames = (0 until 12).map { if (it == i) "" else names[it] } + assertFailsWith { MonthNames(newNames) } + } + } + + @Test + fun testEmptyDayOfWeekNames() { + val names = DayOfWeekNames.ENGLISH_FULL.names + for (i in 0 until 7) { + val newNames = (0 until 7).map { if (it == i) "" else names[it] } + assertFailsWith { DayOfWeekNames(newNames) } + } + } + + @Test + fun testIdenticalMonthNames() { + assertFailsWith { + MonthNames("Jan", "Jan", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec") + } + } + + @Test + fun testIdenticalDayOfWeekNames() { + assertFailsWith { + DayOfWeekNames("Mon", "Tue", "Tue", "Thu", "Fri", "Sat", "Sun") + } + } + private fun test(strings: Map>>, format: DateTimeFormat) { for ((date, stringsForDate) in strings) { val (canonicalString, otherStrings) = stringsForDate diff --git a/core/common/test/format/LocalTimeFormatTest.kt b/core/common/test/format/LocalTimeFormatTest.kt index 595bcc52..80934fc3 100644 --- a/core/common/test/format/LocalTimeFormatTest.kt +++ b/core/common/test/format/LocalTimeFormatTest.kt @@ -216,6 +216,24 @@ class LocalTimeFormatTest { } } + @Test + fun testEmptyAmPmMarkers() { + assertFailsWith { + LocalTime.Format { + amPmMarker("", "pm") + } + } + } + + @Test + fun testIdenticalAmPmMarkers() { + assertFailsWith { + LocalTime.Format { + amPmMarker("pm", "pm") + } + } + } + private fun test(strings: Map>>, format: DateTimeFormat) { for ((date, stringsForDate) in strings) { val (canonicalString, otherStrings) = stringsForDate