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

#!ek9
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: add, arithmetic, associative, calculate, calendar, clamping, date, decrement, duration, increment, migrate, minus, month, pitfall, plus, subtract, summing, time, timezone