From 4177ff208de608f92f973b49b37e457e65709ba2 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Thu, 8 Jul 2021 11:45:41 +0300 Subject: [PATCH] Remove extra hierarchy level in DateTimeUnit sealed class inheritors --- core/common/src/DateTimeUnit.kt | 77 ++++++++++--------- .../serializers/DateTimeUnitSerializers.kt | 20 ++--- core/js/src/Instant.kt | 12 +-- core/js/src/LocalDate.kt | 8 +- core/jvm/src/Instant.kt | 8 +- core/jvm/src/LocalDate.kt | 8 +- core/native/src/LocalDate.kt | 8 +- core/native/src/LocalDateTime.kt | 4 +- .../test/DateTimeUnitSerializationTest.kt | 16 ++-- 9 files changed, 84 insertions(+), 77 deletions(-) diff --git a/core/common/src/DateTimeUnit.kt b/core/common/src/DateTimeUnit.kt index 8cf0945f..576d2657 100644 --- a/core/common/src/DateTimeUnit.kt +++ b/core/common/src/DateTimeUnit.kt @@ -66,44 +66,51 @@ public sealed class DateTimeUnit { @Serializable(with = DateBasedDateTimeUnitSerializer::class) public sealed class DateBased : DateTimeUnit() { - // TODO: investigate how to move subclasses up to DateTimeUnit scope - @Serializable(with = DayBasedDateTimeUnitSerializer::class) - public class DayBased(public val days: Int) : DateBased() { - init { - require(days > 0) { "Unit duration must be positive, but was $days days." } - } + @Suppress("TOPLEVEL_TYPEALIASES_ONLY") + @Deprecated("Use DateTimeUnit.DayBased", ReplaceWith("DateTimeUnit.DayBased", "kotlinx.datetime.DateTimeUnit")) + public typealias DayBased = DateTimeUnit.DayBased + @Suppress("TOPLEVEL_TYPEALIASES_ONLY") + @Deprecated("Use DateTimeUnit.MonthBased", ReplaceWith("DateTimeUnit.MonthBased", "kotlinx.datetime.DateTimeUnit")) + public typealias MonthBased = DateTimeUnit.MonthBased + } + + @Serializable(with = DayBasedDateTimeUnitSerializer::class) + public class DayBased(public val days: Int) : DateBased() { + init { + require(days > 0) { "Unit duration must be positive, but was $days days." } + } + + override fun times(scalar: Int): DateTimeUnit.DayBased = DateTimeUnit.DayBased(safeMultiply(days, scalar)) - override fun times(scalar: Int): DayBased = DayBased(safeMultiply(days, scalar)) + override fun equals(other: Any?): Boolean = + this === other || (other is DateTimeUnit.DayBased && this.days == other.days) - override fun equals(other: Any?): Boolean = - this === other || (other is DayBased && this.days == other.days) + override fun hashCode(): Int = days xor 0x10000 - override fun hashCode(): Int = days xor 0x10000 + override fun toString(): String = if (days % 7 == 0) + formatToString(days / 7, "WEEK") + else + formatToString(days, "DAY") + } - override fun toString(): String = if (days % 7 == 0) - formatToString(days / 7, "WEEK") - else - formatToString(days, "DAY") + @Serializable(with = MonthBasedDateTimeUnitSerializer::class) + public class MonthBased(public val months: Int) : DateBased() { + init { + require(months > 0) { "Unit duration must be positive, but was $months months." } } - @Serializable(with = MonthBasedDateTimeUnitSerializer::class) - public class MonthBased(public val months: Int) : DateBased() { - init { - require(months > 0) { "Unit duration must be positive, but was $months months." } - } - override fun times(scalar: Int): MonthBased = MonthBased(safeMultiply(months, scalar)) + override fun times(scalar: Int): DateTimeUnit.MonthBased = DateTimeUnit.MonthBased(safeMultiply(months, scalar)) - override fun equals(other: Any?): Boolean = - this === other || (other is MonthBased && this.months == other.months) + override fun equals(other: Any?): Boolean = + this === other || (other is DateTimeUnit.MonthBased && this.months == other.months) - override fun hashCode(): Int = months xor 0x20000 + override fun hashCode(): Int = months xor 0x20000 - override fun toString(): String = when { - months % 12_00 == 0 -> formatToString(months / 12_00, "CENTURY") - months % 12 == 0 -> formatToString(months / 12, "YEAR") - months % 3 == 0 -> formatToString(months / 3, "QUARTER") - else -> formatToString(months, "MONTH") - } + override fun toString(): String = when { + months % 12_00 == 0 -> formatToString(months / 12_00, "CENTURY") + months % 12 == 0 -> formatToString(months / 12, "YEAR") + months % 3 == 0 -> formatToString(months / 3, "QUARTER") + else -> formatToString(months, "MONTH") } } @@ -117,11 +124,11 @@ public sealed class DateTimeUnit { public val SECOND: TimeBased = MILLISECOND * 1000 public val MINUTE: TimeBased = SECOND * 60 public val HOUR: TimeBased = MINUTE * 60 - public val DAY: DateBased.DayBased = DateBased.DayBased(days = 1) - public val WEEK: DateBased.DayBased = DAY * 7 - public val MONTH: DateBased.MonthBased = DateBased.MonthBased(months = 1) - public val QUARTER: DateBased.MonthBased = MONTH * 3 - public val YEAR: DateBased.MonthBased = MONTH * 12 - public val CENTURY: DateBased.MonthBased = YEAR * 100 + public val DAY: DayBased = DayBased(days = 1) + public val WEEK: DayBased = DAY * 7 + public val MONTH: MonthBased = MonthBased(months = 1) + public val QUARTER: MonthBased = MONTH * 3 + public val YEAR: MonthBased = MONTH * 12 + public val CENTURY: MonthBased = YEAR * 100 } } diff --git a/core/common/src/serializers/DateTimeUnitSerializers.kt b/core/common/src/serializers/DateTimeUnitSerializers.kt index 3eec77f9..eadb1d72 100644 --- a/core/common/src/serializers/DateTimeUnitSerializers.kt +++ b/core/common/src/serializers/DateTimeUnitSerializers.kt @@ -53,13 +53,13 @@ public object TimeBasedDateTimeUnitSerializer: KSerializer { +public object DayBasedDateTimeUnitSerializer: KSerializer { override val descriptor: SerialDescriptor = buildClassSerialDescriptor("DayBased") { element("days") } - override fun serialize(encoder: Encoder, value: DateTimeUnit.DateBased.DayBased) { + override fun serialize(encoder: Encoder, value: DateTimeUnit.DayBased) { encoder.encodeStructure(descriptor) { encodeIntElement(descriptor, 0, value.days) } @@ -67,7 +67,7 @@ public object DayBasedDateTimeUnitSerializer: KSerializer { +public object MonthBasedDateTimeUnitSerializer: KSerializer { override val descriptor: SerialDescriptor = buildClassSerialDescriptor("MonthBased") { element("months") } - override fun serialize(encoder: Encoder, value: DateTimeUnit.DateBased.MonthBased) { + override fun serialize(encoder: Encoder, value: DateTimeUnit.MonthBased) { encoder.encodeStructure(descriptor) { encodeIntElement(descriptor, 0, value.months) } @@ -106,7 +106,7 @@ public object MonthBasedDateTimeUnitSerializer: KSerializer { plus(value, unit).value.checkZone(timeZone) } - is DateTimeUnit.DateBased.DayBased -> + is DateTimeUnit.DayBased -> thisZdt.plusDays(value.toDouble() * unit.days).toInstant() - is DateTimeUnit.DateBased.MonthBased -> + is DateTimeUnit.MonthBased -> thisZdt.plusMonths(value.toDouble() * unit.months).toInstant() }.let(::Instant) } catch (e: Throwable) { @@ -161,9 +161,9 @@ public actual fun Instant.plus(value: Int, unit: DateTimeUnit, timeZone: TimeZon when (unit) { is DateTimeUnit.TimeBased -> plus(value.toLong(), unit).value.checkZone(timeZone) - is DateTimeUnit.DateBased.DayBased -> + is DateTimeUnit.DayBased -> thisZdt.plusDays(value.toDouble() * unit.days).toInstant() - is DateTimeUnit.DateBased.MonthBased -> + is DateTimeUnit.MonthBased -> thisZdt.plusMonths(value.toDouble() * unit.months).toInstant() }.let(::Instant) } catch (e: Throwable) { @@ -208,8 +208,8 @@ public actual fun Instant.until(other: Instant, unit: DateTimeUnit, timeZone: Ti val otherZdt = other.atZone(timeZone) when(unit) { is DateTimeUnit.TimeBased -> until(other, unit) - is DateTimeUnit.DateBased.DayBased -> (thisZdt.until(otherZdt, ChronoUnit.DAYS).toDouble() / unit.days).toLong() - is DateTimeUnit.DateBased.MonthBased -> (thisZdt.until(otherZdt, ChronoUnit.MONTHS).toDouble() / unit.months).toLong() + is DateTimeUnit.DayBased -> (thisZdt.until(otherZdt, ChronoUnit.DAYS).toDouble() / unit.days).toLong() + is DateTimeUnit.MonthBased -> (thisZdt.until(otherZdt, ChronoUnit.MONTHS).toDouble() / unit.months).toLong() } } catch (e: ArithmeticException) { if (this < other) Long.MAX_VALUE else Long.MIN_VALUE diff --git a/core/js/src/LocalDate.kt b/core/js/src/LocalDate.kt index d27b9fdb..1ef98ce7 100644 --- a/core/js/src/LocalDate.kt +++ b/core/js/src/LocalDate.kt @@ -59,8 +59,8 @@ public actual fun LocalDate.plus(value: Long, unit: DateTimeUnit.DateBased): Loc private fun LocalDate.plusNumber(value: Number, unit: DateTimeUnit.DateBased): LocalDate = try { when (unit) { - is DateTimeUnit.DateBased.DayBased -> this.value.plusDays(value.toDouble() * unit.days) - is DateTimeUnit.DateBased.MonthBased -> this.value.plusMonths(value.toDouble() * unit.months) + is DateTimeUnit.DayBased -> this.value.plusDays(value.toDouble() * unit.days) + is DateTimeUnit.MonthBased -> this.value.plusMonths(value.toDouble() * unit.months) }.let(::LocalDate) } catch (e: Throwable) { if (!e.isJodaDateTimeException() && !e.isJodaArithmeticException()) throw e @@ -92,8 +92,8 @@ public actual fun LocalDate.periodUntil(other: LocalDate): DatePeriod { } public actual fun LocalDate.until(other: LocalDate, unit: DateTimeUnit.DateBased): Int = when(unit) { - is DateTimeUnit.DateBased.MonthBased -> monthsUntil(other) / unit.months - is DateTimeUnit.DateBased.DayBased -> daysUntil(other) / unit.days + is DateTimeUnit.MonthBased -> monthsUntil(other) / unit.months + is DateTimeUnit.DayBased -> daysUntil(other) / unit.days } public actual fun LocalDate.daysUntil(other: LocalDate): Int = diff --git a/core/jvm/src/Instant.kt b/core/jvm/src/Instant.kt index fb50c2c7..ee36a6dc 100644 --- a/core/jvm/src/Instant.kt +++ b/core/jvm/src/Instant.kt @@ -133,9 +133,9 @@ public actual fun Instant.plus(value: Long, unit: DateTimeUnit, timeZone: TimeZo when (unit) { is DateTimeUnit.TimeBased -> plus(value, unit).value.also { it.atZone(timeZone.zoneId) } - is DateTimeUnit.DateBased.DayBased -> + is DateTimeUnit.DayBased -> thisZdt.plusDays(safeMultiply(value, unit.days.toLong())).toInstant() - is DateTimeUnit.DateBased.MonthBased -> + is DateTimeUnit.MonthBased -> thisZdt.plusMonths(safeMultiply(value, unit.months.toLong())).toInstant() }.let(::Instant) } catch (e: Exception) { @@ -173,8 +173,8 @@ public actual fun Instant.until(other: Instant, unit: DateTimeUnit, timeZone: Ti val otherZdt = other.atZone(timeZone) when(unit) { is DateTimeUnit.TimeBased -> until(other, unit) - is DateTimeUnit.DateBased.DayBased -> thisZdt.until(otherZdt, ChronoUnit.DAYS) / unit.days - is DateTimeUnit.DateBased.MonthBased -> thisZdt.until(otherZdt, ChronoUnit.MONTHS) / unit.months + is DateTimeUnit.DayBased -> thisZdt.until(otherZdt, ChronoUnit.DAYS) / unit.days + is DateTimeUnit.MonthBased -> thisZdt.until(otherZdt, ChronoUnit.MONTHS) / unit.months } } catch (e: DateTimeException) { throw DateTimeArithmeticException(e) diff --git a/core/jvm/src/LocalDate.kt b/core/jvm/src/LocalDate.kt index 6ea5b800..f7f6603b 100644 --- a/core/jvm/src/LocalDate.kt +++ b/core/jvm/src/LocalDate.kt @@ -63,11 +63,11 @@ public actual fun LocalDate.minus(value: Int, unit: DateTimeUnit.DateBased): Loc public actual fun LocalDate.plus(value: Long, unit: DateTimeUnit.DateBased): LocalDate = try { when (unit) { - is DateTimeUnit.DateBased.DayBased -> { + is DateTimeUnit.DayBased -> { val addDays: Long = safeMultiply(value, unit.days.toLong()) ofEpochDayChecked(safeAdd(this.value.toEpochDay(), addDays)) } - is DateTimeUnit.DateBased.MonthBased -> + is DateTimeUnit.MonthBased -> this.value.plusMonths(safeMultiply(value, unit.months.toLong())) }.let(::LocalDate) } catch (e: Exception) { @@ -109,8 +109,8 @@ public actual fun LocalDate.periodUntil(other: LocalDate): DatePeriod { } public actual fun LocalDate.until(other: LocalDate, unit: DateTimeUnit.DateBased): Int = when(unit) { - is DateTimeUnit.DateBased.MonthBased -> (this.value.until(other.value, ChronoUnit.MONTHS) / unit.months).clampToInt() - is DateTimeUnit.DateBased.DayBased -> (this.value.until(other.value, ChronoUnit.DAYS) / unit.days).clampToInt() + is DateTimeUnit.MonthBased -> (this.value.until(other.value, ChronoUnit.MONTHS) / unit.months).clampToInt() + is DateTimeUnit.DayBased -> (this.value.until(other.value, ChronoUnit.DAYS) / unit.days).clampToInt() } public actual fun LocalDate.daysUntil(other: LocalDate): Int = diff --git a/core/native/src/LocalDate.kt b/core/native/src/LocalDate.kt index 330dc6e1..d814dc9f 100644 --- a/core/native/src/LocalDate.kt +++ b/core/native/src/LocalDate.kt @@ -256,8 +256,8 @@ public actual fun LocalDate.plus(unit: DateTimeUnit.DateBased): LocalDate = public actual fun LocalDate.plus(value: Int, unit: DateTimeUnit.DateBased): LocalDate = try { when (unit) { - is DateTimeUnit.DateBased.DayBased -> plusDays(safeMultiply(value, unit.days)) - is DateTimeUnit.DateBased.MonthBased -> plusMonths(safeMultiply(value, unit.months)) + is DateTimeUnit.DayBased -> plusDays(safeMultiply(value, unit.days)) + is DateTimeUnit.MonthBased -> plusMonths(safeMultiply(value, unit.months)) } } catch (e: ArithmeticException) { throw DateTimeArithmeticException("Arithmetic overflow when adding a value to a date", e) @@ -286,8 +286,8 @@ public actual operator fun LocalDate.plus(period: DatePeriod): LocalDate = } public actual fun LocalDate.until(other: LocalDate, unit: DateTimeUnit.DateBased): Int = when(unit) { - is DateTimeUnit.DateBased.MonthBased -> monthsUntil(other) / unit.months - is DateTimeUnit.DateBased.DayBased -> daysUntil(other) / unit.days + is DateTimeUnit.MonthBased -> monthsUntil(other) / unit.months + is DateTimeUnit.DayBased -> daysUntil(other) / unit.days } // org.threeten.bp.LocalDate#daysUntil diff --git a/core/native/src/LocalDateTime.kt b/core/native/src/LocalDateTime.kt index 5c4c8400..5c26ebc5 100644 --- a/core/native/src/LocalDateTime.kt +++ b/core/native/src/LocalDateTime.kt @@ -94,8 +94,8 @@ internal fun LocalDateTime.until(other: LocalDateTime, unit: DateTimeUnit.DateBa endDate = endDate.plusDays(1) // won't throw: date - endDate >= 1 } return when (unit) { - is DateTimeUnit.DateBased.MonthBased -> date.monthsUntil(endDate) / unit.months - is DateTimeUnit.DateBased.DayBased -> date.daysUntil(endDate) / unit.days + is DateTimeUnit.MonthBased -> date.monthsUntil(endDate) / unit.months + is DateTimeUnit.DayBased -> date.daysUntil(endDate) / unit.days } } diff --git a/serialization/common/test/DateTimeUnitSerializationTest.kt b/serialization/common/test/DateTimeUnitSerializationTest.kt index f63fcab8..b221fa5b 100644 --- a/serialization/common/test/DateTimeUnitSerializationTest.kt +++ b/serialization/common/test/DateTimeUnitSerializationTest.kt @@ -24,20 +24,20 @@ class DateTimeUnitSerializationTest { } } - private fun dayBasedSerialization(serializer: KSerializer) { + private fun dayBasedSerialization(serializer: KSerializer) { repeat(100) { val days = Random.nextInt(1, Int.MAX_VALUE) - val unit = DateTimeUnit.DateBased.DayBased(days) + val unit = DateTimeUnit.DayBased(days) val json = "{\"days\":$days}" assertEquals(json, Json.encodeToString(serializer, unit)) assertEquals(unit, Json.decodeFromString(serializer, json)) } } - private fun monthBasedSerialization(serializer: KSerializer) { + private fun monthBasedSerialization(serializer: KSerializer) { repeat(100) { val months = Random.nextInt(1, Int.MAX_VALUE) - val unit = DateTimeUnit.DateBased.MonthBased(months) + val unit = DateTimeUnit.MonthBased(months) val json = "{\"months\":$months}" assertEquals(json, Json.encodeToString(serializer, unit)) assertEquals(unit, Json.decodeFromString(serializer, json)) @@ -47,14 +47,14 @@ class DateTimeUnitSerializationTest { private fun dateBasedSerialization(serializer: KSerializer) { repeat(100) { val days = Random.nextInt(1, Int.MAX_VALUE) - val unit = DateTimeUnit.DateBased.DayBased(days) + val unit = DateTimeUnit.DayBased(days) val json = "{\"type\":\"DayBased\",\"days\":$days}" assertEquals(json, Json.encodeToString(serializer, unit)) assertEquals(unit, Json.decodeFromString(serializer, json)) } repeat(100) { val months = Random.nextInt(1, Int.MAX_VALUE) - val unit = DateTimeUnit.DateBased.MonthBased(months) + val unit = DateTimeUnit.MonthBased(months) val json = "{\"type\":\"MonthBased\",\"months\":$months}" assertEquals(json, Json.encodeToString(serializer, unit)) assertEquals(unit, Json.decodeFromString(serializer, json)) @@ -71,14 +71,14 @@ class DateTimeUnitSerializationTest { } repeat(100) { val days = Random.nextInt(1, Int.MAX_VALUE) - val unit = DateTimeUnit.DateBased.DayBased(days) + val unit = DateTimeUnit.DayBased(days) val json = "{\"type\":\"DayBased\",\"days\":$days}" assertEquals(json, Json.encodeToString(serializer, unit)) assertEquals(unit, Json.decodeFromString(serializer, json)) } repeat(100) { val months = Random.nextInt(1, Int.MAX_VALUE) - val unit = DateTimeUnit.DateBased.MonthBased(months) + val unit = DateTimeUnit.MonthBased(months) val json = "{\"type\":\"MonthBased\",\"months\":$months}" assertEquals(json, Json.encodeToString(serializer, unit)) assertEquals(unit, Json.decodeFromString(serializer, json))