Skip to content

Remove ZonedDateTime from the Native implementation #331

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 31 additions & 41 deletions core/native/src/Instant.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ private const val MIN_SECOND = -31619119219200L // -1000000-01-01T00:00:00Z
*/
private const val MAX_SECOND = 31494816403199L // +1000000-12-31T23:59:59

private fun isValidInstantSecond(second: Long) = second >= MIN_SECOND && second <= MAX_SECOND
private fun isValidInstantSecond(second: Long) = second in MIN_SECOND..MAX_SECOND

@Serializable(with = InstantIso8601Serializer::class)
public actual class Instant internal constructor(public actual val epochSeconds: Long, public actual val nanosecondsOfSecond: Int) : Comparable<Instant> {
Expand All @@ -54,14 +54,11 @@ public actual class Instant internal constructor(public actual val epochSeconds:
* @throws ArithmeticException if arithmetic overflow occurs
* @throws IllegalArgumentException if the boundaries of Instant are overflown
*/
internal fun plus(secondsToAdd: Long, nanosToAdd: Long): Instant {
if ((secondsToAdd or nanosToAdd) == 0L) {
return this
}
internal fun plus(secondsToAdd: Long, nanosToAdd: Long): Instant = onNonZero(secondsToAdd or nanosToAdd) {
val newEpochSeconds: Long = safeAdd(safeAdd(epochSeconds, secondsToAdd), (nanosToAdd / NANOS_PER_ONE))
val newNanosToAdd = nanosToAdd % NANOS_PER_ONE
val nanoAdjustment = (nanosecondsOfSecond + newNanosToAdd) // safe int+NANOS_PER_ONE
return fromEpochSecondsThrowing(newEpochSeconds, nanoAdjustment)
fromEpochSecondsThrowing(newEpochSeconds, nanoAdjustment)
}

public actual operator fun plus(duration: Duration): Instant = duration.toComponents { secondsToAdd, nanosecondsToAdd ->
Expand All @@ -81,10 +78,7 @@ public actual class Instant internal constructor(public actual val epochSeconds:
(this.nanosecondsOfSecond - other.nanosecondsOfSecond).nanoseconds

actual override fun compareTo(other: Instant): Int {
val s = epochSeconds.compareTo(other.epochSeconds)
if (s != 0) {
return s
}
onNonZero(epochSeconds.compareTo(other.epochSeconds)) { return it }
return nanosecondsOfSecond.compareTo(other.nanosecondsOfSecond)
}

Expand Down Expand Up @@ -150,34 +144,21 @@ public actual class Instant internal constructor(public actual val epochSeconds:

}

private fun Instant.toZonedDateTimeFailing(zone: TimeZone): ZonedDateTime = try {
toZonedDateTime(zone)
} catch (e: IllegalArgumentException) {
throw DateTimeArithmeticException("Can not convert instant $this to LocalDateTime to perform computations", e)
}

/**
* @throws IllegalArgumentException if the [Instant] exceeds the boundaries of [LocalDateTime]
*/
private fun Instant.toZonedDateTime(zone: TimeZone): ZonedDateTime {
val currentOffset = zone.offsetAt(this)
return ZonedDateTime(toLocalDateTimeImpl(currentOffset), zone, currentOffset)
}

/** Check that [Instant] fits in [ZonedDateTime].
/** Check that [Instant] fits in [LocalDateTime].
* This is done on the results of computations for consistency with other platforms.
*/
private fun Instant.check(zone: TimeZone): Instant = [email protected] {
toZonedDateTimeFailing(zone)
toLocalDateTime(offsetIn(zone))
}

public actual fun Instant.plus(period: DateTimePeriod, timeZone: TimeZone): Instant = try {
with(period) {
val withDate = toZonedDateTimeFailing(timeZone)
.run { if (totalMonths != 0) plus(totalMonths, DateTimeUnit.MONTH) else this }
.run { if (days != 0) plus(days, DateTimeUnit.DAY) else this }
withDate.toInstant()
.run { if (totalNanoseconds != 0L) plus(0, totalNanoseconds).check(timeZone) else this }
val initialOffset = offsetIn(timeZone)
val newLdt = toLocalDateTime(initialOffset)
.run { onNonZero(totalMonths) { plus(it, DateTimeUnit.MONTH) } }
.run { onNonZero(days) { plus(it, DateTimeUnit.DAY) } }
timeZone.localDateTimeToInstant(newLdt, preferred = initialOffset)
.run { onNonZero(totalNanoseconds) { plus(totalNanoseconds, DateTimeUnit.NANOSECOND).check(timeZone) } }
}.check(timeZone)
} catch (e: ArithmeticException) {
throw DateTimeArithmeticException("Arithmetic overflow when adding CalendarPeriod to an Instant", e)
Expand All @@ -197,7 +178,9 @@ public actual fun Instant.plus(value: Long, unit: DateTimeUnit, timeZone: TimeZo
is DateTimeUnit.DateBased -> {
if (value < Int.MIN_VALUE || value > Int.MAX_VALUE)
throw ArithmeticException("Can't add a Long date-based value, as it would cause an overflow")
toZonedDateTimeFailing(timeZone).plus(value.toInt(), unit).toInstant()
val initialOffset = offsetIn(timeZone)
val initialLdt = toLocalDateTime(initialOffset)
timeZone.localDateTimeToInstant(initialLdt.plus(value.toInt(), unit), preferred = initialOffset)
}
is DateTimeUnit.TimeBased ->
check(timeZone).plus(value, unit).check(timeZone)
Expand All @@ -220,22 +203,29 @@ public actual fun Instant.plus(value: Long, unit: DateTimeUnit.TimeBased): Insta
}

public actual fun Instant.periodUntil(other: Instant, timeZone: TimeZone): DateTimePeriod {
var thisLdt = toZonedDateTimeFailing(timeZone)
val otherLdt = other.toZonedDateTimeFailing(timeZone)

val months = thisLdt.until(otherLdt, DateTimeUnit.MONTH).toInt() // `until` on dates never fails
thisLdt = thisLdt.plus(months, DateTimeUnit.MONTH) // won't throw: thisLdt + months <= otherLdt, which is known to be valid
val days = thisLdt.until(otherLdt, DateTimeUnit.DAY).toInt() // `until` on dates never fails
thisLdt = thisLdt.plus(days, DateTimeUnit.DAY) // won't throw: thisLdt + days <= otherLdt
val nanoseconds = thisLdt.until(otherLdt, DateTimeUnit.NANOSECOND) // |otherLdt - thisLdt| < 24h
val initialOffset = offsetIn(timeZone)
val initialLdt = toLocalDateTime(initialOffset)
val otherLdt = other.toLocalDateTime(other.offsetIn(timeZone))

val months = initialLdt.until(otherLdt, DateTimeUnit.MONTH) // `until` on dates never fails
val ldtWithMonths = initialLdt.plus(
months,
DateTimeUnit.MONTH
) // won't throw: thisLdt + months <= otherLdt, which is known to be valid
val days = ldtWithMonths.until(otherLdt, DateTimeUnit.DAY) // `until` on dates never fails
val newInstant = timeZone.localDateTimeToInstant(
ldtWithMonths.plus(days, DateTimeUnit.DAY),
preferred = initialOffset
) // won't throw: thisLdt + days <= otherLdt
val nanoseconds = newInstant.until(other, DateTimeUnit.NANOSECOND) // |otherLdt - thisLdt| < 24h

return buildDateTimePeriod(months, days, nanoseconds)
}

public actual fun Instant.until(other: Instant, unit: DateTimeUnit, timeZone: TimeZone): Long =
when (unit) {
is DateTimeUnit.DateBased ->
toZonedDateTimeFailing(timeZone).dateTime.until(other.toZonedDateTimeFailing(timeZone).dateTime, unit)
toLocalDateTime(offsetIn(timeZone)).until(other.toLocalDateTime(other.offsetIn(timeZone)), unit)
.toLong()
is DateTimeUnit.TimeBased -> {
check(timeZone); other.check(timeZone)
Expand Down
24 changes: 7 additions & 17 deletions core/native/src/LocalDate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,8 @@ public actual class LocalDate actual constructor(public actual val year: Int, pu

// Several times faster than using `compareBy`
actual override fun compareTo(other: LocalDate): Int {
val y = year.compareTo(other.year)
if (y != 0) {
return y
}
val m = monthNumber.compareTo(other.monthNumber)
if (m != 0) {
return m
}
onNonZero(year.compareTo(other.year)) { return it }
onNonZero(monthNumber.compareTo(other.monthNumber)) { return it }
return dayOfMonth.compareTo(other.dayOfMonth)
}

Expand All @@ -159,15 +153,12 @@ public actual class LocalDate actual constructor(public actual val year: Int, pu
* @throws IllegalArgumentException if the result exceeds the boundaries
* @throws ArithmeticException if arithmetic overflow occurs
*/
internal fun plusMonths(monthsToAdd: Int): LocalDate {
if (monthsToAdd == 0) {
return this
}
internal fun plusMonths(monthsToAdd: Int): LocalDate = onNonZero(monthsToAdd) {
val monthCount = year * 12 + (monthNumber - 1)
val calcMonths = safeAdd(monthCount, monthsToAdd)
val newYear = calcMonths.floorDiv(12)
val newMonth = calcMonths.mod(12) + 1
return resolvePreviousValid(newYear, newMonth, dayOfMonth)
resolvePreviousValid(newYear, newMonth, dayOfMonth)
}

// org.threeten.bp.LocalDate#plusDays
Expand All @@ -176,8 +167,7 @@ public actual class LocalDate actual constructor(public actual val year: Int, pu
* @throws ArithmeticException if arithmetic overflow occurs
*/
internal fun plusDays(daysToAdd: Int): LocalDate =
if (daysToAdd == 0) this
else fromEpochDays(safeAdd(toEpochDays(), daysToAdd))
onNonZero(daysToAdd) { fromEpochDays(safeAdd(toEpochDays(), daysToAdd)) }

override fun equals(other: Any?): Boolean =
this === other || (other is LocalDate && compareTo(other) == 0)
Expand Down Expand Up @@ -221,8 +211,8 @@ public actual operator fun LocalDate.plus(period: DatePeriod): LocalDate =
with(period) {
try {
this@plus
.run { if (totalMonths != 0) plusMonths(totalMonths) else this }
.run { if (days != 0) plusDays(days) else this }
.run { onNonZero(totalMonths) { plusMonths(it) } }
.run { onNonZero(days) { plusDays(days) } }
} catch (e: ArithmeticException) {
throw DateTimeArithmeticException("Arithmetic overflow when adding a period to a date", e)
} catch (e: IllegalArgumentException) {
Expand Down
13 changes: 3 additions & 10 deletions core/native/src/LocalDateTime.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,7 @@ public actual constructor(public actual val date: LocalDate, public actual val t

// Several times faster than using `compareBy`
actual override fun compareTo(other: LocalDateTime): Int {
val d = date.compareTo(other.date)
if (d != 0) {
return d
}
onNonZero(date.compareTo(other.date)) { return it }
return time.compareTo(other.time)
}

Expand Down Expand Up @@ -112,19 +109,15 @@ internal fun LocalDateTime.until(other: LocalDateTime, unit: DateTimeUnit.TimeBa
* @throws IllegalArgumentException if the result exceeds the boundaries
* @throws ArithmeticException if arithmetic overflow occurs
*/
internal fun LocalDateTime.plusSeconds(seconds: Int): LocalDateTime
{
if (seconds == 0) {
return this
}
internal fun LocalDateTime.plusSeconds(seconds: Int): LocalDateTime = onNonZero(seconds) {
val currentNanoOfDay = time.toNanosecondOfDay() // at most a day
val totalNanos: Long = seconds % SECONDS_PER_DAY * NANOS_PER_ONE.toLong() + // at most a day
currentNanoOfDay
val totalDays = seconds / SECONDS_PER_DAY + // max/24*60*60 < max * 0.000012
totalNanos.floorDiv(NANOS_PER_DAY) // max 2 days
val newNanoOfDay: Long = totalNanos.mod(NANOS_PER_DAY)
val newTime: LocalTime = if (newNanoOfDay == currentNanoOfDay) time else LocalTime.ofNanoOfDay(newNanoOfDay)
return LocalDateTime(date.plusDays(totalDays.toInt()), newTime)
LocalDateTime(date.plusDays(totalDays.toInt()), newTime)
}

private val ISO_DATETIME_OPTIONAL_SECONDS_TRAILING_ZEROS by lazy {
Expand Down
15 changes: 3 additions & 12 deletions core/native/src/LocalTime.kt
Original file line number Diff line number Diff line change
Expand Up @@ -86,18 +86,9 @@ public actual class LocalTime actual constructor(

// Several times faster than using `compareBy`
actual override fun compareTo(other: LocalTime): Int {
val h = hour.compareTo(other.hour)
if (h != 0) {
return h
}
val m = minute.compareTo(other.minute)
if (m != 0) {
return m
}
val s = second.compareTo(other.second)
if (s != 0) {
return s
}
onNonZero(hour.compareTo(other.hour)) { return it }
onNonZero(minute.compareTo(other.minute)) { return it }
onNonZero(second.compareTo(other.second)) { return it }
return nanosecond.compareTo(other.nanosecond)
}

Expand Down
32 changes: 9 additions & 23 deletions core/native/src/TimeZone.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,12 @@ public actual open class TimeZone internal constructor() {
) {
val prefix = zoneId.take(3)
val offset = lenientOffsetFormat.parse(zoneId.substring(3))
return when (offset.totalSeconds) {
0 -> FixedOffsetTimeZone(offset, prefix)
else -> FixedOffsetTimeZone(offset, "$prefix$offset")
}
return FixedOffsetTimeZone(offset, prefix.onNonZero(offset.totalSeconds) { "$prefix$offset" })
}
if (zoneId.startsWith("UT+") || zoneId.startsWith("UT-")) {
val offset = lenientOffsetFormat.parse(zoneId.substring(2))
return when (offset.totalSeconds) {
0 -> FixedOffsetTimeZone(offset, "UT")
else -> FixedOffsetTimeZone(offset, "UT$offset")
}
val prefix = "UT"
return FixedOffsetTimeZone(offset, prefix.onNonZero(offset.totalSeconds) { "$prefix$offset"})
}
} catch (e: DateTimeFormatException) {
throw IllegalTimeZoneException(e)
Expand All @@ -83,19 +78,13 @@ public actual open class TimeZone internal constructor() {
public actual fun Instant.toLocalDateTime(): LocalDateTime = instantToLocalDateTime(this)
public actual fun LocalDateTime.toInstant(): Instant = localDateTimeToInstant(this)

internal open fun atStartOfDay(date: LocalDate): Instant = error("Should be overridden") //value.atStartOfDay(date)
internal open fun atStartOfDay(date: LocalDate): Instant = error("Should be overridden")
internal open fun offsetAtImpl(instant: Instant): UtcOffset = error("Should be overridden")

internal open fun instantToLocalDateTime(instant: Instant): LocalDateTime = try {
instant.toLocalDateTimeImpl(offsetAtImpl(instant))
} catch (e: IllegalArgumentException) {
throw DateTimeArithmeticException("Instant $instant is not representable as LocalDateTime.", e)
}

internal open fun localDateTimeToInstant(dateTime: LocalDateTime): Instant =
atZone(dateTime).toInstant()
internal open fun instantToLocalDateTime(instant: Instant): LocalDateTime =
instant.toLocalDateTime(offsetAtImpl(instant))

internal open fun atZone(dateTime: LocalDateTime, preferred: UtcOffset? = null): ZonedDateTime =
internal open fun localDateTimeToInstant(dateTime: LocalDateTime, preferred: UtcOffset? = null): Instant =
error("Should be overridden")

override fun equals(other: Any?): Boolean =
Expand All @@ -119,11 +108,8 @@ public actual class FixedOffsetTimeZone internal constructor(public actual val o

override fun offsetAtImpl(instant: Instant): UtcOffset = offset

override fun atZone(dateTime: LocalDateTime, preferred: UtcOffset?): ZonedDateTime =
ZonedDateTime(dateTime, this, offset)

override fun instantToLocalDateTime(instant: Instant): LocalDateTime = instant.toLocalDateTime(offset)
override fun localDateTimeToInstant(dateTime: LocalDateTime): Instant = dateTime.toInstant(offset)
override fun localDateTimeToInstant(dateTime: LocalDateTime, preferred: UtcOffset?): Instant =
dateTime.toInstant(offset)
}


Expand Down
74 changes: 0 additions & 74 deletions core/native/src/ZonedDateTime.kt

This file was deleted.

Loading