Clock
Every notion of “now” and every delay in Effect flows through the Clock
service. Schedules, Effect.sleep, Effect.timeout, and retries all ask the
Clock what time it is and to wait — none of them touch Date.now or
setTimeout directly. That single indirection is what makes time in Effect
deterministic and testable: swap the live clock for a TestClock and you control
the passage of time exactly.
import { Clock, Effect } from "effect"
// Measure how long a piece of work takes, reading time from the Clock service.const timed = Effect.gen(function* () { const start = yield* Clock.currentTimeMillis yield* doWork const end = yield* Clock.currentTimeMillis yield* Effect.log(`work took ${end - start}ms`)})
declare const doWork: Effect.Effect<void>Clock.currentTimeMillis and Clock.currentTimeNanos are effects that yield the
current time at millisecond or nanosecond precision. Reaching for Date.now()
would read the wall clock directly, bypassing the runtime and making the code
untestable — always read time through Clock. (For a richer, typed timestamp see
DateTime and DateTime.now, which itself
reads the Clock.)
Sleeping
Section titled “Sleeping”Effect.sleep suspends the current fiber for a duration. It is interruptible and
implemented on top of the Clock, so it cooperates with concurrency and with the
TestClock:
import { Effect } from "effect"
const program = Effect.gen(function* () { yield* Effect.log("starting") yield* Effect.sleep("2 seconds") // accepts any Duration.Input yield* Effect.log("two seconds later")})Unlike a blocking setTimeout, a sleeping fiber consumes no thread and can be
interrupted — if the surrounding scope is cancelled mid-sleep, the wait ends
immediately. See Concurrency for how fibers and interruption fit
together.
Accessing the full service
Section titled “Accessing the full service”The accessors above cover most needs. When you want the whole service — for
example to call the synchronous currentTimeMillisUnsafe() inside a tight,
already-effectful loop — read the Clock reference or use Clock.clockWith:
import { Clock, Effect } from "effect"
const program = Effect.gen(function* () { const clock = yield* Clock.Clock // Synchronous read; only valid because we already hold the service. return clock.currentTimeMillisUnsafe()})Controlling time in tests
Section titled “Controlling time in tests”The real value of routing time through a service shows up in tests. With
@effect/vitest, it.effect runs your effect against a TestClock whose time
only advances when you tell it to. TestClock.adjust moves the clock forward and
runs anything scheduled to fire on or before the new time — so a five-second
sleep “completes” the instant you advance the clock five seconds, with no real
waiting.
import { assert, describe, it } from "@effect/vitest"import { Effect, Ref } from "effect"import { TestClock } from "effect/testing"
describe("polling", () => { it.effect("ticks once per interval", () => Effect.gen(function* () { const ticks = yield* Ref.make(0)
// Fork a worker that increments a counter every second, forever. yield* Ref.update(ticks, (n) => n + 1).pipe( Effect.delay("1 second"), Effect.forever, Effect.forkChild )
// No real time passes — we drive the clock by hand. yield* TestClock.adjust("1 second") assert.strictEqual(yield* Ref.get(ticks), 1)
yield* TestClock.adjust("3 seconds") assert.strictEqual(yield* Ref.get(ticks), 4) }))})The same technique verifies retry backoff, cron jobs, and timeouts in
microseconds rather than real wall-clock time. To advance to an absolute moment
instead of by a delta, use TestClock.setTime(epochMillis).
TestClock is its own module with more (make, layer, withLive,
testClockWith); the page above is just the common case. For the full reference,
see TestClock.
Reference
Section titled “Reference”The Clock module is small: three effect-level accessors and the service itself.
Everything below imports from the core effect package.
Clock.currentTimeMillis
Section titled “Clock.currentTimeMillis”An Effect<number> that succeeds with the current time in milliseconds since the
Unix epoch, read from the active Clock. This is the everyday way to ask “what
time is it?”.
import { Clock, Effect } from "effect"
const program = Effect.gen(function* () { const ms = yield* Clock.currentTimeMillis return ms // => 1748563200000 (a Unix epoch timestamp in ms)})Clock.currentTimeNanos
Section titled “Clock.currentTimeNanos”An Effect<bigint> that succeeds with the current time in nanoseconds. Use it
when millisecond resolution is too coarse — for high-precision benchmarking or
ordering events that happen within the same millisecond.
import { Clock, Effect } from "effect"
const program = Effect.gen(function* () { const ns = yield* Clock.currentTimeNanos return ns // => 1748563200000000000n (a bigint of nanoseconds)})Clock.clockWith
Section titled “Clock.clockWith”clockWith(f) accesses the active Clock service and runs your function f
with it. Reach for it when you need the full interface — for example to call the
unsafe synchronous accessors — inside a single effect.
import { Clock, Effect } from "effect"
// A custom timed helper built directly on the Clock service.const timeWith = <A, E, R>(self: Effect.Effect<A, E, R>) => Clock.clockWith((clock) => Effect.gen(function* () { const start = clock.currentTimeMillisUnsafe() const result = yield* self const elapsed = clock.currentTimeMillisUnsafe() - start return [result, elapsed] as const }) )
const program = timeWith(Effect.succeed(42))// => [42, 0] (value paired with elapsed milliseconds)Clock.Clock
Section titled “Clock.Clock”The service reference itself. Clock is a Context.Reference, so a live
implementation backed by the runtime’s wall clock is always available — you never
have to provide it in production. Yield it to obtain the full service interface.
import { Clock, Effect } from "effect"
const program = Effect.gen(function* () { const clock = yield* Clock.Clock return clock.currentTimeMillisUnsafe() // => 1748563200000})The Clock interface has these members:
currentTimeMillis: Effect<number>— current time in milliseconds, as an effect.currentTimeNanos: Effect<bigint>— current time in nanoseconds, as an effect.sleep(duration: Duration): Effect<void>— suspend for aDuration. (Note this takes a fully-constructedDuration;Effect.sleepis the friendlier entry point that accepts anyDuration.Inputlike"2 seconds".)currentTimeMillisUnsafe(): number— synchronous, non-effectful millisecond read. Only valid once you already hold the service.currentTimeNanosUnsafe(): bigint— synchronous, non-effectful nanosecond read.
import { Clock, Duration, Effect } from "effect"
const program = Effect.gen(function* () { const clock = yield* Clock.Clock const ms = clock.currentTimeMillisUnsafe() // => 1748563200000 const ns = clock.currentTimeNanosUnsafe() // => 1748563200000000000n yield* clock.sleep(Duration.seconds(1)) // suspends 1s (via the Clock) return { ms, ns }})Sleeping & timeouts build on the Clock
Section titled “Sleeping & timeouts build on the Clock”You rarely call clock.sleep directly. The Effect operators for delaying and
bounding work — Effect.sleep, Effect.delay, and Effect.timeout — are all
implemented in terms of the active Clock, which is exactly why they become
instantaneous and deterministic under TestClock. Each accepts any
Duration.Input.
import { Effect } from "effect"
const program = Effect.gen(function* () { // Suspend the fiber for a duration. yield* Effect.sleep("500 millis")
// Delay an effect before it runs. yield* Effect.log("later").pipe(Effect.delay("1 second"))
// Bound an effect; fails with a timeout if it does not finish in time. const result = yield* slowWork.pipe(Effect.timeout("5 seconds")) return result})
declare const slowWork: Effect.Effect<string>See Schedule for repeating and retrying on a clock-driven cadence.
Time, like every other capability in Effect, is just a service — which is what lets schedules and retries be tested as easily as pure functions. For controlling the clock in tests, see TestClock and the broader Testing guide.