Skip to content

Commit

Permalink
Remove extra hierarchy level in DateTimeUnit sealed class inheritors
Browse files Browse the repository at this point in the history
  • Loading branch information
ilya-g committed Sep 21, 2021
1 parent 44b8ee3 commit 4177ff2
Show file tree
Hide file tree
Showing 9 changed files with 84 additions and 77 deletions.
77 changes: 42 additions & 35 deletions core/common/src/DateTimeUnit.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}

Expand All @@ -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
}
}
20 changes: 10 additions & 10 deletions core/common/src/serializers/DateTimeUnitSerializers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,21 @@ public object TimeBasedDateTimeUnitSerializer: KSerializer<DateTimeUnit.TimeBase
}
}

public object DayBasedDateTimeUnitSerializer: KSerializer<DateTimeUnit.DateBased.DayBased> {
public object DayBasedDateTimeUnitSerializer: KSerializer<DateTimeUnit.DayBased> {

override val descriptor: SerialDescriptor = buildClassSerialDescriptor("DayBased") {
element<Int>("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)
}
}

@ExperimentalSerializationApi
@Suppress("INVISIBLE_MEMBER") // to be able to throw `MissingFieldException`
override fun deserialize(decoder: Decoder): DateTimeUnit.DateBased.DayBased {
override fun deserialize(decoder: Decoder): DateTimeUnit.DayBased {
var seen = false
var days = 0
decoder.decodeStructure(descriptor) {
Expand All @@ -88,25 +88,25 @@ public object DayBasedDateTimeUnitSerializer: KSerializer<DateTimeUnit.DateBased
}
}
if (!seen) throw MissingFieldException("days")
return DateTimeUnit.DateBased.DayBased(days)
return DateTimeUnit.DayBased(days)
}
}

public object MonthBasedDateTimeUnitSerializer: KSerializer<DateTimeUnit.DateBased.MonthBased> {
public object MonthBasedDateTimeUnitSerializer: KSerializer<DateTimeUnit.MonthBased> {

override val descriptor: SerialDescriptor = buildClassSerialDescriptor("MonthBased") {
element<Int>("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)
}
}

@ExperimentalSerializationApi
@Suppress("INVISIBLE_MEMBER") // to be able to throw `MissingFieldException`
override fun deserialize(decoder: Decoder): DateTimeUnit.DateBased.MonthBased {
override fun deserialize(decoder: Decoder): DateTimeUnit.MonthBased {
var seen = false
var months = 0
decoder.decodeStructure(descriptor) {
Expand All @@ -127,7 +127,7 @@ public object MonthBasedDateTimeUnitSerializer: KSerializer<DateTimeUnit.DateBas
}
}
if (!seen) throw MissingFieldException("months")
return DateTimeUnit.DateBased.MonthBased(months)
return DateTimeUnit.MonthBased(months)
}
}

Expand All @@ -136,7 +136,7 @@ public object DateBasedDateTimeUnitSerializer: AbstractPolymorphicSerializer<Dat

private val impl = SealedClassSerializer("kotlinx.datetime.DateTimeUnit.DateBased",
DateTimeUnit.DateBased::class,
arrayOf(DateTimeUnit.DateBased.DayBased::class, DateTimeUnit.DateBased.MonthBased::class),
arrayOf(DateTimeUnit.DayBased::class, DateTimeUnit.MonthBased::class),
arrayOf(DayBasedDateTimeUnitSerializer, MonthBasedDateTimeUnitSerializer))

@InternalSerializationApi
Expand Down Expand Up @@ -164,7 +164,7 @@ public object DateTimeUnitSerializer: AbstractPolymorphicSerializer<DateTimeUnit

private val impl = SealedClassSerializer("kotlinx.datetime.DateTimeUnit",
DateTimeUnit::class,
arrayOf(DateTimeUnit.DateBased.DayBased::class, DateTimeUnit.DateBased.MonthBased::class, DateTimeUnit.TimeBased::class),
arrayOf(DateTimeUnit.DayBased::class, DateTimeUnit.MonthBased::class, DateTimeUnit.TimeBased::class),
arrayOf(DayBasedDateTimeUnitSerializer, MonthBasedDateTimeUnitSerializer, TimeBasedDateTimeUnitSerializer))

@InternalSerializationApi
Expand Down
12 changes: 6 additions & 6 deletions core/js/src/Instant.kt
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,9 @@ public actual fun Instant.plus(value: Long, unit: DateTimeUnit, timeZone: TimeZo
is DateTimeUnit.TimeBased -> {
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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions core/js/src/LocalDate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 =
Expand Down
8 changes: 4 additions & 4 deletions core/jvm/src/Instant.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
Expand Down
8 changes: 4 additions & 4 deletions core/jvm/src/LocalDate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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 =
Expand Down
8 changes: 4 additions & 4 deletions core/native/src/LocalDate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions core/native/src/LocalDateTime.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand Down
16 changes: 8 additions & 8 deletions serialization/common/test/DateTimeUnitSerializationTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,20 @@ class DateTimeUnitSerializationTest {
}
}

private fun dayBasedSerialization(serializer: KSerializer<DateTimeUnit.DateBased.DayBased>) {
private fun dayBasedSerialization(serializer: KSerializer<DateTimeUnit.DayBased>) {
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<DateTimeUnit.DateBased.MonthBased>) {
private fun monthBasedSerialization(serializer: KSerializer<DateTimeUnit.MonthBased>) {
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))
Expand All @@ -47,14 +47,14 @@ class DateTimeUnitSerializationTest {
private fun dateBasedSerialization(serializer: KSerializer<DateTimeUnit.DateBased>) {
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))
Expand All @@ -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))
Expand Down

0 comments on commit 4177ff2

Please sign in to comment.