Skip to content

Commit 65126f5

Browse files
committed
Common: add tests for exceptions and clamping
1 parent d694b21 commit 65126f5

File tree

16 files changed

+282
-29
lines changed

16 files changed

+282
-29
lines changed

core/commonMain/src/Instant.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ public expect class Instant : Comparable<Instant> {
6262
* @throws DateTimeFormatException if the text cannot be parsed or the boundaries of [Instant] are exceeded.
6363
*/
6464
fun parse(isoString: String): Instant
65+
66+
internal val MIN: Instant
67+
internal val MAX: Instant
6568
}
6669
}
6770

core/commonMain/src/LocalDate.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ public expect class LocalDate : Comparable<LocalDate> {
1111
* @throws DateTimeFormatException if the text cannot be parsed or the boundaries of [LocalDate] are exceeded.
1212
*/
1313
public fun parse(isoString: String): LocalDate
14+
15+
internal val MIN: LocalDate
16+
internal val MAX: LocalDate
1417
}
1518

1619
/**

core/commonMain/src/LocalDateTime.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ public expect class LocalDateTime : Comparable<LocalDateTime> {
1414
* exceeded.
1515
*/
1616
public fun parse(isoString: String): LocalDateTime
17+
18+
internal val MIN: LocalDateTime
19+
internal val MAX: LocalDateTime
1720
}
1821

