| 1 | /** |
| 2 | * Service and helpers for reading time and sleeping inside Effect programs. |
| 3 | * The active `Clock` provides Unix time, monotonic time for measuring elapsed |
| 4 | * durations, and a `sleep` operation for delaying work. Because time is |
| 5 | * accessed through a service, tests can replace the clock with a controlled |
| 6 | * implementation. |
| 7 | * |
| 8 | * @since 2.0.0 |
| 9 | */ |
| 10 | import type * as Context from "./Context.ts" |
| 11 | import type * as Duration from "./Duration.ts" |
| 12 | import type { Effect } from "./Effect.ts" |
| 13 | import * as effect from "./internal/effect.ts" |
| 14 | |
| 15 | /** |
| 16 | * Represents a time-based clock which provides functionality related to time |
| 17 | * and scheduling. |
| 18 | * |
| 19 | * **When to use** |
| 20 | * |
| 21 | * Use to define or provide a clock service for current-time and sleep |
| 22 | * operations. |
| 23 | * |
| 24 | * **Example** (Reading current time) |
| 25 | * |
| 26 | * ```ts import.meta.vitest |
| 27 | * import { Clock, Effect } from "effect" |
| 28 | * |
| 29 | * const testClock: Clock.Clock = { |
| 30 | * currentTimeMillisUnsafe: () => 1_000, |
| 31 | * currentTimeMillis: Effect.succeed(1_000), |
| 32 | * monotonicTimeNanosUnsafe: () => 1_000_000_000n, |
| 33 | * monotonicTimeNanos: Effect.succeed(1_000_000_000n), |
| 34 | * currentTimeNanosUnsafe: () => 1_000_000_000n, |
| 35 | * currentTimeNanos: Effect.succeed(1_000_000_000n), |
| 36 | * sleep: () => Effect.void |
| 37 | * } |
| 38 | * |
| 39 | * const clockOperations = Effect.gen(function*() { |
| 40 | * const currentTime = yield* Clock.currentTimeMillis |
| 41 | * const currentTimeNanos = yield* Clock.currentTimeNanos |
| 42 | * return [currentTime, currentTimeNanos] as const |
| 43 | * }) |
| 44 | * |
| 45 | * await Effect.runPromise(Effect.provideService(clockOperations, Clock.Clock, testClock)) // => [1_000, 1_000_000_000n] |
| 46 | * ``` |
| 47 | * |
| 48 | * @category services |
| 49 | * @since 2.0.0 |
| 50 | */ |
| 51 | export interface Clock { |
| 52 | /** |
| 53 | * Returns the current Unix time in milliseconds unsafely. |
| 54 | * |
| 55 | * **When to use** |
| 56 | * |
| 57 | * Use to read a wall-clock timestamp synchronously when you already have a |
| 58 | * `Clock` service and can accept non-effectful access. |
| 59 | * |
| 60 | * **Gotchas** |
| 61 | * |
| 62 | * The value can move backward or forward when the system wall clock is |
| 63 | * corrected, so it is not suitable for measuring elapsed time. |
| 64 | */ |
| 65 | currentTimeMillisUnsafe(): number |
| 66 | /** |
| 67 | * Returns the current Unix time in milliseconds. |
| 68 | * |
| 69 | * **When to use** |
| 70 | * |
| 71 | * Use to read a wall-clock timestamp through this `Clock` service in |
| 72 | * `Effect`. |
| 73 | * |
| 74 | * **Gotchas** |
| 75 | * |
| 76 | * The value can move backward or forward when the system wall clock is |
| 77 | * corrected, so it is not suitable for measuring elapsed time. |
| 78 | */ |
| 79 | readonly currentTimeMillis: Effect<number> |
| 80 | /** |
| 81 | * Returns the current Unix time in nanoseconds unsafely. |
| 82 | * |
| 83 | * **When to use** |
| 84 | * |
| 85 | * Use to read a wall-clock timestamp synchronously when you already have a |
| 86 | * `Clock` service and can accept non-effectful access. |
| 87 | * |
| 88 | * **Gotchas** |
| 89 | * |
| 90 | * The value can move backward or forward when the system wall clock is |
| 91 | * corrected, so it is not suitable for measuring elapsed time. |
| 92 | */ |
| 93 | currentTimeNanosUnsafe(): bigint |
| 94 | /** |
| 95 | * Returns the current Unix time in nanoseconds. |
| 96 | * |
| 97 | * **When to use** |
| 98 | * |
| 99 | * Use to read a wall-clock timestamp through this `Clock` service in |
| 100 | * `Effect`. |
| 101 | * |
| 102 | * **Gotchas** |
| 103 | * |
| 104 | * The value can move backward or forward when the system wall clock is |
| 105 | * corrected, so it is not suitable for measuring elapsed time. |
| 106 | */ |
| 107 | readonly currentTimeNanos: Effect<bigint> |
| 108 | /** |
| 109 | * Returns the current monotonic time in nanoseconds unsafely. |
| 110 | * |
| 111 | * **When to use** |
| 112 | * |
| 113 | * Use to measure elapsed time synchronously when you already have a `Clock` |
| 114 | * service and can accept non-effectful access. |
| 115 | * |
| 116 | * **Gotchas** |
| 117 | * |
| 118 | * The value has an arbitrary origin and is unsuitable for serialization. Use |
| 119 | * it only to subtract readings produced by the same clock. Whether it |
| 120 | * advances while the host is suspended depends on the runtime. |
| 121 | * |
| 122 | * @since 4.0.0 |
| 123 | */ |
| 124 | monotonicTimeNanosUnsafe(): bigint |
| 125 | /** |
| 126 | * Returns the current monotonic time in nanoseconds. |
| 127 | * |
| 128 | * **When to use** |
| 129 | * |
| 130 | * Use to measure elapsed time through this `Clock` service in `Effect`. |
| 131 | * |
| 132 | * **Gotchas** |
| 133 | * |
| 134 | * The value has an arbitrary origin and is unsuitable for serialization. Use |
| 135 | * it only to subtract readings produced by the same clock. Whether it |
| 136 | * advances while the host is suspended depends on the runtime. |
| 137 | * |
| 138 | * @since 4.0.0 |
| 139 | */ |
| 140 | readonly monotonicTimeNanos: Effect<bigint> |
| 141 | /** |
| 142 | * Asynchronously sleeps for the specified duration. |
| 143 | * |
| 144 | * **When to use** |
| 145 | * |
| 146 | * Use to delay an `Effect` workflow by a duration through this `Clock` service. |
| 147 | */ |
| 148 | sleep(duration: Duration.Duration): Effect<void> |
| 149 | } |
| 150 | |
| 151 | /** |
| 152 | * Context reference for the active time service in the environment. |
| 153 | * |
| 154 | * **When to use** |
| 155 | * |
| 156 | * Use when you need to access or provide the full time service, including sleep |
| 157 | * operations, rather than a single timestamp accessor. |
| 158 | * |
| 159 | * **Example** (Accessing the Clock service) |
| 160 | * |
| 161 | * ```ts import.meta.vitest |
| 162 | * import { Clock, Effect } from "effect" |
| 163 | * |
| 164 | * const testClock: Clock.Clock = { |
| 165 | * currentTimeMillisUnsafe: () => 1_000, |
| 166 | * currentTimeMillis: Effect.succeed(1_000), |
| 167 | * monotonicTimeNanosUnsafe: () => 1_000_000_000n, |
| 168 | * monotonicTimeNanos: Effect.succeed(1_000_000_000n), |
| 169 | * currentTimeNanosUnsafe: () => 1_000_000_000n, |
| 170 | * currentTimeNanos: Effect.succeed(1_000_000_000n), |
| 171 | * sleep: () => Effect.void |
| 172 | * } |
| 173 | * |
| 174 | * const program = Effect.gen(function*() { |
| 175 | * const clock = yield* Clock.Clock |
| 176 | * return clock.currentTimeMillisUnsafe() |
| 177 | * }) |
| 178 | * |
| 179 | * await Effect.runPromise(Effect.provideService(program, Clock.Clock, testClock)) // => 1_000 |
| 180 | * ``` |
| 181 | * |
| 182 | * @see {@link clockWith} for using the current Clock service inside an effect |
| 183 | * @see {@link currentTimeMillis} for reading the current time in milliseconds |
| 184 | * @see {@link currentTimeNanos} for reading the current time in nanoseconds |
| 185 | * |
| 186 | * @category services |
| 187 | * @since 2.0.0 |
| 188 | */ |
| 189 | export const Clock: Context.Reference<Clock> = effect.ClockRef |
| 190 | |
| 191 | /** |
| 192 | * Accesses the current Clock service and uses it to run the provided function. |
| 193 | * |
| 194 | * **When to use** |
| 195 | * |
| 196 | * Use when you need the full Clock service interface to perform multiple time |
| 197 | * operations or call unsafe variants within a single effect. |
| 198 | * |
| 199 | * **Example** (Accessing the current Clock service) |
| 200 | * |
| 201 | * ```ts import.meta.vitest |
| 202 | * import { Clock, Effect } from "effect" |
| 203 | * |
| 204 | * const testClock: Clock.Clock = { |
| 205 | * currentTimeMillisUnsafe: () => 1_000, |
| 206 | * currentTimeMillis: Effect.succeed(1_000), |
| 207 | * monotonicTimeNanosUnsafe: () => 1_000_000_000n, |
| 208 | * monotonicTimeNanos: Effect.succeed(1_000_000_000n), |
| 209 | * currentTimeNanosUnsafe: () => 1_000_000_000n, |
| 210 | * currentTimeNanos: Effect.succeed(1_000_000_000n), |
| 211 | * sleep: () => Effect.void |
| 212 | * } |
| 213 | * |
| 214 | * const program = Clock.clockWith((clock) => Effect.sync(() => clock.currentTimeMillisUnsafe())) |
| 215 | * |
| 216 | * await Effect.runPromise(Effect.provideService(program, Clock.Clock, testClock)) // => 1_000 |
| 217 | * ``` |
| 218 | * |
| 219 | * @see {@link Clock} for the service reference |
| 220 | * @see {@link currentTimeMillis} for convenience accessor that returns milliseconds |
| 221 | * @see {@link currentTimeNanos} for convenience accessor that returns nanoseconds |
| 222 | * @category accessors |
| 223 | * @since 2.0.0 |
| 224 | */ |
| 225 | export const clockWith: <A, E, R>(f: (clock: Clock) => Effect<A, E, R>) => Effect<A, E, R> = effect.clockWith |
| 226 | |
| 227 | /** |
| 228 | * Returns an Effect that succeeds with the current Unix time in milliseconds. |
| 229 | * |
| 230 | * **When to use** |
| 231 | * |
| 232 | * Use to create wall-clock timestamps from the active `Clock` service with |
| 233 | * millisecond precision. |
| 234 | * |
| 235 | * **Gotchas** |
| 236 | * |
| 237 | * The value can move backward or forward when the system wall clock is |
| 238 | * corrected, so it is not suitable for measuring elapsed time. |
| 239 | * |
| 240 | * **Example** (Reading milliseconds) |
| 241 | * |
| 242 | * ```ts import.meta.vitest |
| 243 | * import { Clock, Effect } from "effect" |
| 244 | * |
| 245 | * const testClock: Clock.Clock = { |
| 246 | * currentTimeMillisUnsafe: () => 1_000, |
| 247 | * currentTimeMillis: Effect.succeed(1_000), |
| 248 | * monotonicTimeNanosUnsafe: () => 1_000_000_000n, |
| 249 | * monotonicTimeNanos: Effect.succeed(1_000_000_000n), |
| 250 | * currentTimeNanosUnsafe: () => 1_000_000_000n, |
| 251 | * currentTimeNanos: Effect.succeed(1_000_000_000n), |
| 252 | * sleep: () => Effect.void |
| 253 | * } |
| 254 | * |
| 255 | * await Effect.runPromise(Effect.provideService(Clock.currentTimeMillis, Clock.Clock, testClock)) // => 1_000 |
| 256 | * ``` |
| 257 | * |
| 258 | * @see {@link currentTimeNanos} for nanosecond precision |
| 259 | * @see {@link monotonicTimeNanos} for measuring elapsed time |
| 260 | * @see {@link clockWith} for accessing the full Clock service |
| 261 | * |
| 262 | * @category accessors |
| 263 | * @since 2.0.0 |
| 264 | */ |
| 265 | export const currentTimeMillis: Effect<number> = effect.currentTimeMillis |
| 266 | |
| 267 | /** |
| 268 | * Returns an Effect that succeeds with the current Unix time in nanoseconds. |
| 269 | * |
| 270 | * **When to use** |
| 271 | * |
| 272 | * Use to create wall-clock timestamps from the active `Clock` service with |
| 273 | * nanosecond precision. |
| 274 | * |
| 275 | * **Gotchas** |
| 276 | * |
| 277 | * The value can move backward or forward when the system wall clock is |
| 278 | * corrected, so it is not suitable for measuring elapsed time. |
| 279 | * The live clock allows up to one second of drift from `Date.now()` before |
| 280 | * re-anchoring. |
| 281 | * |
| 282 | * **Example** (Reading nanoseconds) |
| 283 | * |
| 284 | * ```ts import.meta.vitest |
| 285 | * import { Clock, Effect } from "effect" |
| 286 | * |
| 287 | * const testClock: Clock.Clock = { |
| 288 | * currentTimeMillisUnsafe: () => 1_000, |
| 289 | * currentTimeMillis: Effect.succeed(1_000), |
| 290 | * monotonicTimeNanosUnsafe: () => 1_000_000_000n, |
| 291 | * monotonicTimeNanos: Effect.succeed(1_000_000_000n), |
| 292 | * currentTimeNanosUnsafe: () => 1_000_000_000n, |
| 293 | * currentTimeNanos: Effect.succeed(1_000_000_000n), |
| 294 | * sleep: () => Effect.void |
| 295 | * } |
| 296 | * |
| 297 | * await Effect.runPromise(Effect.provideService(Clock.currentTimeNanos, Clock.Clock, testClock)) // => 1_000_000_000n |
| 298 | * ``` |
| 299 | * |
| 300 | * @see {@link monotonicTimeNanos} for measuring elapsed time |
| 301 | * |
| 302 | * @category accessors |
| 303 | * @since 2.0.0 |
| 304 | */ |
| 305 | export const currentTimeNanos: Effect<bigint> = effect.currentTimeNanos |
| 306 | |
| 307 | /** |
| 308 | * Returns an Effect that succeeds with the current monotonic time in |
| 309 | * nanoseconds. |
| 310 | * |
| 311 | * **When to use** |
| 312 | * |
| 313 | * Use to measure elapsed time by subtracting two readings. |
| 314 | * |
| 315 | * **Gotchas** |
| 316 | * |
| 317 | * The value has an arbitrary origin and is unsuitable for serialization. Use |
| 318 | * it only to subtract readings produced by the same clock. Whether it advances |
| 319 | * while the host is suspended depends on the runtime. |
| 320 | * |
| 321 | * @see {@link currentTimeNanos} for Unix wall-clock timestamps |
| 322 | * |
| 323 | * @category accessors |
| 324 | * @since 4.0.0 |
| 325 | */ |
| 326 | export const monotonicTimeNanos: Effect<bigint> = effect.monotonicTimeNanos |