New JS Clock

Modern JavaScript/TypeScript Clock Library - API Reference & Usage Guide

Installation & Setup

Option 1: ES Modules (Recommended)

// Import the createClock function
import { createClock } from 'new-js-clock';

// Create a clock
const clock = createClock(document.getElementById('clock'));
// Import with full TypeScript support
import { createClock, ClockOptions, ClockInstance } from 'new-js-clock';

// Create a clock with type safety
const clock: ClockInstance = createClock(
  document.getElementById('clock')!
);

// Or use the full options interface
const options: ClockOptions = {
  showCenti: true,
  countdown: false
};
const precisionClock = createClock(
  document.getElementById('precision')!,
  '10:30:45',
  options
);
// CommonJS support
const { createClock } = require('new-js-clock');
const clock = createClock(document.getElementById('clock'));

Option 2: CDN / Script Tag

For direct browser usage without a bundler:

<!-- Minified (recommended for production) -->
<script src="https://unpkg.com/new-js-clock/dist/new-js-clock.min.js"></script>

<!-- Or via jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/new-js-clock/dist/new-js-clock.min.js"></script>

<!-- Unminified (for debugging) -->
<script src="https://unpkg.com/new-js-clock/dist/new-js-clock.js"></script>

<script>
  // Library is available as global NewJSClock
  var clock = NewJSClock.createClock(document.getElementById('clock'));

  // All features work the same
  var countdown = NewJSClock.createClock(
    document.getElementById('timer'),
    '00:05:00',
    { countdown: true }
  );
</script>
Tip: The script tag method is perfect for simple pages, internal tools, dashboards, or when you don't want to set up a build system.

Build from Source

# Clone the repository
git clone https://github.com/thiago-cavalcanti/new-js-clock.git
cd New-JS-Clock

# Install dependencies
pnpm install

# Build (includes TypeScript type checking)
pnpm run build

# Run tests
pnpm test

# Run linting
pnpm lint

API Reference

createClock(element, initialTime?, options?)

Creates a new clock instance attached to a DOM element.

createClock(element: HTMLElement, initialTime?: string, options?: ClockOptions): ClockInstance

Parameters

Parameter Type Required Description
element HTMLElement Required The DOM element where the clock will be rendered
initialTime string Optional Start time in "HH:MM:SS" or "HH:MM:SS:CC" format. If omitted, uses system time.
options ClockOptions Optional Configuration options object

ClockOptions Interface

Property Type Default Description
showCenti boolean false Display centiseconds (hundredths of a second)
countdown boolean false Run as countdown timer instead of counting up
showHour boolean true Show hours in the display
showMinute boolean true Show minutes in the display
callback function undefined Called when countdown reaches zero
use12Hour boolean false Use 12-hour format with AM/PM indicator
timezoneOffset number undefined Static timezone offset in hours from UTC (e.g., -5 for EST, +1 for CET, +5.5 for IST). Does NOT handle DST.
timezone string undefined IANA timezone name for DST-aware timezone support (e.g., "America/New_York", "Europe/London"). Automatically handles daylight saving time.
stopwatch boolean false Run as stopwatch (counts up from 00:00:00) instead of using system time
lap boolean false Enable lap/split mode - records lap times. Requires stopwatch to be enabled.
lapMode "splits" | "laps" | "both" "both" Type of lap recording: "splits" (cumulative time since start), "laps" (time between laps), or "both"
lapWord string "Split" in lapMode: "splits", otherwise "Lap" Custom word to display before lap number (e.g., "Split", "Lap"). Set to "" for no word.
useAnimationFrame boolean false Use requestAnimationFrame for smoother updates. Automatically falls back to setTimeout when the page is hidden (background tab).
Recommended: Use timezone with IANA names for DST-aware clocks. Use timezoneOffset only when you need a fixed offset that never changes.
Note: The timezoneOffset parameter expects the absolute UTC offset for the target timezone, not a relative offset from local time. Pass the target timezone's UTC offset directly (e.g., -5 for EST/UTC-5, 0 for GMT/UTC+0, 9 for JST/UTC+9). Do not calculate relative offsets from your local timezone.
Note: The timezone parameter accepts IANA timezone identifiers (e.g., "America/New_York", "Europe/Paris", "Asia/Tokyo"). The browser's Intl API handles DST transitions automatically. You cannot use both timezone and timezoneOffset simultaneously.
Note: In system/timezone modes (no custom initialTime), the clock derives display values from Date/Intl on every update tick for wall-clock accuracy. Increment/decrement ticking is used for countdown and stopwatch/custom-time modes.
useAnimationFrame Option: When enabled, the clock uses requestAnimationFrame for smoother visual updates synced to your display's refresh rate (60/120/144Hz). When the page is hidden (background tab), it automatically falls back to setTimeout to ensure the clock stays accurate. This is ideal for stopwatches and visual timers where smoothness matters, while still maintaining accuracy when the user switches tabs.