1922
/**

core/commonTest/src/InstantTest.kt

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,4 +296,185 @@ class InstantTest {
296296
assertEquals("+19999-12-31T23:59:59.000000009Z", LocalDateTime(19999, 12, 31, 23, 59, 59, 9).toInstant(TimeZone.UTC).toString())
297297
}
298298

299+
private val largePositiveLongs = listOf(Long.MAX_VALUE, Long.MAX_VALUE - 1, Long.MAX_VALUE - 50)
300+
private val largeNegativeLongs = listOf(Long.MIN_VALUE, Long.MIN_VALUE + 1, Long.MIN_VALUE + 50)
301+
@OptIn(ExperimentalTime::class)
302+
private val largePositiveInstants = listOf(Instant.MAX, Instant.MAX - 1.seconds, Instant.MAX - 50.seconds)
303+
@OptIn(ExperimentalTime::class)
304+
private val largeNegativeInstants = listOf(Instant.MIN, Instant.MIN + 1.seconds, Instant.MIN + 50.seconds)
305+
private val smallInstants = listOf(
306+
Instant.fromEpochMilliseconds(0),
307+
Instant.fromEpochMilliseconds(1003),
308+
Instant.fromEpochMilliseconds(253112)
309+
)
310+
311+
@Test
312+
fun testParsingThrowing() {
313+
assertFailsWith<DateTimeFormatException> { Instant.parse("x") }
314+
assertFailsWith<DateTimeFormatException> { Instant.parse("12020-12-31T23:59:59.000000000Z") }
315+
// this string represents an Instant that is currently larger than Instant.MAX any of the implementations:
316+
assertFailsWith<DateTimeFormatException> { Instant.parse("+1000000001-12-31T23:59:59.000000000Z") }
317+
}
318+
319+
@ExperimentalTime
320+
@Test
321+
fun testConstructorAccessorClamping() {
322+
// toEpochMilliseconds()/fromEpochMilliseconds()
323+
// assuming that ranges of Long (representing a number of milliseconds) and Instant are not just overlapping,
324+
// but one is included in the other.
325+
if (Instant.MAX.epochSeconds > Long.MAX_VALUE / 1000) {
326+
/* Any number of milliseconds in Long is representable as an Instant */
327+
for (instant in largePositiveInstants) {
328+
assertEquals(Long.MAX_VALUE, instant.toEpochMilliseconds(), "$instant")
329+
}
330+
for (instant in largeNegativeInstants) {
331+
assertEquals(Long.MIN_VALUE, instant.toEpochMilliseconds(), "$instant")
332+
}
333+
for (milliseconds in largePositiveLongs + largeNegativeLongs) {
334+
assertEquals(milliseconds, Instant.fromEpochMilliseconds(milliseconds).toEpochMilliseconds(),
335+
"$milliseconds")
336+
}
337+
} else {
338+
/* Any Instant is representable as a number of milliseconds in Long */
339+
for (milliseconds in largePositiveLongs) {
340+
assertEquals(Instant.MAX, Instant.fromEpochMilliseconds(milliseconds), "$milliseconds")
341+
}
342+
for (milliseconds in largeNegativeLongs) {
343+
assertEquals(Instant.MIN, Instant.fromEpochMilliseconds(milliseconds), "$milliseconds")
344+
}
345+
for (instant in largePositiveInstants + smallInstants + largeNegativeInstants) {
346+
assertEquals(instant.epochSeconds,
347+
Instant.fromEpochMilliseconds(instant.toEpochMilliseconds()).epochSeconds, "$instant")
348+
}
349+
}
350+
// fromEpochSeconds
351+
// On all platforms Long.MAX_VALUE of seconds is not a valid instant.
352+
for (seconds in largePositiveLongs) {
353+
assertEquals(Instant.MAX, Instant.fromEpochSeconds(seconds, 35))
354+
}
355+
for (seconds in largeNegativeLongs) {
356+
assertEquals(Instant.MIN, Instant.fromEpochSeconds(seconds, 35))
357+
}
358+
for (instant in largePositiveInstants + smallInstants + largeNegativeInstants) {
359+
assertEquals(instant, Instant.fromEpochSeconds(instant.epochSeconds, instant.nanosecondsOfSecond.toLong()))
360+
}
361+
}
362+
363+
@OptIn(ExperimentalTime::class)
364+
@Test
365+
fun testArithmeticClamping() {
366+
for (instant in smallInstants + largeNegativeInstants + largePositiveInstants) {
367+
assertEquals(Instant.MAX, instant + Duration.INFINITE)
368+
}
369+
for (instant in smallInstants + largeNegativeInstants + largePositiveInstants) {
370+
assertEquals(Instant.MIN, instant - Duration.INFINITE)
371+
}
372+
assertEquals(Instant.MAX, (Instant.MAX - 4.seconds) + 5.seconds)
373+
assertEquals(Instant.MIN, (Instant.MIN + 10.seconds) - 12.seconds)
374+
}
375+
376+
@OptIn(ExperimentalTime::class)
377+
@Test
378+
fun testCalendarArithmeticThrowing() {
379+
val maxValidInstant = LocalDateTime.MAX.toInstant(TimeZone.UTC)
380+
val minValidInstant = LocalDateTime.MIN.toInstant(TimeZone.UTC)
381+
382+
// Instant.plus(DateTimePeriod(), TimeZone)
383+
// Arithmetic overflow
384+
for (instant in smallInstants + largeNegativeInstants + largePositiveInstants) {
385+
assertFailsWith<DateTimeArithmeticException>("$instant") {
386+
instant.plus(DateTimePeriod(seconds = Long.MAX_VALUE), TimeZone.UTC)
387+
}
388+
assertFailsWith<DateTimeArithmeticException>("$instant") {
389+
instant.plus(DateTimePeriod(seconds = Long.MIN_VALUE), TimeZone.UTC)
390+
}
391+
}
392+
// Overflowing a LocalDateTime in input
393+
maxValidInstant.plus(DateTimePeriod(nanoseconds = -1), TimeZone.UTC)
394+
minValidInstant.plus(DateTimePeriod(nanoseconds = 1), TimeZone.UTC)
395+
assertFailsWith<DateTimeArithmeticException> {
396+
(maxValidInstant + 1.nanoseconds).plus(DateTimePeriod(nanoseconds = -2), TimeZone.UTC)
397+
}
398+
assertFailsWith<DateTimeArithmeticException> {
399+
(minValidInstant - 1.nanoseconds).plus(DateTimePeriod(nanoseconds = 2), TimeZone.UTC)
400+
}
401+
// Overflowing a LocalDateTime in result
402+
assertFailsWith<DateTimeArithmeticException> {
403+
maxValidInstant.plus(DateTimePeriod(nanoseconds = 1), TimeZone.UTC)
404+
}
405+
assertFailsWith<DateTimeArithmeticException> {
406+
minValidInstant.plus(DateTimePeriod(nanoseconds = -1), TimeZone.UTC)
407+
}
408+
// Overflowing a LocalDateTime in intermediate computations
409+
assertFailsWith<DateTimeArithmeticException> {
410+
maxValidInstant.plus(DateTimePeriod(seconds = 1, nanoseconds = -1_000_000_001), TimeZone.UTC)
411+
}
412+
assertFailsWith<DateTimeArithmeticException> {
413+
maxValidInstant.plus(DateTimePeriod(hours = 1, minutes = -61), TimeZone.UTC)
414+
}
415+
assertFailsWith<DateTimeArithmeticException> {
416+
maxValidInstant.plus(DateTimePeriod(days = 1, hours = -48), TimeZone.UTC)
417+
}
418+
419+
// Instant.plus(Long, DateTimeUnit, TimeZone)
420+
// Arithmetic overflow
421+
for (instant in smallInstants + largeNegativeInstants + largePositiveInstants) {
422+
assertFailsWith<DateTimeArithmeticException>("$instant") {
423+
instant.plus(Long.MAX_VALUE, DateTimeUnit.SECOND, TimeZone.UTC)
424+
}
425+
assertFailsWith<DateTimeArithmeticException>("$instant") {
426+
instant.plus(Long.MIN_VALUE, DateTimeUnit.SECOND, TimeZone.UTC)
427+
}
428+
assertFailsWith<DateTimeArithmeticException>("$instant") {
429+
instant.plus(Long.MAX_VALUE, DateTimeUnit.YEAR, TimeZone.UTC)
430+
}
431+
assertFailsWith<DateTimeArithmeticException>("$instant") {
432+
instant.plus(Long.MIN_VALUE, DateTimeUnit.YEAR, TimeZone.UTC)
433+
}
434+
}
435+
// Overflowing a LocalDateTime in input
436+
maxValidInstant.plus(-1, DateTimeUnit.NANOSECOND, TimeZone.UTC)
437+
minValidInstant.plus(1, DateTimeUnit.NANOSECOND, TimeZone.UTC)
438+
assertFailsWith<DateTimeArithmeticException> {
439+
(maxValidInstant + 1.nanoseconds).plus(-2, DateTimeUnit.NANOSECOND, TimeZone.UTC)
440+
}
441+
assertFailsWith<DateTimeArithmeticException> {
442+
(minValidInstant - 1.nanoseconds).plus(2, DateTimeUnit.NANOSECOND, TimeZone.UTC)
443+
}
444+
// Overflowing a LocalDateTime in result
445+
assertFailsWith<DateTimeArithmeticException> {
446+
maxValidInstant.plus(1, DateTimeUnit.NANOSECOND, TimeZone.UTC)
447+
}
448+
assertFailsWith<DateTimeArithmeticException> {
449+
maxValidInstant.plus(1, DateTimeUnit.YEAR, TimeZone.UTC)
450+
}
451+
assertFailsWith<DateTimeArithmeticException> {
452+
minValidInstant.plus(-1, DateTimeUnit.NANOSECOND, TimeZone.UTC)
453+
}
454+
assertFailsWith<DateTimeArithmeticException> {
455+
minValidInstant.plus(-1, DateTimeUnit.YEAR, TimeZone.UTC)
456+
}
457+
458+
// Instant.periodUntil
459+
maxValidInstant.periodUntil(minValidInstant, TimeZone.UTC)
460+
assertFailsWith<DateTimeArithmeticException> {
461+
(maxValidInstant + 1.nanoseconds).periodUntil(minValidInstant, TimeZone.UTC)
462+
}
463+
assertFailsWith<DateTimeArithmeticException> {
464+
maxValidInstant.periodUntil(minValidInstant - 1.nanoseconds, TimeZone.UTC)
465+
}
466+
467+
// Instant.until
468+
// Arithmetic overflow
469+
assertEquals(Long.MAX_VALUE, minValidInstant.until(maxValidInstant, DateTimeUnit.NANOSECOND, TimeZone.UTC))
470+
assertEquals(Long.MIN_VALUE, maxValidInstant.until(minValidInstant, DateTimeUnit.NANOSECOND, TimeZone.UTC))
471+
// Overflowing a LocalDateTime in input
472+
assertFailsWith<DateTimeArithmeticException> {
473+
(maxValidInstant + 1.nanoseconds).until(maxValidInstant, DateTimeUnit.NANOSECOND, TimeZone.UTC)
474+
}
475+
assertFailsWith<DateTimeArithmeticException> {
476+
maxValidInstant.until(maxValidInstant + 1.nanoseconds, DateTimeUnit.NANOSECOND, TimeZone.UTC)
477+
}
478+
}
479+
299480
}

