Skip to content

Commit 7f9c06c

Browse files
committed
Remove all commonKotlin ZonedDateTime arithmetic logic
1 parent 3247cbf commit 7f9c06c

File tree

6 files changed

+45
-69
lines changed

6 files changed

+45
-69
lines changed

core/commonJs/src/internal/Platform.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ private object SystemTimeZone: TimeZone() {
110110
offsetInMinutesBeforePossibleTransition * SECONDS_PER_MINUTE * MILLIS_PER_ONE
111111
val offsetInMinutesAfterPossibleTransition = Date(epochMilliSystemZone.toDouble()).getTimezoneOffset().toInt()
112112
val offset = UtcOffset(minutes = -offsetInMinutesAfterPossibleTransition)
113-
return ZonedDateTime(dateTime, this, offset)
113+
return ZonedDateTime(dateTime, offset)
114114
}
115115

116116
override fun equals(other: Any?): Boolean = other === this

core/commonKotlin/src/Instant.kt

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ private fun Instant.toZonedDateTimeFailing(zone: TimeZone): ZonedDateTime = try
179179
*/
180180
private fun Instant.toZonedDateTime(zone: TimeZone): ZonedDateTime {
181181
val currentOffset = zone.offsetAt(this)
182-
return ZonedDateTime(toLocalDateTimeImpl(currentOffset), zone, currentOffset)
182+
return ZonedDateTime(toLocalDateTimeImpl(currentOffset), currentOffset)
183183
}
184184