ClockInstance Interface

The createClock function returns an object with the following methods:

Method Returns Description
getTime() string Returns the current time as a formatted string (e.g., "14:30:25" or "14:30:25:50")
setTime(timeString) void Sets a new time for the clock without destroying the instance. Accepts "HH:MM:SS" or "HH:MM:SS:CC" format.
startClock() void Starts or resumes the clock if it was stopped
stopClock() void Pauses the clock
toggleClock() void Toggles between running and stopped states
isRunning() boolean Returns true if the clock is currently running
reset() void Resets the clock to its initial time and restarts it. Perfect for countdown timers!
lap() string Records a lap/split time and returns it as a string. Only works when lap option is enabled.
getLaps() string[] Returns an array of all recorded lap/split times based on lapMode. Only works when lap is enabled.
getSplitTimes() string[] Returns an array of split times (cumulative). Only works when lap is enabled with lapMode "splits" or "both".
getLapTimes() string[] Returns an array of lap times (time between laps). Only works when lap is enabled with lapMode "laps" or "both".
getLapRecords() LapRecord[] Returns an array of lap records with full details (lapNumber, lapTime, splitTime, preciseElapsedMs, timestamp). Only works when lap is enabled.
clearLaps() void Clears all recorded lap/split times. Only works when lap is enabled.
bestLap() LapRecord | null Returns the lap record with the fastest lap time. Only works when lap is enabled with lapMode "laps" or "both".
worstLap() LapRecord | null Returns the lap record with the slowest lap time. Only works when lap is enabled with lapMode "laps" or "both".
destroy() void Cleans up the clock instance, stops the timer, and clears the element
Note: All methods are instance-specific. Each clock created with createClock operates independently of others.

LapRecord Interface

The LapRecord interface contains detailed information about each recorded lap:

Property Type Description
lapNumber number The sequential lap number (1, 2, 3, ...)
lapTime string The lap time (time since last lap) in "HH:MM:SS" or "HH:MM:SS:CC" format
splitTime string The split time (cumulative time since start) in "HH:MM:SS" or "HH:MM:SS:CC" format
preciseElapsedMs number High-resolution lap delta in milliseconds (from performance.now()), enabling sub-millisecond precision for lap comparisons
timestamp number Unix timestamp (milliseconds) when the lap was recorded

Usage Examples

Example 1: Basic System Clock

Display the current system time:

import { createClock } from 'new-js-clock';

const clock = createClock(document.getElementById('clock'));

// The clock automatically starts and syncs with system time
// Display: "14:30:25" (updates every second)

Example 2: High Precision Clock

Show centiseconds for higher precision:

const clock = createClock(
  document.getElementById('precision-clock'),
  undefined,
  { showCenti: true }
);

// Display: "14:30:25:83" (updates every 10ms)

Example 3: Countdown Timer

Create a countdown with a callback:

const countdown = createClock(
  document.getElementById('timer'),
  '00:05:00',  // 5 minutes
  {
    countdown: true,
    callback: () => {
      alert('Time is up!');
      // Or play a sound, redirect, etc.
    }
  }
);

// Control the countdown
countdown.stopClock();   // Pause
countdown.startClock();  // Resume
countdown.toggleClock(); // Toggle

Example 4: Custom Start Time

