Explain Duration.parse, parseOrNull, and toIsoString. What string formats are accepted and what are the failure modes?
answer
- toIsoString -> 'PT1H30M'; zero is 'PT0S'
- parse is lenient: ISO + human '1h 30m'; throws on bad
- parseOrNull returns null instead of throwing
- parseIsoString(OrNull) accepts ONLY strict ISO
- No calendar months/years; M in time part = minutes
basics
~10 stoIsoString turns a duration into an ISO-8601 string like PT1H23M45S. Duration.parse reads such strings back, plus Kotlin's own format like '1h 23m'. parse throws on bad input; parseOrNull returns null instead.
solid answer
~40 stoIsoString() emits ISO-8601 duration syntax: a leading PT (or P...T) with components, e.g. 90.minutes.toIsoString() == "PT1H30M"; zero is "PT0S" and infinite produces a very large value, not a special token. Duration.parse(String) is lenient: it accepts both the ISO-8601 form (PT1H30M, P1DT2H) AND Kotlin's default human format ("1h 30m", "1d 5s"). On malformed input it throws IllegalArgumentException. Duration.parseOrNull(String) returns Duration? (null on failure) for non-throwing parsing. There is also parseIsoString / parseIsoStringOrNull which accept ONLY the strict ISO form and reject the human format. Note ISO-8601 weeks/months/years (the date part like P1Y or P1M month) are not meaningful for a fixed duration; kotlin.time works with days/hours/minutes/seconds. Round-trip toIsoString() -> parse is stable.
code
kotlin · 12 linesimport kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes
fun main() {
val d = 90.minutes
val iso = d.toIsoString() // "PT1H30M"
println(iso)
println(Duration.parse(iso)) // 1h 30m
println(Duration.parse("1h 30m")) // 1h 30m (human form)
println(Duration.parseOrNull("bad")) // null
println(Duration.parse(iso) == d) // true (round-trip)
}go deeper
Knows toIsoString produces a string and parse reads one back.
Knows parse throws while parseOrNull returns null, and recognizes the PT...H...M...S shape.
Distinguishes lenient parse (ISO + human) from strict parseIsoString, handles failure modes, and knows round-trip stability.
Designs an interchange/serialization policy (which form for JSON vs logs, which parser at which boundary) and knows the calendar-vs-fixed-span limitation.
## Formatting: toIsoString() `toIsoString()` renders a `Duration` as an **ISO-8601** duration string: ```kotlin import kotlin.time.Duration.Companion.minutes 90.minutes.toIsoString() // "PT1H30M" 0.minutes.toIsoString() // "PT0S" (25.hours).toIsoString() // "PT25H" (hours not rolled into days here) ``` Form: optional `P` date part (days) then `T` time part (`H`/`M`/`S`), e.g. `P1DT2H3M4S`. Fractional seconds appear as a decimal: `1.5.seconds.toIsoString()` -> `"PT1.5S"`. ## Parsing — two strictness levels ### Lenient: `Duration.parse(String): Duration` Accepts **both** notations: - ISO-8601: `"PT1H30M"`, `"P1DT2H"`, `"PT1.5S"`. - Kotlin **default/human** format (what `toString()` prints): `"1h 30m"`, `"1d 5s"`, `"1.5s"`. Throws `IllegalArgumentException` on malformed input. ### Non-throwing: `Duration.parseOrNull(String): Duration?` Same lenient grammar but returns `null` instead of throwing — ideal for user/config input. ### Strict ISO only: `parseIsoString` / `parseIsoStringOrNull` Accept **only** the ISO-8601 form and reject the human `"1h 30m"` style. Use these when you must guarantee interchange-standard input. ```kotlin import kotlin.time.Duration Duration.parse("PT1H30M") // 1h 30m Duration.parse("1h 30m") // 1h 30m (human form OK here) Duration.parseOrNull("oops") // null Duration.parseIsoString("PT1H30M") // 1h 30m // Duration.parseIsoString("1h 30m") -> throws (human form rejected) ``` ## Failure modes & gotchas - **parse vs parseOrNull**: choose `parseOrNull` at trust boundaries to avoid try/catch. - **IllegalArgumentException** message names the offending input; don't swallow it silently. - **No calendar months/years**: ISO `P1M`-as-month or `P1Y` are not representable as a fixed `Duration` — kotlin.time is days-and-below. (`M` in the *time* part means minutes.) - **Infinite**: `Duration.INFINITE.toIsoString()` yields a huge finite-looking value, not an `INF` token. - **Round-trip stability**: `Duration.parse(d.toIsoString()) == d` holds for finite durations. ## When to use which Emit `toIsoString()` for machine interchange/JSON; emit default `toString()` for logs/humans; parse back with `parseOrNull` (lenient) or `parseIsoStringOrNull` (strict) depending on what producers you must accept.
- Which API would you use to reject the human '1h 30m' form and accept only ISO-8601?parseIsoString (throwing) or parseIsoStringOrNull (null-returning). Plain Duration.parse is lenient and accepts both forms.
- Why can't kotlin.time parse 'P1Y' (one year) into a meaningful Duration?A year/month has no fixed length on a calendar, so it isn't a fixed time span. kotlin.time models days-and-below; calendar arithmetic belongs to kotlinx-datetime/java.time.
saying these in an interview costs you the question
- Claiming Duration.parse only accepts ISO and rejects '1h 30m'
- Thinking parseOrNull throws (it returns null)
- Believing kotlin.time can represent calendar months/years as a Duration
- Expecting an 'INF' token from toIsoString for INFINITE