or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-timekeeper

Easy testing of time-dependent code with time freezing and travel functionality.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/timekeeper@2.3.x

To install, run

npx @tessl/cli install tessl/npm-timekeeper@2.3.0

index.mddocs/

Timekeeper

Timekeeper is a JavaScript time mocking library that provides comprehensive functionality for testing time-dependent code. It allows developers to freeze time, travel to specific dates, and control time flow during test execution, working seamlessly in both Node.js and browser environments.

Package Information

  • Package Name: timekeeper
  • Package Type: npm
  • Language: JavaScript (with TypeScript definitions)
  • Installation: npm install --save-dev timekeeper

Core Imports

const timekeeper = require('timekeeper');

For ES modules:

import * as timekeeper from 'timekeeper';

In browser environments:

<script src="path/to/timekeeper.js"></script>
<!-- Available as global: window.timekeeper -->

Basic Usage

const timekeeper = require('timekeeper');

// Freeze time to a specific moment
const frozenTime = new Date('2023-01-01T00:00:00Z');
timekeeper.freeze(frozenTime);

// All Date operations now return the frozen time
console.log(new Date()); // 2023-01-01T00:00:00.000Z
console.log(Date.now()); // 1672531200000

// Travel to a different time (time continues flowing)
const travelTime = new Date('2030-01-01T00:00:00Z');
timekeeper.travel(travelTime);

// Time flows from the travel point
setTimeout(() => {
  console.log(new Date()); // Approximately 2030-01-01T00:00:00.100Z
}, 100);

// Reset to normal time behavior
timekeeper.reset();

Architecture

Timekeeper works by replacing the native JavaScript Date constructor and Date.now() method with custom implementations that respect the frozen or travel states. Key components include:

  • Date Replacement: Swaps native Date with FakeDate during active mocking
  • Time State Management: Tracks frozen time, travel time, and travel start points
  • UMD Compatibility: Works across CommonJS, AMD, and browser global environments
  • Safe Cleanup: Ensures native Date functionality is properly restored

Capabilities

Time Freezing

Freezes time to a specific moment, making all Date operations return the same time.

/**
 * Set current Date Time and freeze it
 * @param date - Date object, timestamp number, or date string. Defaults to current time if undefined
 */
function freeze(date?: Date | number | string): void;

Usage Examples:

const timekeeper = require('timekeeper');

// Freeze to current time
timekeeper.freeze();

// Freeze to specific date
timekeeper.freeze(new Date('2023-06-15T10:30:00Z'));

// Freeze using timestamp
timekeeper.freeze(1687000000000);

// Freeze using date string
timekeeper.freeze('2023-06-15T10:30:00Z');

Time Travel

Sets the current time to a specific moment and allows time to continue flowing from that point.

/**
 * Set current DateTime and allow time to continue flowing
 * @param date - Date object, timestamp number, or date string. Defaults to current time if undefined
 */
function travel(date?: Date | number | string): void;

Usage Examples:

const timekeeper = require('timekeeper');

// Travel to future date
timekeeper.travel(new Date('2030-01-01T00:00:00Z'));

// Time continues flowing from travel point
setTimeout(() => {
  console.log(Date.now()); // Will be greater than 1893456000000
}, 100);

// Travel using timestamp
timekeeper.travel(1893456000000);

// Travel using date string
timekeeper.travel('2030-01-01T00:00:00Z');

Note: If time is already frozen when travel() is called, the time will be frozen to the new travel date instead of allowing it to flow.

Time Reset

Resets timekeeper behavior and restores native Date functionality.

/**
 * Reset the timekeeper behavior and restore native Date
 */
function reset(): void;

Usage Examples:

const timekeeper = require('timekeeper');

// After using freeze or travel
timekeeper.freeze(new Date('2023-01-01'));
console.log(new Date()); // 2023-01-01T00:00:00.000Z

// Reset to normal behavior
timekeeper.reset();
console.log(new Date()); // Current actual time

Time State Reflection

Checks whether timekeeper is currently modifying the native Date object.

/**
 * Reflection method to check if timekeeper is currently active
 * @returns true if timekeeper is modifying Date, false if using native Date
 */
function isKeepingTime(): boolean;

Usage Examples:

const timekeeper = require('timekeeper');

console.log(timekeeper.isKeepingTime()); // false

timekeeper.freeze(new Date('2023-01-01'));
console.log(timekeeper.isKeepingTime()); // true

timekeeper.reset();
console.log(timekeeper.isKeepingTime()); // false

Scoped Time Freezing

Executes a callback with time frozen to a specific date, then automatically resets afterwards. Supports both synchronous and asynchronous callbacks.

/**
 * Execute callback with time frozen to specific date, then reset automatically
 * @param date - Date object, timestamp number, date string, or undefined
 * @param callback - Function to execute with frozen time
 * @returns The return value of the callback
 */
function withFreeze<T>(date: Date | number | string | undefined, callback: () => T): T;

Usage Examples:

const timekeeper = require('timekeeper');

// Synchronous callback
const result = timekeeper.withFreeze(new Date('2023-01-01'), () => {
  console.log(new Date()); // 2023-01-01T00:00:00.000Z
  return 'test completed';
});
console.log(result); // 'test completed'
console.log(timekeeper.isKeepingTime()); // false (automatically reset)

// Asynchronous callback
const asyncResult = timekeeper.withFreeze(new Date('2023-01-01'), async () => {
  console.log(new Date()); // 2023-01-01T00:00:00.000Z
  await new Promise(resolve => setTimeout(resolve, 100));
  return 'async test completed';
});
// timekeeper automatically resets when promise resolves or rejects

// Error handling - reset happens even if callback throws
try {
  timekeeper.withFreeze(new Date('2023-01-01'), () => {
    throw new Error('Something went wrong');
  });
} catch (error) {
  // timekeeper is automatically reset even on error
  console.log(timekeeper.isKeepingTime()); // false
}

Types

// TypeScript interface for the timekeeper object
interface Timekeeper {
  freeze(date?: Date | number | string): void;
  travel(date?: Date | number | string): void;
  reset(): void;
  isKeepingTime(): boolean;
  withFreeze<T>(date: Date | number | string | undefined, callback: () => T): T;
}

Error Handling

  • Invalid Date Inputs: Timekeeper passes invalid date inputs to the native Date constructor, which handles parsing and validation
  • Exception Safety: The withFreeze method includes proper exception handling to ensure reset() is called even if the callback throws an error
  • Promise Handling: For Promise-returning callbacks in withFreeze, both success and error cases trigger automatic reset
  • State Consistency: All methods maintain internal state consistency and properly restore native Date functionality

Testing Framework Integration

Timekeeper works with any JavaScript testing framework. Common patterns include:

// Mocha example
describe('Time-dependent tests', function() {
  afterEach(function() {
    timekeeper.reset(); // Clean up after each test
  });

  it('should handle future dates', function() {
    timekeeper.travel(new Date('2030-01-01'));
    // Test time-dependent logic
  });
});

// Jest example
afterEach(() => {
  timekeeper.reset();
});

test('frozen time behavior', () => {
  timekeeper.freeze(new Date('2023-01-01'));
  expect(Date.now()).toBe(1672531200000);
});