Skip to content

Commit 8f4848d

Browse files
committed
Ensure UtcOffset.parse throws DateTimeFormatException
1 parent 130a0ec commit 8f4848d

File tree

4 files changed

+57
-38
lines changed

4 files changed

+57
-38
lines changed

core/common/test/UtcOffsetTest.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@ class UtcOffsetTest {
3333
val offsetSecondsRange = -18 * 60 * 60 .. +18 * 60 * 60
3434
}
3535

36+
@Test
37+
fun invalidUtcOffsetStrings() {
38+
for (v in invalidUtcOffsetStrings) {
39+
assertFailsWith<DateTimeFormatException>("Should fail: $v") { UtcOffset.parse(v) }
40+
}
41+
for (v in fixedOffsetTimeZoneIds) {
42+
assertFailsWith<DateTimeFormatException>("Time zone name should not be parsed as UtcOffset: $v") { UtcOffset.parse(v) }
43+
}
44+
}
45+
3646
@Test
3747
fun parseAllValidValues() {
3848
fun Int.pad() = toString().padStart(2, '0')

core/native/src/Instant.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ private val zoneOffsetParser: Parser<UtcOffset>
5757
UtcOffset.ofHoursMinutesSeconds(-hours, -minutes, -seconds)
5858
else
5959
UtcOffset.ofHoursMinutesSeconds(hours, minutes, seconds)
60-
} catch (e: IllegalTimeZoneException) {
60+
} catch (e: IllegalArgumentException) {
6161
throw DateTimeFormatException(e)
6262
}
6363
}

