CoolMind
CoolMind

Reputation: 28793

How to get the first value from a list without exceptions in Kotlin?

I have a date value in format "2021-07-14T13:00:00.000+0300" (or similar). I want to convert it to Date. In this case I have to traverse a loop of different formats and check if they fail.

import java.text.*
import java.util.*

val formats = listOf(
    "yyyy-MM-dd'T'HH:mm:ss.SSSZ",
    "dd.MM.yyyy, EEEE, HH:mm" // And many others.
)

val date = "2021-07-14T13:00:00.000+0300"
val locale = Locale.getDefault()

for (format in formats) {
    try {
        return SimpleDateFormat(format, locale).parse(date)
    } catch (e: ParseException) {
    }
}
// If nothing found, return current date.
return Date()

How to convert this for-loop to something like map? So that we can get the first value without exception?

val result = formats.map { ... }

Upvotes: 1

Views: 482

Answers (2)

aneroid
aneroid

Reputation: 15962

Another option, while still using firstNotNullOfOrNull(), is to use parse() with a ParsePosition object whose properties you can safely ignore when combined with setLenient(false)*.

The advantage of the parse​(String, ParsePosition) version over parse​(String) is that it returns null when it can't parse the date, instead of throwing an error, so the try-catch overhead per iteration can be avoided.

Along with that, since you're defaulting to the current date if all formats fail, you can avoid the nullable Date type result with an Elvis op at the very end.

val result: Date = formats.firstNotNullOfOrNull { format ->
    with (SimpleDateFormat(format, locale)) {
        setLenient(false)  // may not be required, see below
        parse(date, ParsePosition(0))  // is null or Date
    }
} ?: Date()

Btw, setLenient(false) may not be required because on v15, there's no leniency for SimpleDateFormat.parse() in the docs...but it does behave leniently. Setting it to true above or leaving it out, and parsing a date of "2021-07-14T53:00:00.000+0300" (note the '53') produced Fri Jul 16 02:00:00 UTC 2021. With no leniency, it produces null. The leniency is mentioned on the abstract base class DateFormat.parse(String, ParsePosition) but not for SimpleDateFormat.parse(String, ParsePosition).

So if you're expecting non-pattern-matching dates rather than invalid-but-pattern-matching dates, the above loop could be reduced to:

val result: Date = formats.firstNotNullOfOrNull { format ->
    SimpleDateFormat(format, locale).parse(date, ParsePosition(0))
} ?: Date()

Upvotes: 2

CoolMind
CoolMind

Reputation: 28793

Use firstNotNullOfOrNull().

val result: Date? = formats.firstNotNullOfOrNull { format ->
    try {
        SimpleDateFormat(format, locale).parse(date)
    } catch (e: ParseException) {
        null
    }
}

Upvotes: 1

Related Questions