How does the Millisecond type work in EK9 and how does it relate to Duration?

← Getting Started · Ref: Q41

Millisecond is EK9's dedicated type for precise timing and performance measurement using the ms suffix: 100ms, 500ms. Type-safe — cannot mix with incompatible types.

Arithmetic: +, -, multiply/divide by Integer or Float, Millisecond/Millisecond gives Float ratio. Conversion: .duration() converts to Duration (#^ also promotes). Construct from Duration: Millisecond(P2W).

Performance timing: SystemClock().millisecond() for start/end measurement.

Use Millisecond for precise timing/timeouts. Use Duration for calendar-scale spans.

Use 'ek9 -h Millisecond' to see the full API.

See Q32 for Duration. See Q31 for Date/Time. See Q44 for locale formatting.

Example

defines module qa.millisecond

  defines program
    MillisecondDemo()
      stdout <- Stdout()

      // === MILLISECOND LITERALS ===

      // Simple ms suffix on integers
      shortTimeout <- 100ms
      halfSecond <- 500ms
      oneSecond <- 1000ms
      stdout.println(`Short: ${shortTimeout}`)
      stdout.println(`Half second: ${halfSecond}`)
      stdout.println(`One second: ${oneSecond}`)

      // Negative milliseconds
      negMs <- -250ms
      stdout.println(`Negative: ${negMs}`)

      // Unset
      unsetMs <- Millisecond()
      require ~unsetMs?
      stdout.println(`Unset isSet: ${unsetMs?}`)

      // === ARITHMETIC ===

      // Add milliseconds together
      combined <- 100ms + 50ms
      stdout.println(`100ms + 50ms = ${combined}`)

      // Multiply by Float or Integer (scaling)
      scaled <- 100ms * 3.5
      stdout.println(`100ms * 3.5 = ${scaled}`)

      scaledInt <- 100ms * 5
      stdout.println(`100ms * 5 = ${scaledInt}`)

      // Divide by Integer or Float
      halved <- 1000ms / 2
      stdout.println(`1000ms / 2 = ${halved}`)

      // Divide Millisecond by Millisecond gives Float ratio
      ratio <- 750ms / 250ms
      stdout.println(`750ms / 250ms = ${ratio}`)

      // Increment and decrement
      ticker <- 100ms
      ticker++
      stdout.println(`After ++: ${ticker}`)
      ticker--
      stdout.println(`After --: ${ticker}`)

      // Negation
      pos <- 500ms
      neg <- -pos
      stdout.println(`Negated: ${neg}`)

      // Absolute value
      absVal <- abs neg
      require absVal == pos
      stdout.println(`abs: ${absVal}`)

      // === CONVERSION TO DURATION ===

      // duration() method converts Millisecond to Duration
      // Values >= 500ms round up to the nearest second
      fiveSeconds <- 5000ms
      asDuration <- fiveSeconds.duration()
      stdout.println(`5000ms as Duration: ${asDuration}`)

      // 501ms rounds up to 1 second
      nearlyASecond <- 501ms
      stdout.println(`501ms as Duration: ${nearlyASecond.duration()}`)

      // Negative milliseconds
      negDuration <- -501ms
      stdout.println(`-501ms as Duration: ${negDuration.duration()}`)

      // #^ promote operator also converts to Duration
      promoted <- #^ 3000ms
      stdout.println(`Promoted to Duration: ${promoted}`)

      // === CONSTRUCT FROM DURATION ===

      // Create Millisecond from a Duration literal
      twoWeeks <- Millisecond(P2W)
      stdout.println(`P2W as Millisecond: ${twoWeeks}`)

      fiveMinutes <- Millisecond(PT5M)
      stdout.println(`PT5M as Millisecond: ${fiveMinutes}`)

      // === ADD DURATION TO MILLISECOND ===

      base <- 5400ms
      withDuration <- base + PT1S
      stdout.println(`5400ms + PT1S = ${withDuration}`)

      // === PERFORMANCE TIMING ===

      starting <- SystemClock().millisecond()
      stdout.println("Do some work")
      ending <- SystemClock().millisecond()
      elapsed <- ending - starting
      stdout.println(`Elapsed: ${elapsed}`)

      // === COMPARISON ===

      require 100ms < 500ms
      require 500ms > 100ms
      require 100ms <> 500ms
      require 100ms == 100ms
      require 100ms <= 100ms
      require 100ms >= 100ms

      shortDelay <- 100ms
      ordering <- shortDelay <=> 500ms
      stdout.println(`100ms <=> 500ms: ${ordering}`)

      // === UNSET PROPAGATION ===

      unsetResult <- unsetMs + 100ms
      require ~unsetResult?
      stdout.println(`Unset + 100ms isSet: ${unsetResult?}`)

      // === HASHCODE ===

      hash <- #? 500ms
      stdout.println(`Hash of 500ms: ${hash}`)

Common mistakes

E50060 — EK9 Millisecond does not have toSeconds() or similar conversion methods. Use .duration() to convert to Duration, or use arithmetic operators. Triggers E50060 — method not resolved. See ek9 -h Millisecond for the full API.

Incorrect:

fiveSeconds.toSeconds()

Correct:

fiveSeconds.duration()
Other ways to ask this
  • How do I measure elapsed time in EK9?
  • What is the ms literal suffix in EK9?
  • How do I convert between Millisecond and Duration in EK9?
  • How do I set timeouts in EK9?

Coming from another language?

Java: System.currentTimeMillis() returns raw long. Python: time.time() returns float. Rust/Go: no ms literal. EK9: native ms literal, dedicated type, promotes to Duration via #^.

Keywords: timing, migrate, duration, delay, elapsed, clock, ms, promote, stopwatch, interval, millisecond, intro, first, beginner, timeout, performance, convert, start