core/commonTest/src/LocalDateTest.kt

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,13 @@ class LocalDateTest {
4242
checkParsedComponents("2019-10-01", 2019, 10, 1, 2, 274)
4343
checkParsedComponents("2016-02-29", 2016, 2, 29, 1, 60)
4444
checkParsedComponents("2017-10-01", 2017, 10, 1, 7, 274)
45-
assertFailsWith<Throwable> { LocalDate.parse("102017-10-01") }
46-
assertFailsWith<Throwable> { LocalDate.parse("2017--10-01") }
47-
assertFailsWith<Throwable> { LocalDate.parse("2017-+10-01") }
48-
assertFailsWith<Throwable> { LocalDate.parse("2017-10-+01") }
49-
assertFailsWith<Throwable> { LocalDate.parse("2017-10--01") }
45+
assertFailsWith<DateTimeFormatException> { LocalDate.parse("102017-10-01") }
46+
assertFailsWith<DateTimeFormatException> { LocalDate.parse("2017--10-01") }
47+
assertFailsWith<DateTimeFormatException> { LocalDate.parse("2017-+10-01") }
48+
assertFailsWith<DateTimeFormatException> { LocalDate.parse("2017-10-+01") }
49+
assertFailsWith<DateTimeFormatException> { LocalDate.parse("2017-10--01") }
50+
// this date is currently larger than the largest representable one any of the platforms:
51+
assertFailsWith<DateTimeFormatException> { LocalDate.parse("+1000000000-10-01") }
5052
}
5153