core/native/src/TimeZone.kt

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,32 @@ public actual open class TimeZone internal constructor(internal val value: TimeZ
2929
if (zoneId.length == 1) {
3030
throw IllegalTimeZoneException("Invalid zone ID: $zoneId")
3131
}
32-
if (zoneId.startsWith("+") || zoneId.startsWith("-")) {
33-
return UtcOffset.parse(zoneId).asTimeZone()
34-
}
35-
if (zoneId == "UTC" || zoneId == "GMT" || zoneId == "UT") {
36-
return FixedOffsetTimeZone(UtcOffset(0), zoneId)
37-
}
38-
if (zoneId.startsWith("UTC+") || zoneId.startsWith("GMT+") ||
39-
zoneId.startsWith("UTC-") || zoneId.startsWith("GMT-")) {
40-
val prefix = zoneId.take(3)
41-
val offset = UtcOffset.parse(zoneId.substring(3))
42-
return when (offset.totalSeconds) {
43-
0 -> FixedOffsetTimeZone(offset, prefix)
44-
else -> FixedOffsetTimeZone(offset, "$prefix$offset")
32+
try {
33+
if (zoneId.startsWith("+") || zoneId.startsWith("-")) {
34+
return UtcOffset.parse(zoneId).asTimeZone()
4535
}
46-
}
47-
if (zoneId.startsWith("UT+") || zoneId.startsWith("UT-")) {
48-
val offset = UtcOffset.parse(zoneId.substring(2))
49-
return when (offset.totalSeconds) {
50-
0 -> FixedOffsetTimeZone(offset, "UT")
51-
else -> FixedOffsetTimeZone(offset, "UT$offset")
36+
if (zoneId == "UTC" || zoneId == "GMT" || zoneId == "UT") {
37+
return FixedOffsetTimeZone(UtcOffset(0), zoneId)
38+
}
39+
if (zoneId.startsWith("UTC+") || zoneId.startsWith("GMT+") ||
40+
zoneId.startsWith("UTC-") || zoneId.startsWith("GMT-")
41+
) {
42+
val prefix = zoneId.take(3)
43+
val offset = UtcOffset.parse(zoneId.substring(3))
44+
return when (offset.totalSeconds) {
45+
0 -> FixedOffsetTimeZone(offset, prefix)
46+
else -> FixedOffsetTimeZone(offset, "$prefix$offset")
47+
}
48+
}
49+
if (zoneId.startsWith("UT+") || zoneId.startsWith("UT-")) {
50+
val offset = UtcOffset.parse(zoneId.substring(2))
51+
return when (offset.totalSeconds) {
52+
0 -> FixedOffsetTimeZone(offset, "UT")
53+
else -> FixedOffsetTimeZone(offset, "UT$offset")
54+
}
5255
}
56+
} catch (e: DateTimeFormatException) {
57+
throw IllegalTimeZoneException(e)
5358
}
5459
return TimeZone(PlatformTimeZoneImpl.of(zoneId))
5560
}

core/native/src/UtcOffset.kt

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -58,47 +58,51 @@ public actual class UtcOffset internal constructor(public actual val totalSecond
5858
minutes = parseNumber(offsetString, 4, true)
5959
seconds = parseNumber(offsetString, 7, true)
6060
}
61-
else -> throw IllegalTimeZoneException("Invalid ID for UtcOffset, invalid format: $offsetString")
61+
else -> throw DateTimeFormatException("Invalid ID for UtcOffset, invalid format: $offsetString")
6262
}
6363
val first: Char = offsetString[0]
6464
if (first != '+' && first != '-') {
65-
throw IllegalTimeZoneException(
65+
throw DateTimeFormatException(
6666
"Invalid ID for UtcOffset, plus/minus not found when expected: $offsetString")
6767
}
68-
return if (first == '-') {
69-
ofHoursMinutesSeconds(-hours, -minutes, -seconds)
70-
} else {
71-
ofHoursMinutesSeconds(hours, minutes, seconds)
68+
try {
69+
return if (first == '-') {
70+
ofHoursMinutesSeconds(-hours, -minutes, -seconds)
71+
} else {
72+
ofHoursMinutesSeconds(hours, minutes, seconds)
73+
}
74+
} catch (e: IllegalArgumentException) {
75+
throw DateTimeFormatException(e)
7276
}
7377
}
7478

7579
// org.threeten.bp.ZoneOffset#validate
7680
private fun validate(hours: Int, minutes: Int, seconds: Int) {
7781
if (hours < -18 || hours > 18) {
78-
throw IllegalTimeZoneException("Zone offset hours not in valid range: value " + hours +
79-
" is not in the range -18 to 18")
82+
throw IllegalArgumentException("Zone offset hours not in valid range: value " + hours +
83+
" is not in the range -18 to 18")
8084
}
8185
if (hours > 0) {
8286
if (minutes < 0 || seconds < 0) {
83-
throw IllegalTimeZoneException("Zone offset minutes and seconds must be positive because hours is positive")
87+
throw IllegalArgumentException("Zone offset minutes and seconds must be positive because hours is positive")
8488
}
8589
} else if (hours < 0) {
8690
if (minutes > 0 || seconds > 0) {
87-
throw IllegalTimeZoneException("Zone offset minutes and seconds must be negative because hours is negative")
91+
throw IllegalArgumentException("Zone offset minutes and seconds must be negative because hours is negative")
8892
}
8993
} else if (minutes > 0 && seconds < 0 || minutes < 0 && seconds > 0) {
90-
throw IllegalTimeZoneException("Zone offset minutes and seconds must have the same sign")
94+
throw IllegalArgumentException("Zone offset minutes and seconds must have the same sign")
9195
}
9296
if (abs(minutes) > 59) {
93-
throw IllegalTimeZoneException("Zone offset minutes not in valid range: abs(value) " +
94-
abs(minutes) + " is not in the range 0 to 59")
97+
throw IllegalArgumentException("Zone offset minutes not in valid range: abs(value) " +
98+
abs(minutes) + " is not in the range 0 to 59")
9599
}
96100
if (abs(seconds) > 59) {
97-
throw IllegalTimeZoneException("Zone offset seconds not in valid range: abs(value) " +
98-
abs(seconds) + " is not in the range 0 to 59")
101+
throw IllegalArgumentException("Zone offset seconds not in valid range: abs(value) " +
102+
abs(seconds) + " is not in the range 0 to 59")
99103
}
100104
if (abs(hours) == 18 && (abs(minutes) > 0 || abs(seconds) > 0)) {
101-
throw IllegalTimeZoneException("Zone offset not in valid range: -18:00 to +18:00")
105+
throw IllegalArgumentException("Utc offset not in valid range: -18:00 to +18:00")
102106
}
103107
}
104108

@@ -120,12 +124,12 @@ public actual class UtcOffset internal constructor(public actual val totalSecond
120124
// org.threeten.bp.ZoneOffset#parseNumber
121125
private fun parseNumber(offsetId: CharSequence, pos: Int, precededByColon: Boolean): Int {
122126
if (precededByColon && offsetId[pos - 1] != ':') {
123-
throw IllegalTimeZoneException("Invalid ID for ZoneOffset, colon not found when expected: $offsetId")
127+
throw DateTimeFormatException("Invalid ID for UtcOffset, colon not found when expected: $offsetId")
124128
}
125129
val ch1 = offsetId[pos]
126130
val ch2 = offsetId[pos + 1]
127131
if (ch1 < '0' || ch1 > '9' || ch2 < '0' || ch2 > '9') {
128-
throw IllegalTimeZoneException("Invalid ID for ZoneOffset, non numeric characters found: $offsetId")
132+
throw DateTimeFormatException("Invalid ID for UtcOffset, non numeric characters found: $offsetId")
129133
}
130134
return (ch1 - '0') * 10 + (ch2 - '0')
131135
}

0 commit comments

Comments
 (0)