New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

@time-provider/core

Package Overview
Dependencies
Maintainers
1
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@time-provider/core

Time-Provider : Core library ~ A TypeScript library for injecting time into your application, making time-dependent code deterministic, easy to test, and independent of your date library.

Source
npmnpm
Version
1.1.0
Version published
Weekly downloads
0
Maintainers
1
Weekly downloads
 
Created
Source

Time-Provider

NPM check codecov

"Fake timers that change unrelated behavior are not substitutes for a clock; they are substitutes for the world."

— Barbara Liskov (who could have said this)

Time is an application dependency, not a runtime detail.

time-provider makes time explicit, testable, and independent from global state.

Time should be SOLID! (that's DEEP DIP)

Most applications access time through global APIs:

  • Date
  • Temporal.Now
  • setTimeout
  • setInterval

These APIs are convenient, but they make time an implicit dependency.

This creates problems:

  • business logic becomes coupled to the system clock
  • tests depend on real time passing
  • timers are difficult to control deterministically
  • different application boundaries cannot easily define their own notion of time

time-provider applies dependency inversion to time.

Instead of:

Application
    |
    ▼
Global runtime clock

you get:

Application boundary
    |
    ▼
TimeProvider
    |
    ├── Time access
    ├── Parser
    └── Scheduler

Time becomes an explicit dependency that can be replaced depending on the context.

What is it?

time-provider is a TypeScript library for injecting time and timers into applications.

It provides a single boundary for:

  • reading the current time
  • parsing timestamps
  • scheduling future actions
  • controlling time progression during tests
interface ITimeProvider<TDate> {
  clock: IClock<TDate>;
  parser: IParser<TDate>;
  scheduler: IScheduler;
}
interface IClock<TDate> {
  localNow(): TDate;
  utcNow(): TDate;
}
interface IParser<TDate> {
  parse(input: string | number | TDate): TDate;
}
interface IScheduler {
  setTimeout(callback: () => void, millisecondsDelay?: number): SetTimeoutHandle;
  clearTimeout(handle: SetTimeoutHandle): void;
  setInterval(callback: () => void, millisecondsDelay?: number): SetIntervalHandle;
  clearInterval(handle: SetIntervalHandle): void;
}

A time provider exposes:

  • time access
  • timestamp parsing
  • scheduling capabilities

Together they cover the main ways applications interact with time.

Application boundaries can define their own time

"Now" can vary depending on your app domain or your business logic.

Different parts of an application may require different notions of time:

  • domain logic may depend on a business-defined clock
  • background jobs may depend on processing time
  • simulations may use virtual time
  • tests may use deterministic time

Global APIs force all these cases into the same hidden dependency.

With dependency injection, each boundary receives the time provider it needs.

Keep your date library

time-provider neither introduces a new date model nor forces you into one.

Your application keeps using the date library it already uses.

Supported adapters include:

  • Temporal
  • Native Date
  • Day.js
  • Luxon
  • Moment.js

The adapter preserves the native date type with type-safe signatures.

time-provider provides the dependency boundary around time while keeping your existing date stack.

Why not just create a Clock interface?

A clock abstraction solves one problem:

What time is it?

But applications usually depend on more than reading time.

They also need:

  • scheduling future actions
  • deterministic timer execution
  • controlled time progression in tests
  • timestamp parsing

time-provider keeps these concerns together:

TimeProvider
├── Clock
│   ├── localNow()
│   └── utcNow()
│
├── Parser
│   └── parse()
│
└── Scheduler
    ├── setTimeout()
    ├── clearTimeout()
    ├── setInterval()
    └── clearInterval()

Features

  • Dependency-injectable clocks and timers
  • Deterministic timers without replacing global APIs
  • Multiple clock strategies
  • Multiple isolated time contexts in the same application
  • Support for multiple date libraries
  • Native date types preserved
  • Type-safe return values

Quick start

Install the core package and an adapter:

npm install @time-provider/core @time-provider/plugin-native

Production:

import { createTimeProvider } from "@time-provider/core";
import { plugin } from "@time-provider/plugin-native";

const timeProvider = createTimeProvider.for(plugin).create();

Testing:

const timeProvider = createTimeProvider
  .for(plugin)
  .asManual()
  .withInitialTime("2026-01-01T00:00Z")
  .create();

let retries = 0;

timeProvider.scheduler.setInterval(() => {
  retries++;
}, 1000);

timeProvider.clock.advance({
  seconds: 3,
});

expect(retries).toBe(3);

Usage

Dependency injection

import type { ITimeProvider } from "@time-provider/core";
import { Temporal } from "@js-temporal/polyfill";

class UserService {
  constructor(private readonly timeProvider: ITimeProvider<Temporal.Instant>) {}

  createUser() {
    return {
      createdAt: this.timeProvider.clock.utcNow(),
    };
  }
}

The service does not know whether time comes from:

  • the system clock
  • a fixed clock
  • a simulation
  • a test clock

It only depends on the time capability it receives.

Testing deterministic time

const timeProvider = createTimeProvider
  .for(plugin)
  .asFixed()
  .withFixedTime("2026-01-01T00:00Z")
  .create();

const sut = new UserService(time);

expect(sut.createUser().createdAt).toEqual(timeProvider.clock.utcNow());

Timers

The scheduler follows the selected clock strategy.

With a manual clock, callbacks execute when time advances.

const timeProvider = createTimeProvider
  .for(plugin)
  .asManual()
  .withInitialTime("2026-01-01T00:00Z")
  .create();

let count = 0;

timeProvider.scheduler.setInterval(() => {
  count++;
}, 1000);

timeProvider.clock.advance({
  seconds: 3,
});

expect(count).toBe(3);

Clock strategies

System clock

The default production strategy.

Uses:

  • current system time
  • native runtime timers
const timeProvider = createTimeProvider.for(plugin).create();

Fixed clock

Always returns the same instant.

Useful for deterministic tests.

const timeProvider = createTimeProvider
  .for(plugin)
  .asFixed()
  .withFixedTime("2026-01-01T00:00Z")
  .create();

Manual clock

Time progresses only when explicitly advanced.

Useful for:

  • simulations
  • integration tests
  • workflows depending on elapsed time
time.advance({
  seconds: 5,
});

Sequential clock

Returns a predefined sequence of instants.

Useful when testing code that depends on changing timestamps.

const timeProvider = createTimeProvider
  .for(plugin)
  .asSequential()
  .withSequentialTime("2026-01-01T00:01Z")
  .withSequentialTime("2026-01-01T00:02Z")
  .create();

Installation

npm install @time-provider/core @time-provider/plugin-temporal

Adapters:

npm install @time-provider/plugin-native
npm install @time-provider/plugin-dayjs
npm install @time-provider/plugin-luxon
npm install @time-provider/plugin-moment

Architecture

The library is split into:

  • @time-provider/core

    • common abstractions
    • provider creation
    • clock strategies
    • scheduler behavior
  • adapters

    • map the generic API to a date library
    • preserve native date types

Use cases

  • Testing expiration logic
  • Testing retry policies
  • Simulating workflows
  • Running deterministic integration tests
  • Keeping domain logic independent from system time
  • Supporting multiple isolated time contexts

License

MIT

Keywords

clock

FAQs

Package last updated on 18 Jul 2026

Related posts