Start from a specific time and count up:

const custom = createClock(
  document.getElementById('custom'),
  '10:30:45'  // Start at this time
);

// Counts up from 10:30:45
// Will roll over at 23:59:59 to 00:00:00

Example 5: Multiple Independent Clocks

Create multiple clocks that work independently:

// Clock 1: System time
const clock1 = createClock(document.getElementById('clock1'));

// Clock 2: Countdown from 10 minutes
const clock2 = createClock(
  document.getElementById('clock2'),
  '00:10:00',
  { countdown: true }
);

// Clock 3: Custom time with centiseconds
const clock3 = createClock(
  document.getElementById('clock3'),
  '05:00:00',
  { showCenti: true }
);

// Each clock operates independently!
clock1.stopClock();  // Only stops clock1
clock2.stopClock();  // Only stops clock2
Tip: This is the main bug fix in 1.0.0. In the old jQuery version, all control methods only worked on the last clock created. Now each instance is completely independent!

Example 6: 12-Hour Clock with AM/PM

Display time in 12-hour format with AM/PM indicator:

const clock12h = createClock(
  document.getElementById('clock12h'),
  undefined,  // Uses system time
  { use12Hour: true }
);

// Display examples:
// "02:30:45 PM" for 14:30:45
// "12:00:00 AM" for midnight
// "12:00:00 PM" for noon
const clock12h: ClockInstance = createClock(
  document.getElementById('clock12h')!,
  undefined,  // Uses system time
  { use12Hour: true }
);

// Works with all other options:
const clock12hWithCenti: ClockInstance = createClock(
  document.getElementById('clock12h-centi')!,
  undefined,
  {
    use12Hour: true,
    showCenti: true
  }
);
// Display: "02:30:45:50 PM"
Note: The 12-hour format automatically converts 24-hour time from the system clock or initial time string. 00:00 becomes 12:00 AM, 12:00 becomes 12:00 PM, 13:30 becomes 01:30 PM, etc.

Example 7: Multiple Timezone Dashboard

Display times from different timezones - choose between DST-aware (recommended) or static offset:

// Using IANA timezone names (DST-aware)
// New York - automatically adjusts for DST
const nyClock = createClock(
  document.getElementById('ny-time'),
  undefined,
  { timezone: 'America/New_York' }
);

// London - automatically adjusts for BST
const londonClock = createClock(
  document.getElementById('london-time'),
  undefined,
  { timezone: 'Europe/London' }
);

// Tokyo - no DST, but still works
const tokyoClock = createClock(
  document.getElementById('tokyo-time'),
  undefined,
  { timezone: 'Asia/Tokyo' }
);

// Sydney - Southern Hemisphere DST
const sydneyClock = createClock(
  document.getElementById('sydney-time'),
  undefined,
  { timezone: 'Australia/Sydney' }
);

// Combine with 12-hour format
const ny12hClock = createClock(
  document.getElementById('ny-12h'),
  undefined,
  {
    timezone: 'America/New_York',
    use12Hour: true
  }
);
// Using static offset (does NOT handle DST)
// New York (EST): UTC-5 - stays at -5 even during EDT
const nyClock = createClock(
  document.getElementById('ny-time'),
  undefined,
  { timezoneOffset: -5 }
);

// London (GMT): UTC+0
const londonClock = createClock(
  document.getElementById('london-time'),
  undefined,
  { timezoneOffset: 0 }
);

// Tokyo (JST): UTC+9
const tokyoClock = createClock(
  document.getElementById('tokyo-time'),
  undefined,
  { timezoneOffset: 9 }
);

// Mumbai (IST): UTC+5:30
const mumbaiClock = createClock(
  document.getElementById('mumbai-time'),
  undefined,
  { timezoneOffset: 5.5 }
);
Tip: Common IANA timezone names include: America/New_York, America/Los_Angeles, Europe/London, Europe/Paris, Asia/Tokyo, Asia/Shanghai, Australia/Sydney, Pacific/Auckland. See the full list of IANA timezones.

Example 8: Pomodoro Timer

A practical example - Pomodoro technique timer:

