How do I work with Duration?

← Getting Started · Ref: Q32

Duration represents time spans using ISO 8601 literals. Starts with P, date components Y/M/W/D, T separates time H/M/S. M means months before T, minutes after T.

EXAMPLES

  P1Y6M15D one year six months fifteen days, PT1H30M one hour thirty minutes, P3W three weeks (=P21D).

Arithmetic: +, -, */by Integer or Float, compound assignment. Accessors: .years(), .months(), .days(), .hours(), .minutes(), .seconds(). Comparison: ==, <>, <, >, <=, >=, <=>.

Use 'ek9 -h Duration' for the full API.

See Q31 for Date/Time. See Q41 for Millisecond. See Q44 for locale formatting. See Q539 for date differences.

Example

defines module qa.duration

  defines program
    DurationDemo()
      stdout <- Stdout()

      // === ISO 8601 DURATION FORMAT EXPLAINED ===

      // The 'P' prefix means "Period" - every duration starts with P
      // After P, date components: Y=years, M=months, W=weeks, D=days
      // The 'T' separator introduces time components: H=hours, M=minutes, S=seconds
      // Note: M means months BEFORE the T, and minutes AFTER the T

      // Date-only durations (no T needed)
      oneYear <- P1Y
      twoMonths <- P2M
      tenDays <- P10D
      threeWeeks <- P3W
      stdout.println(`Year: ${oneYear}, Months: ${twoMonths}, Days: ${tenDays}, Weeks: ${threeWeeks}`)

      // Weeks convert to days: P3W equals P21D
      require threeWeeks == P21D

      // Time-only durations (PT prefix - P then T then time components)
      oneHour <- PT1H
      thirtyMinutes <- PT30M
      fortyFiveSeconds <- PT45S
      stdout.println(`Hour: ${oneHour}, Minutes: ${thirtyMinutes}, Seconds: ${fortyFiveSeconds}`)

      // Combined time: PT1H30M15S = 1 hour, 30 minutes, 15 seconds
      combinedTime <- PT1H30M15S
      stdout.println(`Combined time: ${combinedTime}`)

      // Combined date: P1Y6M15D = 1 year, 6 months, 15 days
      combinedDate <- P1Y6M15D
      stdout.println(`Combined date: ${combinedDate}`)

      // Full combined: P2Y3M10DT4H30M = 2 years, 3 months, 10 days, 4 hours, 30 minutes
      // The T separates date parts from time parts
      fullCombined <- P2Y3M10DT4H30M
      stdout.println(`Full: ${fullCombined}`)

      // Complex with weeks: P2Y6W3DT8H5M8S
      // = 2 years, 6 weeks, 3 days (=45 days), 8 hours, 5 minutes, 8 seconds
      withWeeks <- P2Y6W3DT8H5M8S
      stdout.println(`With weeks: ${withWeeks}`)

      // === COMPONENT ACCESSORS ===

      d1 <- P2Y3M15DT4H30M45S
      stdout.println(`Years: ${d1.years()}, Months: ${d1.months()}, Days: ${d1.days()}`)
      stdout.println(`Hours: ${d1.hours()}, Minutes: ${d1.minutes()}, Seconds: ${d1.seconds()}`)

      // === DURATION ARITHMETIC ===

      // Addition and subtraction
      morning <- PT2H30M
      afternoon <- PT4H15M
      totalWork <- morning + afternoon
      difference <- afternoon - morning
      stdout.println(`Total: ${totalWork}, Difference: ${difference}`)

      // Multiply by Integer or Float
      doubled <- PT1H * 2
      tripled <- PT1H * 3
      scaled <- PT1H * 1.5
      stdout.println(`Doubled: ${doubled}, Tripled: ${tripled}, Scaled: ${scaled}`)

      // Divide by Integer or Float
      halved <- PT2H / 2
      thirded <- PT3H / 3
      stdout.println(`Halved: ${halved}, Thirded: ${thirded}`)

      // Compound assignment operators
      accumulator <- PT1H
      accumulator += PT30M
      require accumulator == PT1H30M
      accumulator -= PT15M
      require accumulator == PT1H15M
      accumulator *= 2
      require accumulator == PT2H30M
      accumulator /= 2
      require accumulator == PT1H15M
      stdout.println(`After compound ops: ${accumulator}`)

      // Negation
      positive <- PT5H
      negative <- -positive
      stdout.println(`Positive: ${positive}, Negative: ${negative}`)

      // === NEGATIVE DURATIONS ===

      // Subtracting a later time from an earlier time gives a negative duration
      negDur <- 06:15:12 - 12:04:09
      stdout.println(`Negative: ${negDur}`)
      require negDur == PT-5H-48M-57S

      // === DURATION FROM STRING ===

      // Construct from a string representation
      fromString <- Duration("P1Y6M")
      stdout.println(`From string: ${fromString}`)

      // Convert to string with $ operator
      asString <- $fullCombined
      stdout.println(`As string: ${asString}`)
      roundTrip <- Duration(asString)
      require roundTrip == fullCombined

      // === COMPARISON ===

      short <- PT30M
      long <- PT2H
      require short < long
      require long > short
      require short <> long
      require PT1H == PT1H
      require PT30M <= PT1H
      require PT2H >= PT1H
      stdout.println(`30min < 2h: ${short < long}`)

      // === UNSET DURATION ===

      unset <- Duration()
      require ~unset?
      stdout.println(`Unset duration isSet: ${unset?}`)

      // === USING DURATIONS WITH DATES AND TIMES ===

      // Date arithmetic with complex durations
      startDate <- 2020-01-15
      later <- startDate + P1Y6M10D
      stdout.println(`Date + P1Y6M10D: ${later}`)

      earlier <- startDate - P2M
      stdout.println(`Date - P2M: ${earlier}`)

      // Time arithmetic with wrapping
      startTime <- 22:00
      wrapped <- startTime + PT3H
      stdout.println(`22:00 + PT3H wraps to: ${wrapped}`)

      // DateTime arithmetic
      meeting <- 2024-06-15T10:30:00Z
      meetingEnd <- meeting + PT1H30M
      stdout.println(`Meeting ends: ${meetingEnd}`)

      // Subtracting temporal values produces a Duration
      dateGap <- 2024-12-25 - 2024-01-01
      stdout.println(`Days until Christmas: ${dateGap}`)

      timeGap <- 17:30 - 09:00
      stdout.println(`Work day: ${timeGap}`)

      // === MILLISECOND CONVERSION ===

      // Millisecond to Duration
      timeout <- 5400ms
      asDuration <- timeout.duration()
      stdout.println(`5400ms as duration: ${asDuration}`)

      // Duration to Millisecond
      fromDuration <- Millisecond(P2W)
      stdout.println(`P2W as millis: ${fromDuration}`)

Common mistakes

E50060 — EK9 Duration uses short method names: hours(), minutes(), seconds() not getHours(), getMinutes(), getSeconds(). Triggers E50060 — method not resolved. See ek9 -h Duration for the full API.

Incorrect:

d1.getHours()

Correct:

d1.hours()
Other ways to ask this
  • What is the ISO 8601 duration format?
  • How do Duration literals work in EK9?
  • How do I represent time spans in EK9?
  • What does PT1H30M mean?

Coming from another language?

Java: verbose Duration.ofHours()/Period split. Python: timedelta, no months/years. Go/Rust/JS: no duration literals. EK9: ISO 8601 PT1H30M literals, unified type years through seconds.

Keywords: start, period, months, weeks, first, days, format, 8601, years, timing, duration, interval, minutes, millisecond, iso, intro, timespan, hours, literal, beginner, seconds, arithmetic