How do I add or subtract time from a date in EK9?

← Date, Time, and Duration · Ref: Q540

Use the + and - operators with Duration literals. EK9 supports both simple and compound assignment forms.

BASIC ARITHMETIC

  nextWeek <- 2024-01-15 + P7D
  lastMonth <- 2024-06-15 - P1M
  meetingEnd <- 2024-06-15T10:00:00Z + PT1H30M

COMPOUND ASSIGNMENT

  appointment <- 2024-03-01
  appointment += P2W
  appointment -= P3D

INCREMENT AND DECREMENT

  today <- Date().today()
  today++
  today--

For Date, ++ adds one day and -- subtracts one day.
For DateTime, ++ and -- also add/subtract one day.

THE DURATION SUMMING PITFALL

Date + Duration is CALENDAR month arithmetic: it adds real calendar months and clamps to the month end. Because every step clamps, applying P1M twice is not the same as applying P2M once:

  2024-01-31 + P1M gives 2024-02-29, clamped - February has no 31st
  2024-01-31 + P1M + P1M gives 2024-03-29, clamping to Feb 29 and then adding a month
  2024-01-31 + P2M gives 2024-03-31, one two-month calendar step

Month arithmetic on a Date is therefore NOT associative, exactly as in Java and Python.

A Duration on its own is flat: P1M + P1M re-normalises to P2M. So combine the durations first and apply them once:

  2024-01-31 + (P1M + P1M) gives 2024-03-31

Step month by month only when stepwise clamping is what you mean.

For a specific day, construct the Date from components (see Q550 for the deep dive).

MILLISECOND ARITHMETIC

You can also add Millisecond values:

  precise <- 2024-06-15T10:00:00Z + 5000ms

See Q32 for Duration literals. See Q539 for date subtraction. See Q550 for calendar edge cases and the full duration summing deep dive. See Q556 for Time wrapping.

Example

defines module qa.add.subtract.time

  defines program
    AddSubtractTimeDemo()
      stdout <- Stdout()

      // Basic date + duration
      start <- 2024-01-15
      nextWeek <- start + P7D
      lastMonth <- start - P1M
      stdout.println(`Start: ${start}`)
      stdout.println(`+ P7D: ${nextWeek}`)
      stdout.println(`- P1M: ${lastMonth}`)

      // Complex duration
      future <- start + P1Y6M10D
      stdout.println(`+ P1Y6M10D: ${future}`)

      // Compound assignment
      appointment <- 2024-03-01
      appointment += P2W
      stdout.println(`+ P2W: ${appointment}`)
      appointment -= P3D
      stdout.println(`- P3D: ${appointment}`)

      // Increment and decrement (one day)
      mutableDate <- 2024-06-15
      mutableDate++
      stdout.println(`After ++: ${mutableDate}`)
      mutableDate--
      stdout.println(`After --: ${mutableDate}`)

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

      // Duration summing: Date + Duration clamps at the month end at EVERY step
      // so base + P1M + P1M (Feb 29, then Mar 29) differs from base + P2M (Mar 31)
      base <- 2024-01-31
      sequential <- base + P1M + P1M
      combined <- base + P2M
      durationsFirst <- base + (P1M + P1M)
      stdout.println(`Sequential P1M + P1M: ${sequential}`)
      stdout.println(`Combined P2M: ${combined}`)
      stdout.println(`Durations first (P1M + P1M): ${durationsFirst}`)

      // Millisecond arithmetic
      precise <- 2024-06-15T10:00:00Z + 5000ms
      stdout.println(`+ 5000ms: ${precise}`)

      // Time arithmetic (wraps at 24h - see Q556)
      evening <- 22:00
      lateNight <- evening + PT3H
      stdout.println(`22:00 + PT3H: ${lateNight}`)

Common mistakes

E50060 — Date has no plusDays() method. EK9 uses the + operator with Duration literals (P7D for 7 days) for date arithmetic. See ek9 -h E50060 for details.

Incorrect:

nextWeek <- start.plusDays(7)

Correct:

nextWeek <- start + P7D
Other ways to ask this
  • How does date arithmetic work with Duration?
  • How do I add days or months to a date?
  • What is the duration summing pitfall?

Coming from another language?

Java: date.plusDays(7), date.plusMonths(1) calendar-safe but non-associative (Jan 31 + 1 month + 1 month differs from Jan 31 + 2 months). Python: timedelta(days=7) for days only, relativedelta for months (same non-associativity). JavaScript: manual setDate/setMonth (notoriously broken). Go: t.AddDate(0, 1, 0) calendar-safe but non-associative. EK9: date + P7D; Date + Duration is calendar-aware and clamps at the month end like Java, so it is not associative, while Duration + Duration is flat (P1M + P1M = P2M): combine the durations first, then apply once.

Keywords: duration, associative, calculate, migrate, summing, add, month, calendar, subtract, arithmetic, pitfall, increment, minus, date, clamping, plus, timezone, decrement, time