let pomodoroCount = 0;

const pomodoro = createClock(
  document.getElementById('pomodoro'),
  '00:25:00',
  {
    countdown: true,
    callback: () => {
      pomodoroCount++;
      alert(`Pomodoro ${pomodoroCount} completed! Take a 5-minute break.`);

      // Reset for next pomodoro
      pomodoro.destroy();
      pomodoro = createClock(
        document.getElementById('pomodoro'),
        '00:25:00',
        {
          countdown: true,
          callback: () => alert('Next pomodoro done!')
        }
      );
    }
  }
);

Example 8: Kitchen Timer with Minutes Only

Hide hours for a simple minute:second display:

const kitchenTimer = createClock(
  document.getElementById('kitchen'),
  '00:03:00',
  {
    countdown: true,
    showHour: false,  // Hides hours
    callback: () => {
      document.getElementById('kitchen').style.background = '#ffebee';
      alert('Dinner is ready!');
    }
  }
);

// Display: "03:00" instead of "00:03:00"

Example 9: Stopwatch with Lap Times

Create a simple stopwatch with lap functionality:

const stopwatch = createClock(
  document.getElementById('stopwatch'),
  '00:00:00',
  { showCenti: true }
);

const laps = [];

function recordLap() {
  const lapTime = stopwatch.getTime();
  laps.push(lapTime);
  console.log(`Lap ${laps.length}: ${lapTime}`);
}

function resetStopwatch() {
  stopwatch.reset();  // Uses the new reset() method!
  laps.length = 0;
}

Example 10: Reusable Countdown with reset()

Create a countdown that can be easily restarted without recreating:

const countdown = createClock(
  document.getElementById('countdown'),
  '00:05:00',
  {
    countdown: true,
    callback: () => {
      alert('Time is up!');
    }
  }
);

// User can pause/resume
countdown.stopClock();
countdown.startClock();

// Reset to original time and restart - no need to destroy/recreate!
countdown.reset();

// This is much cleaner than the old way:
// countdown.destroy();
// countdown = createClock(element, '00:05:00', {...});
Tip: The reset() method is perfect for countdown timers that users may want to restart. It resets to the initial time and automatically starts running again.

Example 11: Dynamic Time with setTime()

Change the displayed time dynamically without destroying the clock:

const clock = createClock(
  document.getElementById('clock'),
  '10:00:00'
);

// Later, change to a different time
clock.setTime('15:30:00');

// Works with centiseconds too
const precision = createClock(
  document.getElementById('precision'),
  '00:00:00:00',
  { showCenti: true }
);

clock.setTime('01:23:45:67');

// Perfect for setting a countdown time from user input
const userInput = document.getElementById('time-input').value;  // e.g., "00:10:00"
countdown.setTime(userInput);
Note: setTime() validates the time string and throws an error if invalid. Wrap in try-catch for user input.

Example 12: Pomodoro Timer with reset()

A practical Pomodoro timer using the new reset() method:

let pomodoroCount = 0;
const POMODORO_TIME = '00:25:00';
const BREAK_TIME = '00:05:00';
let isBreak = false;

const pomodoro = createClock(
  document.getElementById('pomodoro'),
  POMODORO_TIME,
  {
    countdown: true,
    callback: () => {
      if (!isBreak) {
        pomodoroCount++;
        alert(`Pomodoro ${pomodoroCount} completed! Take a 5-minute break.`);
        isBreak = true;
        pomodoro.setTime(BREAK_TIME);
        pomodoro.startClock();
      } else {
        alert('Break over! Starting next pomodoro.');
        isBreak = false;
        pomodoro.setTime(POMODORO_TIME);
        pomodoro.startClock();
      }
    }
  }
);

// Skip break button - just reset and continue
function skipBreak() {
  isBreak = false;
  pomodoro.setTime(POMODORO_TIME);
}

// Reset current pomodoro
function resetPomodoro() {
  pomodoro.reset();  // Easy reset to initial time!
}

Example 13: Stopwatch

Create a stopwatch that counts up from 00:00:00:

