How do I work with Date and Time?

← Getting Started · Ref: Q31

EK9 has built-in Date and Time types with literal syntax, calendar-safe arithmetic, and rich operators. No imports or third-party libraries needed.

DATE: Calendar Date Without Time
Literal syntax: 2024-01-15, 1971-02-01
Constructor forms: Date(2020, 10, 03) or Date(daysFromEpoch) or Date(stringValue)
Unset constructor: Date() creates an unset date (not today). Get today: Date().today() returns the current date.
Accessors: year(), month(), day(), dayOfMonth(), dayOfWeek(), dayOfYear()
Arithmetic with Duration:

  nextWeek <- birthday + P7D
  anniversary <- wedding + P1Y1M4D

Subtract two Dates to get a Duration:

  age <- today - birthday

This is calendar-safe: adding P1M to January 31st gives February 28th (or 29th in a leap year), not an invalid date.

TIME: Time of Day Without Date
Literal syntax: 09:30:00, 14:15, 12:00:01
Constructor forms: Time(12, 00) or Time(12, 00, 01)
Unset constructor: Time() creates an unset time. Time().now() returns the current time.
Convenience: Time().startOfDay() gives 00:00:00, Time().endOfDay() gives 23:59:59
Accessors: hour(), minute(), second()
Arithmetic with Duration:

  later <- morning + PT3H

Time wraps at 24 hours: adding PT3H to 22:00 gives 01:00, not 25:00.
Subtract two Times to get a Duration:

  workHours <- 14:15 - 09:30:00

COMPARISON AND TRI-STATE

Both types support ==, <>, <, >, <=, >=, <=>. Like all EK9 types, Date and Time can be unset (see Q29). An unset date is different from any specific date.

DateTime combines date, time, and timezone into a single type (see Q92). Duration (P1Y1M4D) and Millisecond (500ms) are built-in temporal span types (see Q32, Q41). All temporal types support locale-aware formatting via the Locale type (see Q44).

Use 'ek9 -h Date' and 'ek9 -h Time' to see the full API for each type.

See Q32 for duration. See Q92 for datetime operations. See Q536-Q556 for deep-dive date, time, timezone, and duration coverage.

Example

defines module qa.date.time

  defines program
    DateTimeDemo()
      stdout <- Stdout()

      // === DATE ===

      // Date literals (ISO 8601)
      birthday <- 1971-02-01
      wedding <- 2020-10-03
      stdout.println(`Birthday: ${birthday}`)
      stdout.println(`Wedding: ${wedding}`)

      // Constructor form
      sameWedding <- Date(2020, 10, 03)
      stdout.println(`Same wedding: ${sameWedding == wedding}`)

      // Date() is unset, Date().today() gets current date
      unsetDate <- Date()
      stdout.println(`Unset date isSet: ${unsetDate?}`)
      currentDate <- Date().today()
      stdout.println(`Today: ${currentDate}`)

      // Date accessors
      stdout.println(`Year: ${birthday.year()}, Month: ${birthday.month()}, Day: ${birthday.day()}`)
      stdout.println(`Day of week: ${birthday.dayOfWeek()}, Day of year: ${birthday.dayOfYear()}`)

      // Date + Duration arithmetic
      nextWeek <- birthday + P7D
      lastWeek <- birthday - P7D
      stdout.println(`Week after: ${nextWeek}, Week before: ${lastWeek}`)

      // Compound duration: one year, one month, four days
      anniversary <- wedding + P1Y1M4D
      stdout.println(`Anniversary + P1Y1M4D: ${anniversary}`)

      // Date - Date gives Duration
      gap <- anniversary - wedding
      stdout.println(`Gap: ${gap}`)

      // Compound assignment
      mutableDate <- 2024-01-15
      mutableDate += P2W
      stdout.println(`After += P2W: ${mutableDate}`)

      // === TIME ===

      // Time literals
      morning <- 09:30:00
      afternoon <- 14:15
      stdout.println(`Morning: ${morning}, Afternoon: ${afternoon}`)

      // Constructor forms
      noon <- Time(12, 00)
      precise <- Time(12, 00, 01)
      stdout.println(`Noon: ${noon}, Precise: ${precise}`)

      // Time() is unset
      unsetTime <- Time()
      stdout.println(`Unset time isSet: ${unsetTime?}`)

      // startOfDay and endOfDay
      dayStart <- Time().startOfDay()
      dayEnd <- Time().endOfDay()
      stdout.println(`Day start: ${dayStart}, Day end: ${dayEnd}`)

      // Time accessors
      stdout.println(`Hour: ${morning.hour()}, Minute: ${morning.minute()}, Second: ${morning.second()}`)

      // Time + Duration arithmetic
      later <- morning + PT3H
      earlier <- morning - PT1H
      stdout.println(`+3h: ${later}, -1h: ${earlier}`)

      // Time - Time gives Duration
      workHours <- afternoon - morning
      stdout.println(`Work hours: ${workHours}`)

      // Comparisons (use variables from function calls to avoid tautological conditions)
      if birthday < currentDate
        stdout.println("Birthday is in the past")
      currentTime <- Time().now()
      if morning < currentTime
        stdout.println("Morning is before current time")

Common mistakes

E50060 — EK9 built-in types use short method names without 'get' prefix. Date has year(), month(), day() not getYear(), getMonth(), getDay(). Triggers E50060 — method not resolved. See ek9 -h Date for the full API.

Incorrect:

birthday.getYear()

Correct:

birthday.year()
Other ways to ask this
  • What date and time types does EK9 have?
  • How do I use date literals in EK9?
  • How does date arithmetic work in EK9?

Coming from another language?

Java: java.util.Date (broken), java.time.LocalDate/LocalTime (Java 8), no literals, verbose factory methods. Python: datetime.date/datetime.time, no literals, limited arithmetic. Rust: no built-in, chrono crate required. Go: time.Time only, no separate Date/Time, bizarre reference date format. JavaScript: Date object notoriously broken (months 0-indexed, mutable), no Time type. EK9: Date and Time built-in with literal syntax, calendar-safe arithmetic, Duration interop, no imports needed.

Keywords: month, arithmetic, duration, day, start, second, beginner, first, year, literal, intro, today, date, calendar, accessor, hour, time, minute