Skip to content

Commit f3a390d

Browse files
committed
Fix overflow when subtracting Int.MIN_VALUE
1 parent 9828d4c commit f3a390d

File tree

9 files changed

+33
-4
lines changed

9 files changed

+33
-4
lines changed

core/commonMain/src/Instant.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,7 @@ public expect fun Instant.plus(value: Int, unit: DateTimeUnit, timeZone: TimeZon
342342
*
343343
* @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime].
344344
*/
345-
public fun Instant.minus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant =
346-
plus(-value, unit, timeZone)
345+
public expect fun Instant.minus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant
347346

348347
/**
349348
* Returns an instant that is the result of adding the [value] number of the specified [unit] to this instant.
@@ -365,7 +364,7 @@ public fun Instant.plus(value: Int, unit: DateTimeUnit.TimeBased): Instant =
365364
* The return value is clamped to the platform-specific boundaries for [Instant] if the result exceeds them.
366365
*/
367366
public fun Instant.minus(value: Int, unit: DateTimeUnit.TimeBased): Instant =
368-
plus(-value.toLong(), unit)
367+
minus(value.toLong(), unit)
369368

370369
/**
371370
* Returns an instant that is the result of adding the [value] number of the specified [unit] to this instant

core/commonMain/src/LocalDate.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ public expect fun LocalDate.plus(value: Int, unit: DateTimeUnit.DateBased): Loca
223223
*
224224
* @throws DateTimeArithmeticException if the result exceeds the boundaries of [LocalDate].
225225
*/
226-
public fun LocalDate.minus(value: Int, unit: DateTimeUnit.DateBased): LocalDate = plus(-value, unit)
226+
public expect fun LocalDate.minus(value: Int, unit: DateTimeUnit.DateBased): LocalDate
227227

228228
/**
229229
* Returns a [LocalDate] that is the result of adding the [value] number of the specified [unit] to this date.

core/commonTest/src/InstantTest.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,19 @@ class InstantRangeTest {
466466
assertArithmeticFails("$instant") { instant.plus(DateTimePeriod(seconds = Long.MAX_VALUE), UTC) }
467467
assertArithmeticFails("$instant") { instant.plus(DateTimePeriod(seconds = Long.MIN_VALUE), UTC) }
468468
}
469+
// Arithmetic overflow in an Int
470+
for (instant in smallInstants + listOf(maxValidInstant)) {
471+
assertEquals(instant.epochSeconds + Int.MIN_VALUE,
472+
instant.plus(Int.MIN_VALUE, DateTimeUnit.SECOND, UTC).epochSeconds)
473+
assertEquals(instant.epochSeconds - Int.MAX_VALUE,
474+
instant.minus(Int.MAX_VALUE, DateTimeUnit.SECOND, UTC).epochSeconds)
475+
}
476+
for (instant in smallInstants + listOf(minValidInstant)) {
477+
assertEquals(instant.epochSeconds + Int.MAX_VALUE,
478+
instant.plus(Int.MAX_VALUE, DateTimeUnit.SECOND, UTC).epochSeconds)
479+
assertEquals(instant.epochSeconds - Int.MIN_VALUE,
480+
instant.minus(Int.MIN_VALUE, DateTimeUnit.SECOND, UTC).epochSeconds)
481+
}
469482
// Overflowing a LocalDateTime in input
470483
maxValidInstant.plus(DateTimePeriod(nanoseconds = -1), UTC)
471484
minValidInstant.plus(DateTimePeriod(nanoseconds = 1), UTC)

core/jsMain/src/Instant.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,12 @@ public actual fun Instant.plus(value: Int, unit: DateTimeUnit, timeZone: TimeZon
178178
throw e
179179
}
180180

181+
public actual fun Instant.minus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant =
182+
if (value == Int.MIN_VALUE)
183+
plus(-value.toLong(), unit, timeZone)
184+
else
185+
plus(-value, unit, timeZone)
186+
181187
actual fun Instant.plus(value: Long, unit: DateTimeUnit.TimeBased): Instant =
182188
try {
183189
multiplyAndDivide(value, unit.nanoseconds, NANOS_PER_ONE.toLong()).let { (d, r) ->

core/jsMain/src/LocalDate.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public actual class LocalDate internal constructor(internal val value: jtLocalDa
5050

5151
public actual fun LocalDate.plus(unit: DateTimeUnit.DateBased): LocalDate = plusNumber(1, unit)
5252
public actual fun LocalDate.plus(value: Int, unit: DateTimeUnit.DateBased): LocalDate = plusNumber(value, unit)
53+
public actual fun LocalDate.minus(value: Int, unit: DateTimeUnit.DateBased): LocalDate = plusNumber(-value, unit)
5354
public actual fun LocalDate.plus(value: Long, unit: DateTimeUnit.DateBased): LocalDate = plusNumber(value, unit)
5455

5556
private fun LocalDate.plusNumber(value: Number, unit: DateTimeUnit.DateBased): LocalDate =

core/jvmMain/src/Instant.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ public actual fun Instant.plus(unit: DateTimeUnit, timeZone: TimeZone): Instant
110110
public actual fun Instant.plus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant =
111111
plus(value.toLong(), unit, timeZone)
112112

113+
public actual fun Instant.minus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant =
114+
plus(-value.toLong(), unit, timeZone)
115+
113116
public actual fun Instant.plus(value: Long, unit: DateTimeUnit, timeZone: TimeZone): Instant =
114117
try {
115118
val thisZdt = atZone(timeZone)

core/jvmMain/src/LocalDate.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ public actual fun LocalDate.plus(unit: DateTimeUnit.DateBased): LocalDate =
5555
public actual fun LocalDate.plus(value: Int, unit: DateTimeUnit.DateBased): LocalDate =
5656
plus(value.toLong(), unit)
5757

58+
public actual fun LocalDate.minus(value: Int, unit: DateTimeUnit.DateBased): LocalDate =
59+
plus(-value.toLong(), unit)
60+
5861
public actual fun LocalDate.plus(value: Long, unit: DateTimeUnit.DateBased): LocalDate =
5962
try {
6063
when (unit) {

core/nativeMain/src/Instant.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,8 @@ public actual fun Instant.plus(unit: DateTimeUnit, timeZone: TimeZone): Instant
297297
plus(1L, unit, timeZone)
298298
public actual fun Instant.plus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant =
299299
plus(value.toLong(), unit, timeZone)
300+
public actual fun Instant.minus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant =
301+
plus(-value.toLong(), unit, timeZone)
300302
public actual fun Instant.plus(value: Long, unit: DateTimeUnit, timeZone: TimeZone): Instant = try {
301303
when (unit) {
302304
is DateTimeUnit.DateBased -> {

core/nativeMain/src/LocalDate.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,8 @@ public actual fun LocalDate.plus(value: Int, unit: DateTimeUnit.DateBased): Loca
259259
throw DateTimeArithmeticException("Boundaries of LocalDate exceeded when adding a value", e)
260260
}
261261

262+
public actual fun LocalDate.minus(value: Int, unit: DateTimeUnit.DateBased): LocalDate = plus(-value, unit)
263+
262264
public actual fun LocalDate.plus(value: Long, unit: DateTimeUnit.DateBased): LocalDate =
263265
if (value > Int.MAX_VALUE || value < Int.MIN_VALUE)
264266
throw DateTimeArithmeticException("Can't add a Long to a LocalDate") // TODO: less specific message

0 commit comments

Comments
 (0)