const stopwatch = createClock(
  document.getElementById('stopwatch'),
  undefined,
  { stopwatch: true }
);

// Display starts at "00:00:00" and counts up
// Stopwatch runs indefinitely (doesn't stop at midnight)

// Control like any other clock
stopwatch.stopClock();   // Pause
stopwatch.startClock();  // Resume
stopwatch.toggleClock(); // Toggle

// Reset back to 00:00:00
stopwatch.reset();

Example 14: Stopwatch with Lap Times

Record lap times during a stopwatch run:

const stopwatch = createClock(
  document.getElementById('stopwatch'),
  undefined,
  {
    stopwatch: true,
    lap: true,           // Enable lap mode
    lapWord: 'Split'     // Custom word (default: "Split" in splits mode, otherwise "Lap")
  }
);

// Record a lap - returns the lap time string
const lap1 = stopwatch.lap();  // "Split 1: 00:00:15"
const lap2 = stopwatch.lap();  // "Split 2: 00:00:32"
const lap3 = stopwatch.lap();  // "Split 3: 00:00:45"

// Get all laps as an array
const allLaps = stopwatch.getLaps();
// ["Split 1: 00:00:15", "Split 2: 00:00:32", "Split 3: 00:00:45"]

// Clear laps without stopping the stopwatch
stopwatch.clearLaps();

// Reset also clears laps
stopwatch.reset();

Example 15: Stopwatch with Centiseconds

High-precision stopwatch for accurate timing:

const precisionStopwatch = createClock(
  document.getElementById('precision-stopwatch'),
  undefined,
  {
    stopwatch: true,
    showCenti: true   // Show centiseconds
  }
);

// Display: "00:00:15:23" (hours:minutes:seconds:centiseconds)

Example 16: Race Timer with Custom Lap Word

Customize the lap word for different use cases:

// No word at all (just number and time)
const timer1 = createClock(
  document.getElementById('timer1'),
  undefined,
  { stopwatch: true, lap: true, lapWord: '' }
);
// Lap: "1: 00:01:23"

// Custom word for different sports
const raceTimer = createClock(
  document.getElementById('race'),
  undefined,
  { stopwatch: true, lap: true, lapWord: 'Checkpoint' }
);
// Lap: "Checkpoint 1: 00:05:30"

Migration Guide (v0.8 to v1.0.0)

Breaking Changes

Old Way (v0.8 - jQuery)

// Old jQuery syntax
$('#clock').jsclock();
$('#clock').jsclock('10:30:45');
$('#clock').jsclock('00:05:00', { countdown: true });

// Control methods (BROKEN for multiple clocks)
$.fn.jsclock.stopClock();
$.fn.jsclock.getTime();

New Way (1.0.0 - JavaScript/TypeScript)

// New ES module syntax
import { createClock } from 'new-js-clock';

const clock = createClock(document.getElementById('clock'));
const clock2 = createClock(document.getElementById('clock2'), '10:30:45');
const timer = createClock(
  document.getElementById('timer'),
  '00:05:00',
  { countdown: true }
);

// Control methods (WORKS correctly for each instance!)
clock.stopClock();
clock.getTime();
timer.startClock();
// New ES module syntax with TypeScript
import { createClock, ClockOptions, ClockInstance } from 'new-js-clock';

// Type-safe clock instances
const clock: ClockInstance = createClock(
  document.getElementById('clock')!
);

const clock2 = createClock(
  document.getElementById('clock2')!,
  '10:30:45'
);

// Typed options
const timerOptions: ClockOptions = {
  countdown: true,
  callback: () => console.log('Time is up!')
};
const timer: ClockInstance = createClock(
  document.getElementById('timer')!,
  '00:05:00',
  timerOptions
);

// All methods are type-safe
clock.stopClock();
const currentTime: string = clock.getTime();
const isRunning: boolean = timer.isRunning();
Benefits of upgrading:
  • Multiple clocks work correctly and independently
  • TypeScript support with full type safety
  • No jQuery dependency (smaller bundle size)
  • Modern ES modules
  • Better performance
  • 162 deterministic Jest tests (99.74% lines, 99.02% statements, 100% functions, and 98.32% branch coverage)
  • Dockerized Selenium Grid E2E browser tests using selenium/standalone-all-browsers on port 4444, running headless Chrome/Firefox/Edge for end-to-end behavior (including extended background-tab visibility scenarios)