185185
/** Check that [Instant] fits in [ZonedDateTime].
@@ -192,8 +192,8 @@ private fun Instant.check(zone: TimeZone): Instant = [email protected] {
192192
public actual fun Instant.plus(period: DateTimePeriod, timeZone: TimeZone): Instant = try {
193193
with(period) {
194194
val withDate = toZonedDateTimeFailing(timeZone)
195-
.run { if (totalMonths != 0L) plus(totalMonths, DateTimeUnit.MONTH) else this }
196-
.run { if (days != 0) plus(days.toLong(), DateTimeUnit.DAY) else this }
195+
.run { if (totalMonths != 0L) timeZone.atZone(dateTime.plus(totalMonths, DateTimeUnit.MONTH), offset) else this }
196+
.run { if (days != 0) timeZone.atZone(dateTime.plus(days, DateTimeUnit.DAY), offset) else this }
197197
withDate.toInstant()
198198
.run { if (totalNanoseconds != 0L) plus(0, totalNanoseconds).check(timeZone) else this }
199199
}.check(timeZone)
@@ -212,8 +212,13 @@ public actual fun Instant.minus(value: Int, unit: DateTimeUnit, timeZone: TimeZo
212212
plus(-value.toLong(), unit, timeZone)
213213
public actual fun Instant.plus(value: Long, unit: DateTimeUnit, timeZone: TimeZone): Instant = try {
214214
when (unit) {
215-
is DateTimeUnit.DateBased ->
216-
toZonedDateTimeFailing(timeZone).plus(value, unit).toInstant()
215+
is DateTimeUnit.DateBased -> {
216+
val toZonedDateTimeFailing = toZonedDateTimeFailing(timeZone)
217+
timeZone.atZone(
218+
toZonedDateTimeFailing.dateTime.plus(value, unit),
219+
toZonedDateTimeFailing.offset
220+
).toInstant()
221+
}
217222
is DateTimeUnit.TimeBased ->
218223
check(timeZone).plus(value, unit).check(timeZone)
219224
}
@@ -238,11 +243,19 @@ public actual fun Instant.periodUntil(other: Instant, timeZone: TimeZone): DateT
238243
var thisLdt = toZonedDateTimeFailing(timeZone)
239244
val otherLdt = other.toZonedDateTimeFailing(timeZone)
240245

241-
val months = thisLdt.until(otherLdt, DateTimeUnit.MONTH) // `until` on dates never fails
242-
thisLdt = thisLdt.plus(months, DateTimeUnit.MONTH) // won't throw: thisLdt + months <= otherLdt, which is known to be valid
243-
val days = thisLdt.until(otherLdt, DateTimeUnit.DAY) // `until` on dates never fails
244-
thisLdt = thisLdt.plus(days, DateTimeUnit.DAY) // won't throw: thisLdt + days <= otherLdt
245-
val nanoseconds = thisLdt.until(otherLdt, DateTimeUnit.NANOSECOND) // |otherLdt - thisLdt| < 24h
246+
val months = thisLdt.dateTime.until(otherLdt.dateTime, DateTimeUnit.MONTH) // `until` on dates never fails
247+
thisLdt = timeZone.atZone(
248+
thisLdt.dateTime.plus(months, DateTimeUnit.MONTH),
249+
thisLdt.offset
250+
) // won't throw: thisLdt + months <= otherLdt, which is known to be valid
251+
val days =
252+
thisLdt.dateTime.until(otherLdt.dateTime, DateTimeUnit.DAY) // `until` on dates never fails
253+
thisLdt = timeZone.atZone(
254+
thisLdt.dateTime.plus(days, DateTimeUnit.DAY),
255+
thisLdt.offset
256+
) // won't throw: thisLdt + days <= otherLdt
257+
val nanoseconds =
258+
thisLdt.toInstant().until(otherLdt.toInstant(), DateTimeUnit.NANOSECOND) // |otherLdt - thisLdt| < 24h
246259

247260
return buildDateTimePeriod(months, days.toInt(), nanoseconds)
248261
}
@@ -281,3 +294,9 @@ private val ISO_DATE_TIME_OFFSET_WITH_TRAILING_ZEROS = DateTimeComponents.Format
281294
outputSecond = WhenToOutput.IF_NONZERO
282295
)
283296
}
297+
298+
private fun LocalDateTime.plus(value: Long, unit: DateTimeUnit.DateBased) =
299+
date.plus(value, unit).atTime(time)
300+
301+
private fun LocalDateTime.plus(value: Int, unit: DateTimeUnit.DateBased) =
302+
date.plus(value, unit).atTime(time)

core/commonKotlin/src/TimeZone.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public actual class FixedOffsetTimeZone internal constructor(public actual val o
119119
override fun offsetAtImpl(instant: Instant): UtcOffset = offset
120120

121121
override fun atZone(dateTime: LocalDateTime, preferred: UtcOffset?): ZonedDateTime =
122-
ZonedDateTime(dateTime, this, offset)
122+
ZonedDateTime(dateTime, offset)
123123

124124
override fun instantToLocalDateTime(instant: Instant): LocalDateTime = instant.toLocalDateTime(offset)
125125
override fun localDateTimeToInstant(dateTime: LocalDateTime): Instant = dateTime.toInstant(offset)

core/commonKotlin/src/ZonedDateTime.kt

Lines changed: 4 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -8,68 +8,21 @@
88

99
package kotlinx.datetime
1010

11-
internal class ZonedDateTime(val dateTime: LocalDateTime, private val zone: TimeZone, val offset: UtcOffset) {
12-
/**
13-
* @throws IllegalArgumentException if the result exceeds the boundaries
14-
* @throws ArithmeticException if arithmetic overflow occurs
15-
*/
16-
internal fun plus(value: Long, unit: DateTimeUnit.DateBased): ZonedDateTime =
17-
dateTime.date.plus(value, unit).atTime(dateTime.time).resolve()
18-
19-
// Never throws in practice
20-
private fun LocalDateTime.resolve(): ZonedDateTime =
21-
// workaround for https://github.com/Kotlin/kotlinx-datetime/issues/51
22-
if (this@resolve.toInstant(offset).toLocalDateTime(zone) == this@resolve) {
23-
// this LocalDateTime is valid in these timezone and offset.
24-
ZonedDateTime(this, zone, offset)
25-
} else {
26-
// this LDT does need proper resolving, as the instant that it would map to given the preferred offset
27-
// is is mapped to another LDT.
28-
zone.atZone(this, offset)
29-
}
30-
11+
internal class ZonedDateTime(val dateTime: LocalDateTime, val offset: UtcOffset) {
3112
override fun equals(other: Any?): Boolean =
3213
this === other || other is ZonedDateTime &&
33-
dateTime == other.dateTime && offset == other.offset && zone == other.zone
14+
dateTime == other.dateTime && offset == other.offset
3415

3516
override fun hashCode(): Int {
36-
return dateTime.hashCode() xor offset.hashCode() xor zone.hashCode().rotateLeft(3)
17+
return dateTime.hashCode() xor offset.hashCode()
3718
}
3819

3920
override fun toString(): String {
40-
var str = dateTime.toString() + offset.toString()
41-
if (zone !is FixedOffsetTimeZone || offset !== zone.offset) {
42-
str += "[$zone]"
43-
}
21+
val str = dateTime.toString() + offset.toString()
4422
return str
4523
}
4624
}
4725

