Easy testing of time-dependent code with time freezing and travel functionality.
npx @tessl/cli install tessl/npm-timekeeper@2.3.0Timekeeper 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.
npm install --save-dev timekeeperconst 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 -->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();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:
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');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.
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 timeChecks 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()); // falseExecutes 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
}// 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;
}withFreeze method includes proper exception handling to ensure reset() is called even if the callback throws an errorwithFreeze, both success and error cases trigger automatic resetTimekeeper 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);
});