Time Format Reference

Valid Time Formats

Format Example Description
HH:MM:SS "14:30:45" Standard format (hours, minutes, seconds)
HH:MM:SS:CC "14:30:45:50" With centiseconds (hundredths of a second)

Format Rules:

Note: When showCenti is enabled but the initial time doesn't include centiseconds, they default to "00".

Error Handling

The library validates inputs and throws descriptive errors for invalid configurations. The caller is responsible for displaying error messages to users.

// TypeError: "Invalid countdown option: must be a boolean"
createClock(el, '10:30:45', { countdown: 'yes' });

// TypeError: "Invalid element: must be an HTMLElement"
createClock(undefined);

// TypeError: "Invalid options: must be an object"
createClock(el, undefined, 123);
createClock(el, undefined, []);

// Error: "Initial time required for countdown mode"
createClock(el, undefined, { countdown: true });

// Error: "Invalid time string format: must be HH:MM:SS or HH:MM:SS:CC with leading zeros"
createClock(el, '25:00:00');  // Hour > 23
createClock(el, '10:70:00');  // Minute > 59
createClock(el, '10:30');     // Missing seconds

// RangeError: "Invalid timezoneOffset: must be between -12 and +14"
createClock(el, undefined, { timezoneOffset: -13 });  // Too negative
createClock(el, undefined, { timezoneOffset: 15 });   // Too positive

// TypeError: "Invalid timezoneOffset option: must be a number"
createClock(el, undefined, { timezoneOffset: 'EST' }); // Wrong type

// RangeError: "Invalid timezone: "Invalid/Zone" is not a valid IANA timezone name"
createClock(el, undefined, { timezone: 'Invalid/Zone' });

// Error: "Cannot use both timezone and timezoneOffset options; choose one"
createClock(el, undefined, { timezone: 'America/New_York', timezoneOffset: -5 });

Always wrap clock creation in try-catch for production use:

JavaScript

try {
  const clock = createClock(element, timeString, options);
} catch (error) {
  // Display error to user (caller decides how)
  element.textContent = error.message;
  console.error('Failed to create clock:', error.message);
}

TypeScript

try {
  const clock: ClockInstance = createClock(
    document.getElementById('clock')!,
    '10:30:45'
  );
} catch (error: unknown) {
  if (error instanceof Error) {
    // Display error to user (caller decides how)
    element.textContent = error.message;
    console.error('Failed to create clock:', error.message);
  }
}
Note: Error types follow JavaScript conventions: TypeError for incorrect types, RangeError for out-of-range values, and Error for general validation failures.

Browser Support

New JS Clock 1.0.0 supports all modern browsers with native ES2020 support:

Requires ES2020+ support. For older browsers, use a transpiler like Babel.

Test Coverage

The library has strong, behavior-focused test quality metrics:

Bundler Configuration

This library ships both ESM and CommonJS builds via package exports. Most bundlers work out of the box.

Vite

Works out of the box - no configuration needed.

webpack

// webpack.config.js
module.exports = {
  resolve: {
    extensions: ['.js', '.ts'],
    conditionNames: ['import', 'require', 'default']
  }
};

esbuild

// esbuild.config.js
esbuild.build({
  bundle: true,
  format: 'esm',
  mainFields: ['module', 'main']
});
// For CommonJS output
esbuild.build({
  bundle: true,
  format: 'cjs',
  mainFields: ['main', 'module']
});

TypeScript

The library includes TypeScript declarations. For ESM projects, ensure your tsconfig.json uses a compatible moduleResolution:

{
  "compilerOptions": {
    "moduleResolution": "node"  // or "node16", "nodenext", or "bundler"
  }
}
Note: If you use moduleResolution: "bundler" in your project, the library will still work correctly since it's compiled with moduleResolution: "node" for broad compatibility. CommonJS projects can use require('new-js-clock').