4826
internal fun ZonedDateTime.toInstant(): Instant =
4927
Instant(dateTime.toEpochSecond(offset), dateTime.nanosecond)
5028

51-
52-
// org.threeten.bp.ZonedDateTime#until
53-
// This version is simplified and to be used ONLY in case you know the timezones are equal!
54-
/**
55-
* @throws ArithmeticException on arithmetic overflow
56-
* @throws DateTimeArithmeticException if setting [other] to the offset of [this] leads to exceeding boundaries of
57-
* [LocalDateTime].
58-
*/
59-
60-
internal fun ZonedDateTime.until(other: ZonedDateTime, unit: DateTimeUnit): Long =
61-
when (unit) {
62-
// if the time unit is date-based, the offsets are disregarded and only the dates and times are compared.
63-
is DateTimeUnit.DateBased -> dateTime.until(other.dateTime, unit)
64-
// if the time unit is not date-based, we need to make sure that [other] is at the same offset as [this].
65-
is DateTimeUnit.TimeBased -> {
66-
val offsetDiff = offset.totalSeconds - other.offset.totalSeconds
67-
val otherLdtAdjusted = try {
68-
other.dateTime.plusSeconds(offsetDiff)
69-
} catch (e: IllegalArgumentException) {
70-
throw DateTimeArithmeticException(
71-
"Unable to find difference between date-times, as one of them overflowed")
72-
}
73-
dateTime.until(otherLdtAdjusted, unit)
74-
}
75-
}

core/commonKotlin/src/internal/RegionTimeZone.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ internal class RegionTimeZone(private val tzid: TimeZoneRules, override val id:
2020

2121
override fun atZone(dateTime: LocalDateTime, preferred: UtcOffset?): ZonedDateTime =
2222
when (val info = tzid.infoAtDatetime(dateTime)) {
23-
is OffsetInfo.Regular -> ZonedDateTime(dateTime, this, info.offset)
23+
is OffsetInfo.Regular -> ZonedDateTime(dateTime, info.offset)
2424
is OffsetInfo.Gap -> {
2525
try {
26-
ZonedDateTime(dateTime.plusSeconds(info.transitionDurationSeconds), this, info.offsetAfter)
26+
ZonedDateTime(dateTime.plusSeconds(info.transitionDurationSeconds), info.offsetAfter)
2727
} catch (e: IllegalArgumentException) {
2828
throw DateTimeArithmeticException(
2929
"Overflow whet correcting the date-time to not be in the transition gap",
@@ -32,8 +32,10 @@ internal class RegionTimeZone(private val tzid: TimeZoneRules, override val id:
3232
}
3333
}
3434

35-
is OffsetInfo.Overlap -> ZonedDateTime(dateTime, this,
36-
if (info.offsetAfter == preferred) info.offsetAfter else info.offsetBefore)
35+
is OffsetInfo.Overlap -> ZonedDateTime(
36+
dateTime,
37+
if (info.offsetAfter == preferred) info.offsetAfter else info.offsetBefore
38+
)
3739
}
3840

3941
override fun offsetAtImpl(instant: Instant): UtcOffset = tzid.infoAtInstant(instant)

core/commonKotlin/test/ThreeTenBpTimeZoneTest.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,10 @@ class ThreeTenBpTimeZoneTest {
4040
fun overlappingLocalTime() {
4141
val t = LocalDateTime(2007, 10, 28, 2, 30, 0, 0)
4242
val zone = TimeZone.of("Europe/Paris")
43-
assertEquals(ZonedDateTime(LocalDateTime(2007, 10, 28, 2, 30, 0, 0),
44-
zone, UtcOffset(seconds = 2 * 3600)), zone.atZone(t))
43+
assertEquals(ZonedDateTime(
44+
LocalDateTime(2007, 10, 28, 2, 30, 0, 0),
45+
UtcOffset(seconds = 2 * 3600)
46+
), zone.atZone(t))
4547
}
4648

4749
}

0 commit comments

Comments
 (0)