5254
@Test
@@ -65,7 +67,7 @@ class LocalDateTest {
6567

6668
checkComponents(LocalDate.parse("2016-01-31") + DatePeriod(months = 1), 2016, 2, 29)
6769

68-
// assertFailsWith<IllegalArgumentException> { startDate + CalendarPeriod(hours = 7) } // won't compile
70+
// assertFailsWith<IllegalArgumentException> { startDate + DateTimePeriod(hours = 7) } // won't compile
6971
// assertFailsWith<IllegalArgumentException> { startDate.plus(7, ChronoUnit.MINUTE) } // won't compile
7072
}
7173

@@ -136,15 +138,42 @@ class LocalDateTest {
136138

137139
@Test
138140
fun invalidDate() {
139-
assertFailsWith<Throwable> { LocalDate(2007, 2, 29) }
141+
assertFailsWith<IllegalArgumentException> { LocalDate(2007, 2, 29) }
140142
LocalDate(2008, 2, 29)
141-
assertFailsWith<Throwable> { LocalDate(2007, 4, 31) }
142-
assertFailsWith<Throwable> { LocalDate(2007, 1, 0) }
143-
assertFailsWith<Throwable> { LocalDate(2007,1, 32) }
144-
assertFailsWith<Throwable> { LocalDate(Int.MIN_VALUE, 1, 1) }
145-
assertFailsWith<Throwable> { LocalDate(2007, 1, 32) }
146-
assertFailsWith<Throwable> { LocalDate(2007, 0, 1) }
147-
assertFailsWith<Throwable> { LocalDate(2007, 13, 1) }
143+
assertFailsWith<IllegalArgumentException> { LocalDate(2007, 4, 31) }
144+
assertFailsWith<IllegalArgumentException> { LocalDate(2007, 1, 0) }
145+
assertFailsWith<IllegalArgumentException> { LocalDate(2007,1, 32) }
146+
assertFailsWith<IllegalArgumentException> { LocalDate(Int.MIN_VALUE, 1, 1) }
147+
assertFailsWith<IllegalArgumentException> { LocalDate(2007, 1, 32) }
148+
assertFailsWith<IllegalArgumentException> { LocalDate(2007, 0, 1) }
149+
assertFailsWith<IllegalArgumentException> { LocalDate(2007, 13, 1) }
150+
}
151+
152+
@Test
153+
fun testArithmeticThrowing() {
154+
// LocalDate.plus(Long, DateTimeUnit)
155+
LocalDate.MAX.plus(-1, DateTimeUnit.DAY)
156+
LocalDate.MIN.plus(1, DateTimeUnit.DAY)
157+
// Arithmetic overflow
158+
assertFailsWith<DateTimeArithmeticException> { LocalDate.MAX.plus(Long.MAX_VALUE, DateTimeUnit.YEAR) }
159+
assertFailsWith<DateTimeArithmeticException> { LocalDate.MAX.plus(Long.MAX_VALUE - 2, DateTimeUnit.YEAR) }
160+
assertFailsWith<DateTimeArithmeticException> { LocalDate.MIN.plus(Long.MIN_VALUE, DateTimeUnit.YEAR) }
161+
assertFailsWith<DateTimeArithmeticException> { LocalDate.MIN.plus(Long.MIN_VALUE + 2, DateTimeUnit.YEAR) }
162+
assertFailsWith<DateTimeArithmeticException> { LocalDate.MIN.plus(Long.MAX_VALUE, DateTimeUnit.DAY) }
163+
// Exceeding the boundaries of LocalDate
164+
assertFailsWith<DateTimeArithmeticException> { LocalDate.MAX.plus(1, DateTimeUnit.YEAR) }
165+
assertFailsWith<DateTimeArithmeticException> { LocalDate.MIN.plus(-1, DateTimeUnit.YEAR) }
166+
167+
// LocalDate.plus(DatePeriod)
168+
LocalDate.MAX.plus(DatePeriod(years = -2, months = 12, days = 31))
169+
// Exceeding the boundaries in result
170+
assertFailsWith<DateTimeArithmeticException> {
171+
LocalDate.MAX.plus(DatePeriod(years = -2, months = 24, days = 1))
172+
}
173+
// Exceeding the boundaries in intermediate computations
174+
assertFailsWith<DateTimeArithmeticException> {
175+
LocalDate.MAX.plus(DatePeriod(years = -2, months = 25, days = -1000))
176+
}
148177
}
149178

150179
}

