@@ -5,6 +5,7 @@ import kotlinx.datetime.LocalDate
5
5
import kotlinx.datetime.LocalDateTime
6
6
import kotlinx.datetime.LocalTime
7
7
import kotlinx.datetime.format.DateTimeComponents
8
+ import kotlinx.datetime.toKotlinInstant
8
9
import kotlinx.datetime.toKotlinLocalDate
9
10
import kotlinx.datetime.toKotlinLocalDateTime
10
11
import kotlinx.datetime.toKotlinLocalTime
@@ -163,6 +164,32 @@ internal object Parsers : GlobalParserOptions {
163
164
return null
164
165
}
165
166
167
+ private fun String.toInstantOrNull (): Instant ? {
168
+ // Default format used by Instant.parse
169
+ val format = DateTimeComponents .Formats .ISO_DATE_TIME_OFFSET
170
+ return catchSilent {
171
+ // low chance throwing exception, thanks to using parseOrNull instead of parse
172
+ format.parseOrNull(this )?.toInstantUsingOffset()
173
+ }
174
+ // fallback on the java instant to catch things like "2022-01-23T04:29:60", a.k.a. leap seconds
175
+ ? : toJavaInstantOrNull()?.toKotlinInstant()
176
+ }
177
+
178
+ private fun String.toJavaInstantOrNull (): JavaInstant ? {
179
+ // Default format used by java.time.Instant.parse
180
+ val format = DateTimeFormatter .ISO_INSTANT
181
+ return catchSilent {
182
+ // low chance throwing exception, thanks to using parseUnresolved instead of parse
183
+ val parsePosition = ParsePosition (0 )
184
+ val accessor = format.parseUnresolved(this , parsePosition)
185
+ if (accessor != null && parsePosition.errorIndex == - 1 ) {
186
+ JavaInstant .from(accessor)
187
+ } else {
188
+ null
189
+ }
190
+ }
191
+ }
192
+
166
193
private fun String.toLocalDateTimeOrNull (formatter : DateTimeFormatter ? ): LocalDateTime ? =
167
194
toJavaLocalDateTimeOrNull(formatter)?.toKotlinLocalDateTime()
168
195
@@ -276,13 +303,19 @@ internal object Parsers : GlobalParserOptions {
276
303
// Long
277
304
stringParser<Long > { it.toLongOrNull() },
278
305
// kotlinx.datetime.Instant
279
- stringParser<Instant >(catch = true ) {
280
- // same as Instant.parse(it), but with one fewer potential exception thrown/caught
281
- val format = DateTimeComponents .Formats .ISO_DATE_TIME_OFFSET
282
- format.parse(it).toInstantUsingOffset()
306
+ stringParser<Instant > {
307
+ it.toInstantOrNull()
283
308
},
309
+ // stringParser<Instant>(true) {
310
+ // Instant.parse(it)
311
+ // }, // TODO remove
284
312
// java.time.Instant, will be skipped if kotlinx.datetime.Instant is already checked
285
- stringParser<JavaInstant >(catch = true , coveredBy = setOf (typeOf<Instant >())) { JavaInstant .parse(it) },
313
+ stringParser<JavaInstant >(coveredBy = setOf (typeOf<Instant >())) {
314
+ it.toJavaInstantOrNull()
315
+ },
316
+ // stringParser<JavaInstant>(catch = true /*coveredBy = setOf(typeOf<Instant>())*/) {
317
+ // JavaInstant.parse(it)
318
+ // }, // TODO remove
286
319
// kotlinx.datetime.LocalDateTime
287
320
stringParserWithOptions<LocalDateTime > { options ->
288
321
val formatter = options?.getDateTimeFormatter()
@@ -308,9 +341,19 @@ internal object Parsers : GlobalParserOptions {
308
341
parser
309
342
},
310
343
// kotlin.time.Duration
311
- stringParser<Duration > { it.toDurationOrNull() },
344
+ stringParser<Duration > {
345
+ it.toDurationOrNull()
346
+ },
347
+ // stringParser<Duration>(true) {
348
+ // Duration.parse(it)
349
+ // }, // TODO remove
312
350
// java.time.Duration, will be skipped if kotlin.time.Duration is already checked
313
- stringParser<JavaDuration >(coveredBy = setOf (typeOf<Duration >())) { it.toJavaDurationOrNull() },
351
+ stringParser<JavaDuration >(coveredBy = setOf (typeOf<Duration >())) {
352
+ it.toJavaDurationOrNull()
353
+ },
354
+ // stringParser<JavaDuration>(true/*coveredBy = setOf(typeOf<Duration>())*/) {
355
+ // JavaDuration.parse(it)
356
+ // }, // TODO remove
314
357
// kotlinx.datetime.LocalTime
315
358
stringParserWithOptions<LocalTime > { options ->
316
359
val formatter = options?.getDateTimeFormatter()
0 commit comments