diff --git a/firebase-dataconnect/connectors/src/androidTest/kotlin/com/google/firebase/dataconnect/connectors/demo/DateScalarIntegrationTest.kt b/firebase-dataconnect/connectors/src/androidTest/kotlin/com/google/firebase/dataconnect/connectors/demo/DateScalarIntegrationTest.kt index dca905d755d..67eac700af0 100644 --- a/firebase-dataconnect/connectors/src/androidTest/kotlin/com/google/firebase/dataconnect/connectors/demo/DateScalarIntegrationTest.kt +++ b/firebase-dataconnect/connectors/src/androidTest/kotlin/com/google/firebase/dataconnect/connectors/demo/DateScalarIntegrationTest.kt @@ -20,15 +20,14 @@ package com.google.firebase.dataconnect.connectors.demo import com.google.firebase.dataconnect.DataConnectException import com.google.firebase.dataconnect.ExperimentalFirebaseDataConnect +import com.google.firebase.dataconnect.LocalDate import com.google.firebase.dataconnect.connectors.demo.testutil.DemoConnectorIntegrationTestBase import com.google.firebase.dataconnect.generated.GeneratedMutation import com.google.firebase.dataconnect.generated.GeneratedQuery -import com.google.firebase.dataconnect.testutil.dateFromYearMonthDayUTC import com.google.firebase.dataconnect.testutil.executeWithEmptyVariables import com.google.firebase.dataconnect.testutil.property.arbitrary.EdgeCases import com.google.firebase.dataconnect.testutil.property.arbitrary.dataConnect import com.google.firebase.dataconnect.testutil.property.arbitrary.dateTestData -import com.google.firebase.dataconnect.testutil.property.arbitrary.toJavaUtilDate import io.kotest.assertions.assertSoftly import io.kotest.assertions.throwables.shouldThrow import io.kotest.assertions.withClue @@ -47,7 +46,7 @@ class DateScalarIntegrationTest : DemoConnectorIntegrationTestBase() { @Test fun nonNullDate_insert_NormalCases() = runTest { checkAll(20, Arb.dataConnect.dateTestData()) { - val key = connector.insertNonNullDate.execute(it.toJavaUtilDate()).data.key + val key = connector.insertNonNullDate.execute(it.date).data.key assertNonNullDateByKeyEquals(key, it.string) } } @@ -56,7 +55,7 @@ class DateScalarIntegrationTest : DemoConnectorIntegrationTestBase() { fun nonNullDate_insert_EdgeCases() = runTest { assertSoftly { EdgeCases.dates.all().forEach { - val key = connector.insertNonNullDate.execute(it.toJavaUtilDate()).data.key + val key = connector.insertNonNullDate.execute(it.date).data.key assertNonNullDateByKeyEquals(key, it.string) } } @@ -70,14 +69,14 @@ class DateScalarIntegrationTest : DemoConnectorIntegrationTestBase() { // "2024-03-27" if it did the erroneous conversion to UTC before taking the YYYY-MM-DD. val date = "2024-03-26T19:48:00.144-07:00" val key = connector.insertNonNullDate.executeWithStringVariables(date).data.key - assertNonNullDateByKeyEquals(key, dateFromYearMonthDayUTC(2024, 3, 26)) + assertNonNullDateByKeyEquals(key, LocalDate(2024, 3, 26)) } @Test fun nonNullDate_insert_ShouldIgnoreTime() = runTest { val date = "2024-03-26T19:48:00.144Z" val key = connector.insertNonNullDate.executeWithStringVariables(date).data.key - assertNonNullDateByKeyEquals(key, dateFromYearMonthDayUTC(2024, 3, 26)) + assertNonNullDateByKeyEquals(key, LocalDate(2024, 3, 26)) } @Test @@ -92,9 +91,9 @@ class DateScalarIntegrationTest : DemoConnectorIntegrationTestBase() { queryResult.data shouldBe GetNonNullDatesWithDefaultsByKeyQuery.Data( GetNonNullDatesWithDefaultsByKeyQuery.Data.NonNullDatesWithDefaults( - valueWithVariableDefault = dateFromYearMonthDayUTC(6904, 11, 30), - valueWithSchemaDefault = dateFromYearMonthDayUTC(2112, 1, 31), - epoch = EdgeCases.dates.epoch.toJavaUtilDate(), + valueWithVariableDefault = LocalDate(6904, 11, 30), + valueWithSchemaDefault = LocalDate(2112, 1, 31), + epoch = EdgeCases.dates.epoch.date, requestTime1 = expectedRequestTime, requestTime2 = expectedRequestTime, ) @@ -134,8 +133,8 @@ class DateScalarIntegrationTest : DemoConnectorIntegrationTestBase() { @Test fun nonNullDate_update_NormalCases() = runTest { checkAll(20, Arb.dataConnect.dateTestData(), Arb.dataConnect.dateTestData()) { date1, date2 -> - val key = connector.insertNonNullDate.execute(date1.toJavaUtilDate()).data.key - connector.updateNonNullDate.execute(key) { value = date2.toJavaUtilDate() } + val key = connector.insertNonNullDate.execute(date1.date).data.key + connector.updateNonNullDate.execute(key) { value = date2.date } assertNonNullDateByKeyEquals(key, date2.string) } } @@ -151,8 +150,8 @@ class DateScalarIntegrationTest : DemoConnectorIntegrationTestBase() { assertSoftly { for ((date1, date2) in dates1.zip(dates2)) { withClue("date1=${date1.string} date2=${date2.string}") { - val key = connector.insertNonNullDate.execute(date1.toJavaUtilDate()).data.key - connector.updateNonNullDate.execute(key) { value = date2.toJavaUtilDate() } + val key = connector.insertNonNullDate.execute(date1.date).data.key + connector.updateNonNullDate.execute(key) { value = date2.date } assertNonNullDateByKeyEquals(key, date2.string) } } @@ -162,15 +161,15 @@ class DateScalarIntegrationTest : DemoConnectorIntegrationTestBase() { @Test fun nonNullDate_update_DateVariableOmitted() = runTest { val date = Arb.dataConnect.dateTestData().next(rs) - val key = connector.insertNonNullDate.execute(date.toJavaUtilDate()).data.key + val key = connector.insertNonNullDate.execute(date.date).data.key connector.updateNonNullDate.execute(key) {} - assertNonNullDateByKeyEquals(key, date.toJavaUtilDate()) + assertNonNullDateByKeyEquals(key, date.date) } @Test fun nullableDate_insert_NormalCases() = runTest { checkAll(20, Arb.dataConnect.dateTestData()) { - val key = connector.insertNullableDate.execute { value = it.toJavaUtilDate() }.data.key + val key = connector.insertNullableDate.execute { value = it.date }.data.key assertNullableDateByKeyEquals(key, it.string) } } @@ -180,7 +179,7 @@ class DateScalarIntegrationTest : DemoConnectorIntegrationTestBase() { val edgeCases = EdgeCases.dates.all() + listOf(null) assertSoftly { edgeCases.forEach { - val key = connector.insertNullableDate.execute { value = it?.toJavaUtilDate() }.data.key + val key = connector.insertNullableDate.execute { value = it?.date }.data.key if (it === null) { assertNullableDateByKeyHasNullInnerValue(key) } else { @@ -204,14 +203,14 @@ class DateScalarIntegrationTest : DemoConnectorIntegrationTestBase() { // "2024-03-27" if it did the erroneous conversion to UTC before taking the YYYY-MM-DD. val date = "2024-03-26T19:48:00.144-07:00" val key = connector.insertNullableDate.executeWithStringVariables(date).data.key - assertNullableDateByKeyEquals(key, dateFromYearMonthDayUTC(2024, 3, 26)) + assertNullableDateByKeyEquals(key, LocalDate(2024, 3, 26)) } @Test fun nullableDate_insert_ShouldIgnoreTime() = runTest { val date = "2024-03-26T19:48:00.144Z" val key = connector.insertNullableDate.executeWithStringVariables(date).data.key - assertNullableDateByKeyEquals(key, dateFromYearMonthDayUTC(2024, 3, 26)) + assertNullableDateByKeyEquals(key, LocalDate(2024, 3, 26)) } @Test @@ -242,9 +241,9 @@ class DateScalarIntegrationTest : DemoConnectorIntegrationTestBase() { queryResult.data shouldBe GetNullableDatesWithDefaultsByKeyQuery.Data( GetNullableDatesWithDefaultsByKeyQuery.Data.NullableDatesWithDefaults( - valueWithVariableDefault = dateFromYearMonthDayUTC(8113, 2, 9), - valueWithSchemaDefault = dateFromYearMonthDayUTC(1921, 12, 2), - epoch = EdgeCases.dates.epoch.toJavaUtilDate(), + valueWithVariableDefault = LocalDate(8113, 2, 9), + valueWithSchemaDefault = LocalDate(1921, 12, 2), + epoch = EdgeCases.dates.epoch.date, requestTime1 = expectedRequestTime, requestTime2 = expectedRequestTime, ) @@ -254,8 +253,8 @@ class DateScalarIntegrationTest : DemoConnectorIntegrationTestBase() { @Test fun nullableDate_update_NormalCases() = runTest { checkAll(20, Arb.dataConnect.dateTestData(), Arb.dataConnect.dateTestData()) { date1, date2 -> - val key = connector.insertNullableDate.execute { value = date1.toJavaUtilDate() }.data.key - connector.updateNullableDate.execute(key) { value = date2.toJavaUtilDate() } + val key = connector.insertNullableDate.execute { value = date1.date }.data.key + connector.updateNullableDate.execute(key) { value = date2.date } assertNullableDateByKeyEquals(key, date2.string) } } @@ -271,8 +270,8 @@ class DateScalarIntegrationTest : DemoConnectorIntegrationTestBase() { assertSoftly { for ((date1, date2) in dates1.zip(dates2)) { withClue("date1=${date1.string} date2=${date2.string}") { - val key = connector.insertNullableDate.execute { value = date1.toJavaUtilDate() }.data.key - connector.updateNullableDate.execute(key) { value = date2.toJavaUtilDate() } + val key = connector.insertNullableDate.execute { value = date1.date }.data.key + connector.updateNullableDate.execute(key) { value = date2.date } assertNullableDateByKeyEquals(key, date2.string) } } @@ -281,26 +280,26 @@ class DateScalarIntegrationTest : DemoConnectorIntegrationTestBase() { @Test fun nullableDate_update_UpdateNonNullValueToNull() = runTest { - val date = Arb.dataConnect.dateTestData().next(rs).toJavaUtilDate() - val key = connector.insertNullableDate.execute { value = date }.data.key + val date = Arb.dataConnect.dateTestData().next(rs) + val key = connector.insertNullableDate.execute { value = date.date }.data.key connector.updateNullableDate.execute(key) { value = null } assertNullableDateByKeyHasNullInnerValue(key) } @Test fun nullableDate_update_UpdateNullValueToNonNull() = runTest { - val date = Arb.dataConnect.dateTestData().next(rs).toJavaUtilDate() + val date = Arb.dataConnect.dateTestData().next(rs) val key = connector.insertNullableDate.execute { value = null }.data.key - connector.updateNullableDate.execute(key) { value = date } - assertNullableDateByKeyEquals(key, date) + connector.updateNullableDate.execute(key) { value = date.date } + assertNullableDateByKeyEquals(key, date.date) } @Test fun nullableDate_update_DateVariableOmitted() = runTest { - val date = Arb.dataConnect.dateTestData().next(rs).toJavaUtilDate() - val key = connector.insertNullableDate.execute { value = date }.data.key + val date = Arb.dataConnect.dateTestData().next(rs) + val key = connector.insertNullableDate.execute { value = date.date }.data.key connector.updateNullableDate.execute(key) {} - assertNullableDateByKeyEquals(key, date) + assertNullableDateByKeyEquals(key, date.date) } private suspend fun assertNonNullDateByKeyEquals(key: NonNullDateKey, expected: String) { @@ -311,7 +310,7 @@ class DateScalarIntegrationTest : DemoConnectorIntegrationTestBase() { queryResult.data shouldBe GetDateByKeyQueryStringData(expected) } - private suspend fun assertNonNullDateByKeyEquals(key: NonNullDateKey, expected: java.util.Date) { + private suspend fun assertNonNullDateByKeyEquals(key: NonNullDateKey, expected: LocalDate) { val queryResult = connector.getNonNullDateByKey.execute(key) queryResult.data shouldBe GetNonNullDateByKeyQuery.Data(GetNonNullDateByKeyQuery.Data.Value(expected)) @@ -334,10 +333,7 @@ class DateScalarIntegrationTest : DemoConnectorIntegrationTestBase() { queryResult.data shouldBe GetDateByKeyQueryStringData(expected) } - private suspend fun assertNullableDateByKeyEquals( - key: NullableDateKey, - expected: java.util.Date - ) { + private suspend fun assertNullableDateByKeyEquals(key: NullableDateKey, expected: LocalDate) { val queryResult = connector.getNullableDateByKey.execute(key) queryResult.data shouldBe GetNullableDateByKeyQuery.Data(GetNullableDateByKeyQuery.Data.Value(expected)) @@ -345,8 +341,8 @@ class DateScalarIntegrationTest : DemoConnectorIntegrationTestBase() { /** * A `Data` type that can be used in place of [GetNonNullDateByKeyQuery.Data] that types the value - * as a [String] instead of a [java.util.Date], allowing verification of the data sent over the - * wire without possible confounding from date deserialization. + * as a [String] instead of a [LocalDate], allowing verification of the data sent over the wire + * without possible confounding from date deserialization. */ @Serializable private data class GetDateByKeyQueryStringData(val value: DateStringValue?) { @@ -357,14 +353,14 @@ class DateScalarIntegrationTest : DemoConnectorIntegrationTestBase() { /** * A `Variables` type that can be used in place of [InsertNonNullDateMutation.Variables] that - * types the value as a [String] instead of a [java.util.Date], allowing verification of the data - * sent over the wire without possible confounding from date serialization. + * types the value as a [String] instead of a [LocalDate], allowing verification of the data sent + * over the wire without possible confounding from date serialization. */ @Serializable private data class InsertDateStringVariables(val value: String?) /** * A `Variables` type that can be used in place of [InsertNonNullDateMutation.Variables] that - * types the value as a [Int] instead of a [java.util.Date], allowing verification that the server + * types the value as a [Int] instead of a [LocalDate], allowing verification that the server * fails with an expected error (rather than crashing, for example). */ @Serializable private data class InsertDateIntVariables(val value: Int) diff --git a/firebase-dataconnect/connectors/src/androidTest/kotlin/com/google/firebase/dataconnect/connectors/demo/KeyVariablesIntegrationTest.kt b/firebase-dataconnect/connectors/src/androidTest/kotlin/com/google/firebase/dataconnect/connectors/demo/KeyVariablesIntegrationTest.kt index 0ec62809cf0..231be08834f 100644 --- a/firebase-dataconnect/connectors/src/androidTest/kotlin/com/google/firebase/dataconnect/connectors/demo/KeyVariablesIntegrationTest.kt +++ b/firebase-dataconnect/connectors/src/androidTest/kotlin/com/google/firebase/dataconnect/connectors/demo/KeyVariablesIntegrationTest.kt @@ -19,7 +19,6 @@ package com.google.firebase.dataconnect.connectors.demo import com.google.firebase.dataconnect.connectors.demo.testutil.DemoConnectorIntegrationTestBase import com.google.firebase.dataconnect.testutil.property.arbitrary.dataConnect import com.google.firebase.dataconnect.testutil.property.arbitrary.dateTestData -import com.google.firebase.dataconnect.testutil.property.arbitrary.toJavaUtilDate import com.google.firebase.dataconnect.testutil.randomTimestamp import com.google.firebase.dataconnect.testutil.withMicrosecondPrecision import io.kotest.matchers.shouldBe @@ -86,7 +85,7 @@ class KeyVariablesIntegrationTest : DemoConnectorIntegrationTestBase() { @Test fun primaryKeyIsDate() = runTest { - val id = Arb.dataConnect.dateTestData().next(rs).toJavaUtilDate() + val id = Arb.dataConnect.dateTestData().next(rs).date val value = Arb.dataConnect.string().next(rs) val key = connector.insertPrimaryKeyIsDate.execute(foo = id, value = value).data.key diff --git a/firebase-dataconnect/connectors/src/androidTest/kotlin/com/google/firebase/dataconnect/connectors/demo/ListVariablesAndDataIntegrationTest.kt b/firebase-dataconnect/connectors/src/androidTest/kotlin/com/google/firebase/dataconnect/connectors/demo/ListVariablesAndDataIntegrationTest.kt index 0125ae2cb15..98adca0a0e9 100644 --- a/firebase-dataconnect/connectors/src/androidTest/kotlin/com/google/firebase/dataconnect/connectors/demo/ListVariablesAndDataIntegrationTest.kt +++ b/firebase-dataconnect/connectors/src/androidTest/kotlin/com/google/firebase/dataconnect/connectors/demo/ListVariablesAndDataIntegrationTest.kt @@ -17,12 +17,12 @@ package com.google.firebase.dataconnect.connectors.demo import com.google.firebase.Timestamp +import com.google.firebase.dataconnect.LocalDate import com.google.firebase.dataconnect.connectors.demo.testutil.DemoConnectorIntegrationTestBase import com.google.firebase.dataconnect.testutil.property.arbitrary.DataConnectArb import com.google.firebase.dataconnect.testutil.property.arbitrary.EdgeCases import com.google.firebase.dataconnect.testutil.property.arbitrary.dataConnect -import com.google.firebase.dataconnect.testutil.property.arbitrary.dateTestData -import com.google.firebase.dataconnect.testutil.property.arbitrary.toJavaUtilDate +import com.google.firebase.dataconnect.testutil.property.arbitrary.localDate import com.google.firebase.dataconnect.testutil.withMicrosecondPrecision import io.kotest.common.ExperimentalKotest import io.kotest.matchers.shouldBe @@ -37,7 +37,6 @@ import io.kotest.property.arbitrary.long import io.kotest.property.arbitrary.map import io.kotest.property.arbitrary.uuid import io.kotest.property.checkAll -import java.util.Date import java.util.UUID import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.test.runTest @@ -475,7 +474,7 @@ class ListVariablesAndDataIntegrationTest : DemoConnectorIntegrationTestBase() { val booleans: List, val uuids: List, val int64s: List, - val dates: List, + val dates: List, val timestamps: List, ) { @@ -524,7 +523,7 @@ class ListVariablesAndDataIntegrationTest : DemoConnectorIntegrationTestBase() { booleans = EdgeCases.booleans, uuids = EdgeCases.uuids, int64s = EdgeCases.int64s, - dates = EdgeCases.dates.all().map { it.toJavaUtilDate() }, + dates = EdgeCases.dates.all().map { it.date }, timestamps = EdgeCases.javaTime.instants.all.map { it.timestamp }, ) } @@ -546,8 +545,7 @@ class ListVariablesAndDataIntegrationTest : DemoConnectorIntegrationTestBase() { booleans: Arb> = Arb.list(Arb.boolean(), 1..100), uuids: Arb> = Arb.list(Arb.uuid(), 1..100), int64s: Arb> = Arb.list(Arb.long(), 1..100), - dates: Arb> = - Arb.list(Arb.dataConnect.dateTestData().map { it.toJavaUtilDate() }, 1..100), + dates: Arb> = Arb.list(Arb.dataConnect.localDate(), 1..100), timestamps: Arb> = Arb.list(Arb.dataConnect.javaTime.instantTestCase().map { it.timestamp }, 1..100), ): Arb = arbitrary { diff --git a/firebase-dataconnect/gradleplugin/plugin/src/main/resources/com/google/firebase/dataconnect/gradle/plugin/DataConnectExecutableVersions.json b/firebase-dataconnect/gradleplugin/plugin/src/main/resources/com/google/firebase/dataconnect/gradle/plugin/DataConnectExecutableVersions.json index fc1c279feca..886c8eeacb1 100644 --- a/firebase-dataconnect/gradleplugin/plugin/src/main/resources/com/google/firebase/dataconnect/gradle/plugin/DataConnectExecutableVersions.json +++ b/firebase-dataconnect/gradleplugin/plugin/src/main/resources/com/google/firebase/dataconnect/gradle/plugin/DataConnectExecutableVersions.json @@ -1,5 +1,5 @@ { - "defaultVersion": "1.6.1", + "defaultVersion": "1.7.0", "versions": [ { "version": "1.3.4", @@ -270,6 +270,24 @@ "os": "linux", "size": 25223320, "sha512DigestHex": "5e8002a048b55a358d6d4a8c59c30ef8c3615461fd400bf4c8e86e84cc1b6482da604cb2635ec1639276c3b9c9f22a9c570f6729934e4fc77b09c8ccb6f3b986" + }, + { + "version": "1.7.0", + "os": "windows", + "size": 25783808, + "sha512DigestHex": "9fc0bf918ea2c20bc8dcf26efd101e7a567a13bf7b0967c16f35989e3557d0055edce6522b23fb70361f26f3ad1abd93458af5b33d3fa3019333ca72680353a2" + }, + { + "version": "1.7.0", + "os": "macos", + "size": 25350912, + "sha512DigestHex": "f887290a6083c3c88ee92f532c6fceb993a64714d5703ea7c389381222eab05998e8a25e0bee0770686433d5e7915fe6bfd58b3a0098d13ad0800c4e515fee0d" + }, + { + "version": "1.7.0", + "os": "linux", + "size": 25272472, + "sha512DigestHex": "795c3f63a3c78b94204ae8c525227f3295a02cd90e553f52bde543029a91f68da0d17653cc6b4c863ed778104fd2baa97a729f80ab4bd54dd5dd4f5e15354b7a" } ] } \ No newline at end of file diff --git a/firebase-dataconnect/testutil/src/main/kotlin/com/google/firebase/dataconnect/testutil/DateTimeTestUtils.kt b/firebase-dataconnect/testutil/src/main/kotlin/com/google/firebase/dataconnect/testutil/DateTimeTestUtils.kt index f3d3018d2dd..a0cfdcbcf3c 100644 --- a/firebase-dataconnect/testutil/src/main/kotlin/com/google/firebase/dataconnect/testutil/DateTimeTestUtils.kt +++ b/firebase-dataconnect/testutil/src/main/kotlin/com/google/firebase/dataconnect/testutil/DateTimeTestUtils.kt @@ -18,31 +18,10 @@ package com.google.firebase.dataconnect.testutil import com.google.firebase.Timestamp import java.util.Calendar -import java.util.Date import java.util.GregorianCalendar import java.util.TimeZone import kotlin.random.Random -/** - * Creates and returns a new [Date] object that represents the given year, month, and day in UTC. - * - * @param year The year; must be between 0 and 9999, inclusive. - * @param month The month; must be between 1 and 12, inclusive. - * @param day The day of the month; must be between 1 and 31, inclusive. - */ -fun dateFromYearMonthDayUTC(year: Int, month: Int, day: Int): Date { - require(year in 0..9999) { "year must be between 0 and 9999, inclusive" } - require(month in 1..12) { "month must be between 1 and 12, inclusive" } - require(day in 1..31) { "day must be between 1 and 31, inclusive" } - - return GregorianCalendar(TimeZone.getTimeZone("UTC")) - .apply { - set(year, month - 1, day, 0, 0, 0) - set(Calendar.MILLISECOND, 0) - } - .time -} - /** Generates and returns a random [Timestamp] object. */ fun randomTimestamp(): Timestamp { val nanoseconds = Random.nextInt(1_000_000_000) diff --git a/firebase-dataconnect/testutil/src/main/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary/dates.kt b/firebase-dataconnect/testutil/src/main/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary/dates.kt index 6959ccbd50b..6ed685f0476 100644 --- a/firebase-dataconnect/testutil/src/main/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary/dates.kt +++ b/firebase-dataconnect/testutil/src/main/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary/dates.kt @@ -20,7 +20,6 @@ package com.google.firebase.dataconnect.testutil.property.arbitrary import com.google.firebase.dataconnect.LocalDate import com.google.firebase.dataconnect.testutil.NullableReference -import com.google.firebase.dataconnect.testutil.dateFromYearMonthDayUTC import com.google.firebase.dataconnect.testutil.dayRangeInYear import com.google.firebase.dataconnect.testutil.property.arbitrary.DateEdgeCases.MAX_YEAR import com.google.firebase.dataconnect.testutil.property.arbitrary.DateEdgeCases.MIN_YEAR @@ -102,9 +101,6 @@ data class DateTestData( val string: String, ) -fun DateTestData.toJavaUtilDate(): java.util.Date = - dateFromYearMonthDayUTC(year = date.year, month = date.month, day = date.day) - @Suppress("MemberVisibilityCanBePrivate") object DateEdgeCases { // See https://en.wikipedia.org/wiki/ISO_8601#Years for rationale of lower bound of 1583.