core/commonTest/src/LocalDateTimeTest.kt

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ class LocalDateTimeTest {
2323
checkParsedComponents("2019-10-01T18:43:15", 2019, 10, 1, 18, 43, 15, 0, 2, 274)
2424
checkParsedComponents("2019-10-01T18:12", 2019, 10, 1, 18, 12, 0, 0, 2, 274)
2525

26+
assertFailsWith<DateTimeFormatException> { LocalDateTime.parse("x") }
27+
assertFailsWith<DateTimeFormatException> { "+1000000000-03-26T04:00:00".toLocalDateTime() }
28+
2629
/* Based on the ThreeTenBp project.
2730
* Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos
2831
*/
@@ -41,6 +44,8 @@ class LocalDateTimeTest {
4144

4245
val diff = with(TimeZone.UTC) { ldt2.toInstant() - ldt1.toInstant() }
4346
assertEquals(1.hours + 7.minutes - 15.seconds + 400100.microseconds, diff)
47+
assertFailsWith<DateTimeArithmeticException> { (Instant.MAX - 3.days).toLocalDateTime(TimeZone.UTC) }
48+
assertFailsWith<DateTimeArithmeticException> { (Instant.MIN + 6.hours).toLocalDateTime(TimeZone.UTC) }
4449
}
4550

4651
@OptIn(ExperimentalTime::class)
@@ -57,6 +62,7 @@ class LocalDateTimeTest {
5762
val instant = Instant.parse("2019-10-01T18:43:15.100500Z")
5863
val datetime = instant.toLocalDateTime(TimeZone.UTC)
5964
checkComponents(datetime, 2019, 10, 1, 18, 43, 15, 100500000)
65+
assertFailsWith<DateTimeArithmeticException> { Instant.MAX.toLocalDateTime(TimeZone.UTC) }
6066
}
6167

6268
@Test
@@ -99,14 +105,14 @@ class LocalDateTimeTest {
99105
fun localTime(hour: Int, minute: Int, second: Int = 0, nanosecond: Int = 0): LocalDateTime =
100106
LocalDateTime(2020, 1, 1, hour, minute, second, nanosecond)
101107
localTime(23, 59)
102-
assertFailsWith<Throwable> { localTime(-1, 0) }
103-
assertFailsWith<Throwable> { localTime(24, 0) }
104-
assertFailsWith<Throwable> { localTime(0, -1) }
105-
assertFailsWith<Throwable> { localTime(0, 60) }
106-
assertFailsWith<Throwable> { localTime(0, 0, -1) }
107-
assertFailsWith<Throwable> { localTime(0, 0, 60) }
108-
assertFailsWith<Throwable> { localTime(0, 0, 0, -1) }
109-
assertFailsWith<Throwable> { localTime(0, 0, 0, 1_000_000_000) }
108+
assertFailsWith<IllegalArgumentException> { localTime(-1, 0) }
109+
assertFailsWith<IllegalArgumentException> { localTime(24, 0) }
110+
assertFailsWith<IllegalArgumentException> { localTime(0, -1) }
111+
assertFailsWith<IllegalArgumentException> { localTime(0, 60) }
112+
assertFailsWith<IllegalArgumentException> { localTime(0, 0, -1) }
113+
assertFailsWith<IllegalArgumentException> { localTime(0, 0, 60) }
114+
assertFailsWith<IllegalArgumentException> { localTime(0, 0, 0, -1) }
115+
assertFailsWith<IllegalArgumentException> { localTime(0, 0, 0, 1_000_000_000) }
110116
}
111117

112118
}

