Skip to content

Commit f864b19

Browse files
committed
Add more error cases
1 parent 9a596b1 commit f864b19

File tree

2 files changed

+57
-23
lines changed

2 files changed

+57
-23
lines changed

core/native/src/Instant.kt

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,6 @@ private class UnboundedLocalDateTime(
164164
val nanosecond: Int,
165165
) {
166166
fun toInstant(offsetSeconds: Int): Instant {
167-
fun isLeapYear(year: Int): Boolean =
168-
year and 3 == 0 && (year % 100 != 0 || year % 400 == 0)
169167
val epochSeconds = run {
170168
// org.threeten.bp.LocalDate#toEpochDay
171169
val epochDays = run {
@@ -252,9 +250,8 @@ internal fun parseIso(isoString: String): Instant {
252250
val s = isoString
253251
var i = 0
254252
require(s.isNotEmpty()) { "An empty string is not a valid Instant" }
255-
val yearSign = when (s[i]) {
256-
'+' -> { ++i; '+' }
257-
'-' -> { ++i; '-' }
253+
val yearSign = when (val c = s[i]) {
254+
'+', '-' -> { ++i; c }
258255
else -> ' '
259256
}
260257
val yearStart = i
@@ -275,7 +272,7 @@ internal fun parseIso(isoString: String): Instant {
275272
parseFailure("The '+' sign at the start is only valid for year numbers longer than 4 digits")
276273
}
277274
if (yearSign == ' ' && i - yearStart != 4) {
278-
parseFailure("The '+' sign is required for year numbers longer than 4 digits")
275+
parseFailure("A '+' or '-' sign is required for year numbers longer than 4 digits")
279276
}
280277
if (yearSign == '-') -absYear else absYear
281278
}
@@ -307,10 +304,10 @@ internal fun parseIso(isoString: String): Instant {
307304
fraction = fraction * 10 + (s[i] - '0')
308305
++i
309306
}
310-
if (i <= fractionStart + 9) {
307+
if (i - fractionStart in 1..9) {
311308
fraction * POWERS_OF_TEN[fractionStart + 9 - i]
312309
} else {
313-
parseFailure("At most 9 digits are supported for the fraction of the second, got {i - fractionStart}")
310+
parseFailure("1..9 digits are supported for the fraction of the second, got {i - fractionStart}")
314311
}
315312
} else {
316313
i += 15

core/native/test/InstantIsoStringsTest.kt

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,50 @@ class InstantIsoStringsTest {
3131
Triple("1970-01-02T01:01:01.100000000Z", 90061, 100000000))
3232
instants.forEach {
3333
val (str, seconds, nanos) = it
34-
val instant = parseIso(str)
34+
val instant = parseInstant(str)
3535
assertEquals(seconds.toLong() * 1000 + nanos / 1000000, instant.toEpochMilliseconds())
3636
}
3737

38-
assertInvalidFormat { parseIso("1970-01-01T23:59:60Z")}
39-
assertInvalidFormat { parseIso("1970-01-01T24:00:00Z")}
40-
assertInvalidFormat { parseIso("1970-01-01T23:59Z")}
41-
assertInvalidFormat { parseIso("x") }
42-
assertInvalidFormat { parseIso("12020-12-31T23:59:59.000000000Z") }
38+
for (nonIsoString in listOf(
39+
"",
40+
"x",
41+
" 1970-01-01T00:00:00Z",
42+
"+1234567890-01-01T00:00:00Z",
43+
"-1234567890-01-01T00:00:00Z",
44+
"003-01-01T00:00:00Z",
45+
"-003-01-01T00:00:00Z",
46+
"+1970-01-01T00:00:00Z",
47+
"11970-01-01T00:00:00Z",
48+
"1970-01-01T00:00:00Z",
49+
"1970/01-01T00:00:00Z",
50+
"1970-01/01T00:00:00Z",
51+
"1970-01-01 00:00:00Z",
52+
"1970-01-01T00-00:00Z",
53+
"1970-01-01T00:00-00Z",
54+
"1970-X1-01T00:00:00Z",
55+
"1970-1X-01T00:00:00Z",
56+
"1970-11-X1T00:00:00Z",
57+
"1970-11-1XT00:00:00Z",
58+
"1970-11-10T00:00:00Z",
59+
"1970-11-10TX0:00:00Z",
60+
"1970-11-10T0X:00:00Z",
61+
"1970-11-10T00:X0:00Z",
62+
"1970-11-10T00:0X:00Z",
63+
"1970-11-10T00:00:X0Z",
64+
"1970-11-10T00:00:0XZ",
65+
"1970-11-10T00:00:0٩Z",
66+
"1970-11-10T00:00:00.Z",
67+
"1970-11-10T00:00:00.1234567890Z",
68+
)) {
69+
assertInvalidFormat(nonIsoString) { parseInstant(nonIsoString) }
70+
}
71+
assertInvalidFormat { parseInstant("1970-01-01T23:59:60Z")}
72+
assertInvalidFormat { parseInstant("1970-01-01T24:00:00Z")}
73+
assertInvalidFormat { parseInstant("1970-01-01T23:59Z")}
74+
assertInvalidFormat { parseInstant("x") }
75+
assertInvalidFormat { parseInstant("12020-12-31T23:59:59.000000000Z") }
4376
// this string represents an Instant that is currently larger than Instant.MAX any of the implementations:
44-
assertInvalidFormat { parseIso("+1000000001-12-31T23:59:59.000000000Z") }
77+
assertInvalidFormat { parseInstant ("+1000000001-12-31T23:59:59.000000000Z") }
4578
}
4679

4780
@Test
@@ -54,16 +87,16 @@ class InstantIsoStringsTest {
5487
Pair("2020-01-01T00:01:01+00", "2020-01-01T00:01:01Z"),
5588
)
5689
strings.forEach { (str, strInZ) ->
57-
val instant = parseIso(str)
58-
assertEquals(parseIso(strInZ), instant, str)
90+
val instant = parseInstant(str)
91+
assertEquals(parseInstant(strInZ), instant, str)
5992
assertEquals(strInZ, formatIso(instant), str)
6093
}
61-
assertInvalidFormat { parseIso("2020-01-01T00:01:01+18:01") }
62-
assertInvalidFormat { parseIso("2020-01-01T00:01:01+1801") }
63-
assertInvalidFormat { parseIso("2020-01-01T00:01:01+0") }
64-
assertInvalidFormat { parseIso("2020-01-01T00:01:01+") }
65-
assertInvalidFormat { parseIso("2020-01-01T00:01:01") }
66-
assertInvalidFormat { parseIso("2020-01-01T00:01:01+000000") }
94+
assertInvalidFormat { parseInstant("2020-01-01T00:01:01+18:01") }
95+
assertInvalidFormat { parseInstant("2020-01-01T00:01:01+1801") }
96+
assertInvalidFormat { parseInstant("2020-01-01T00:01:01+0") }
97+
assertInvalidFormat { parseInstant("2020-01-01T00:01:01+") }
98+
assertInvalidFormat { parseInstant("2020-01-01T00:01:01") }
99+
assertInvalidFormat { parseInstant("2020-01-01T00:01:01+000000") }
67100

68101
val instants = listOf(
69102
Instant.DISTANT_FUTURE,
@@ -93,6 +126,10 @@ class InstantIsoStringsTest {
93126
94127
*/
95128
}
129+
130+
private fun parseInstant(isoString: String): Instant {
131+
return parseIso(isoString)
132+
}
96133
}
97134

98135

0 commit comments

Comments
 (0)