core/commonTest/src/TimeZoneTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ class TimeZoneTest {
5353
assertEquals("Europe/Moscow", tzm.id)
5454
// TODO: Check known offsets from UTC for particular moments
5555

56-
// TODO: assert exception type?
57-
assertFails { TimeZone.of("Mars/Standard") }
56+
assertFailsWith<IllegalTimeZoneException> { TimeZone.of("Mars/Standard") }
57+
assertFailsWith<IllegalTimeZoneException> { TimeZone.of("UTC+X") }
5858

5959
}
6060

core/jsMain/src/Instant.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ public actual class Instant internal constructor(internal val value: jtInstant)
5959

6060
actual fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Long): Instant =
6161
Instant(jtInstant.ofEpochSecond(epochSeconds, nanosecondAdjustment))
62+
63+
internal actual val MIN: Instant = Instant(jtInstant.MIN)
64+
internal actual val MAX: Instant = Instant(jtInstant.MAX)
6265
}
6366
}
6467

core/jsMain/src/LocalDate.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ public actual class LocalDate internal constructor(internal val value: jtLocalDa
1313
public actual fun parse(isoString: String): LocalDate {
1414
return jtLocalDate.parse(isoString).let(::LocalDate)
1515
}
16+
17+
internal actual val MIN: LocalDate = LocalDate(jtLocalDate.MIN)
18+
internal actual val MAX: LocalDate = LocalDate(jtLocalDate.MAX)
1619
}
1720

1821
public actual constructor(year: Int, monthNumber: Int, dayOfMonth: Int) :

core/jsMain/src/LocalDateTime.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ public actual class LocalDateTime internal constructor(internal val value: jtLoc
4242
public actual fun parse(isoString: String): LocalDateTime {
4343
return jtLocalDateTime.parse(isoString).let(::LocalDateTime)
4444
}
45+
46+
internal actual val MIN: LocalDateTime = LocalDateTime(jtLocalDateTime.MIN)
47+
internal actual val MAX: LocalDateTime = LocalDateTime(jtLocalDateTime.MAX)
4548
}
4649

4750
}

0 commit comments

Comments
 (0)