Skip to content

hatefrad/ts-time-utils

Repository files navigation

ts-time-utils

A comprehensive TypeScript utility library for time, dates, durations, and calendar operations. Zero dependencies, full tree-shaking support, 430+ functions across 32 public modules.

npm version License: MIT

Live Playground & Docs | GitHub

Features

  • Lightweight — Import only what you need with tree-shaking support
  • Zero dependencies — Pure TypeScript, no external packages
  • Type-safe — Full TypeScript support with IntelliSense
  • Comprehensive — 430+ functions across 32 public modules
  • Fluent API — Chain operations with the chain() API
  • Extensible — Plugin system for custom functionality

When Not To Use This Library

  • You only need a single built-in Date helper or one Intl formatter.
  • You want the standardized Temporal API rather than a utility library.
  • You need a mutable Moment-style wrapper with implicit global locale state.

Installation

npm install ts-time-utils

Quick Start

import { formatDuration, timeAgo, Duration } from 'ts-time-utils';

// Format milliseconds to readable duration
formatDuration(3661000); // "1 hour, 1 minute, 1 second"

// Get "time ago" strings
timeAgo(new Date(Date.now() - 3600000)); // "1 hour ago"

// Duration arithmetic
const meeting = Duration.fromMinutes(45);
const buffer = Duration.fromMinutes(15);
meeting.add(buffer).toString(); // "1h"

Tree-shaking (recommended)

import { formatDuration } from 'ts-time-utils/format';
import { differenceInUnits } from 'ts-time-utils/calculate';
import { isValidDate } from 'ts-time-utils/validate';

Every public module is also available as a subpath import such as ts-time-utils/timezone, ts-time-utils/workingHours, or ts-time-utils/naturalLanguage.

Module Selection

  • Use ts-time-utils when you need a few core utilities from different areas.
  • Use ts-time-utils/format or ts-time-utils/calculate when one module is enough.
  • Use dedicated subpaths like ts-time-utils/chain, ts-time-utils/plugins, ts-time-utils/locale, or ts-time-utils/workingHours for those feature areas.

Utility Categories

Format

Format durations, time ago strings, and custom date formats.

import { formatDuration, timeAgo, parseDuration } from 'ts-time-utils/format';

formatDuration(65000);                    // "1 minute, 5 seconds"
formatDuration(65000, { short: true });   // "1m 5s"
timeAgo(new Date(Date.now() - 60000));    // "1 minute ago"
parseDuration('1h 30m');                  // 5400000 (ms)

Calculate

Date arithmetic, differences, and business day calculations.

import { differenceInUnits, differenceInCalendarDays, addTime, startOf, endOf } from 'ts-time-utils/calculate';

differenceInUnits(date1, date2, 'days');  // 10
differenceInCalendarDays(date1, date2);   // Calendar date boundary count
addTime(new Date(), 5, 'hours');          // 5 hours from now
startOf(new Date(), 'day');               // 00:00:00 today
endOf(new Date(), 'month');               // Last moment of month

Validate

Date validation, checks, and comparisons.

import { isValidDate, isLeapYear, isWeekend, isSameDay } from 'ts-time-utils/validate';

isValidDate(new Date('2025-13-01'));      // false
isLeapYear(2024);                         // true
isWeekend(new Date('2025-09-13'));        // true (Saturday)
isSameDay(date1, date2);                  // boolean

Duration

Immutable Duration class with arithmetic operations.

import { Duration } from 'ts-time-utils/duration';

const d1 = Duration.fromHours(2.5);
const d2 = Duration.fromString('1h 30m 45s');
const d3 = Duration.between(startDate, endDate);

d1.add(d2).toString();        // "4h 0m 45s"
d1.greaterThan(d2);           // true
d1.multiply(2).hours;         // 5

Chain API

Fluent chainable API for date operations.

import { chain } from 'ts-time-utils/chain';

chain(new Date())
  .startOf('day')
  .add(9, 'hours')
  .add(30, 'minutes')
  .toDate();  // Today at 9:30am

chain(new Date())
  .add(1, 'week')
  .startOf('week')
  .format('YYYY-MM-DD');  // Next week Monday

Plugin extensions are imported from ts-time-utils/plugins, and that module now loads the chain class directly. You can import plugins before or after chain without any hidden global setup.

Timezone

Timezone conversions, DST handling, and zone comparisons.

import { formatInTimeZone, isDST, convertBetweenZones } from 'ts-time-utils/timezone';

formatInTimeZone(new Date(), 'America/New_York');
isDST(new Date('2025-07-14'), 'America/New_York');  // true
convertBetweenZones(new Date(), 'UTC', 'Asia/Tokyo');

isDST() uses a yearly-offset heuristic rather than authoritative transition metadata.

Calendar

ISO weeks, quarters, holidays, and calendar grids.

import { getWeekNumber, getQuarter, getEaster, getUSHolidays } from 'ts-time-utils/calendar';

getWeekNumber(new Date('2025-09-14'));    // 37
getQuarter(new Date('2025-07-15'));       // 3
getEaster(2025);                          // Easter Sunday 2025
getUSHolidays(2025);                      // Array of US federal holidays

Date Range

Date range operations: overlap, gaps, merge, split.

import { mergeDateRanges, findGaps, dateRangeOverlap } from 'ts-time-utils/dateRange';

const ranges = [
  { start: new Date('2024-01-01'), end: new Date('2024-01-10') },
  { start: new Date('2024-01-05'), end: new Date('2024-01-15') },
];

mergeDateRanges(ranges);  // Merged into single range
findGaps(busyTimes, workday);  // Available time slots
dateRangeOverlap(range1, range2);  // true/false

Recurrence

RRULE-inspired recurring event patterns.

import { createRecurrence, recurrenceToString } from 'ts-time-utils/recurrence';

const weekly = createRecurrence({
  frequency: 'weekly',
  interval: 1,
  startDate: new Date('2025-01-01'),
  byWeekday: [1, 3, 5],  // Mon, Wed, Fri
});

weekly.getNextOccurrence(new Date());
weekly.getAllOccurrences();
recurrenceToString(weekly.rule);  // "Every week on Monday, Wednesday, Friday"

Cron

Parse and match cron expressions.

import { parseCronExpression, matchesCron, getNextCronDate, describeCron } from 'ts-time-utils/cron';

const date = new Date();

parseCronExpression('0 9 * * 1-5');       // minute/hour/day/month/day-of-week parts
matchesCron(date, '0 9 * * 1-5');         // true if weekday 9am
getNextCronDate('0 9 * * *');             // Next 9am
describeCron('0 9 * * 1-5');              // "At 09:00 on Monday through Friday"

Fiscal Year

Fiscal year utilities with configurable start month.

import { getFiscalYear, getFiscalQuarter } from 'ts-time-utils/fiscal';

const date = new Date();

getFiscalYear(date, { startMonth: 4 });          // April start
getFiscalYear(date, { startMonth: 7 });          // July start
getFiscalYear(date, { startMonth: 10 });         // October start
getFiscalQuarter(date, { startMonth: 4 });       // Q2 for UK fiscal

Compare & Sort

Sort, group, and analyze date arrays.

import { sortDates, closestDate, groupDatesByMonth, snapDate } from 'ts-time-utils/compare';

sortDates(dates, 'desc');
closestDate(target, candidates);
groupDatesByMonth(dates);  // Map by YYYY-MM
snapDate(date, 15, 'minutes');  // Snap to 15-min grid

Iterate

Iterate through date sequences and count dates.

import { eachDay, eachWeekday, countWeekdays, filterDays } from 'ts-time-utils/iterate';

eachDay(start, end);       // Array of each day
eachWeekday(start, end);   // Weekdays only (Mon-Fri)
countWeekdays(start, end); // Number of weekdays
filterDays(start, end, d => d.getDate() === 15);  // 15th of each month

Natural Language

Parse human-friendly date strings.

import { parseNaturalDate, extractDatesFromText } from 'ts-time-utils/naturalLanguage';

parseNaturalDate('tomorrow');
parseNaturalDate('next Friday');
parseNaturalDate('in 2 weeks');
parseNaturalDate('end of month');

extractDatesFromText('Meeting tomorrow at 3pm');
// [{ date: Date, text: 'tomorrow at 3pm', confidence: 0.9 }]

parseNaturalDate accepts referenceDate, defaultTime, and strict. When strict is true, the parser only returns the library's recognized patterns and skips the permissive Date fallback.

International Holidays

Public holidays for 20 countries.

import { getHolidays, isHoliday, getNextHoliday } from 'ts-time-utils/holidays';

const today = new Date();

getHolidays(2025, 'UK');      // UK bank holidays
getHolidays(2025, 'DE');      // German holidays
isHoliday(today, 'CA');       // Is Canadian holiday?
getNextHoliday(today, 'AU');   // Next Australian holiday

// Supported: UK, NL, DE, CA, AU, IT, ES, CN, IN, US,
//            JP, FR, BR, MX, KR, SG, PL, SE, BE, CH

Locale

Multi-language formatting with 40+ locales.

import { formatRelativeTime, formatDateLocale, detectLocale } from 'ts-time-utils/locale';

const pastDate = new Date(Date.now() - 2 * 60 * 60 * 1000);
const date = new Date();

formatRelativeTime(pastDate, { locale: 'es' });  // "hace 2 horas"
formatRelativeTime(pastDate, { locale: 'de' });  // "vor 2 Stunden"
formatDateLocale(date, 'fr', 'long');            // "15 janvier 2024"
detectLocale();                                   // Auto-detect system locale

Working Hours

Business hours calculations with break support. These helpers use the Date values you pass in directly; if you need a different timezone's wall clock, adapt the instant first with a timezone helper such as convertDateToZone() or reinterpretAsZone().

import { isWorkingTime, addWorkingDays, workingDaysBetween } from 'ts-time-utils/workingHours';

const config = {
  workingDays: [1, 2, 3, 4, 5],
  hours: { start: 9, end: 17 },
  breaks: [{ start: 12, end: 13 }]
};
const now = new Date();

isWorkingTime(now, config);
addWorkingDays(now, 5, config);
workingDaysBetween(now, new Date('2025-12-31'), config);

Serialization

Safe JSON date serialization and deserialization.

import { serializeDate, parseJSONWithDates, stringifyWithDates } from 'ts-time-utils/serialize';

serializeDate(date, { format: 'iso' });    // "2025-09-14T12:30:45.123Z"
serializeDate(date, { format: 'epoch' });  // 1757853045123

const json = stringifyWithDates(data, ['createdAt']);
const parsed = parseJSONWithDates(json, ['createdAt']);

Performance

Async utilities, benchmarking, and timing.

import { sleep, benchmark, Stopwatch, debounce } from 'ts-time-utils/performance';

await sleep(1000);
await benchmark(() => heavyOperation(), 10);

const stopwatch = new Stopwatch();
stopwatch.start();
// ... operations
stopwatch.getElapsed();

const debouncedFn = debounce(fn, 300);

Age

Age calculations and birthday utilities.

import { calculateAge, getLifeStage, getNextBirthday } from 'ts-time-utils/age';

calculateAge(new Date('1990-05-15'));  // { years: 34, months: 4, days: 2 }
getLifeStage(25);                      // "adult"
getNextBirthday(birthDate);            // Next birthday date

Countdown

Timer and countdown utilities.

import { createCountdown, getRemainingTime, formatCountdown } from 'ts-time-utils/countdown';

const countdown = createCountdown(targetDate, {
  onTick: (remaining) => console.log(remaining.days, 'd'),
  onComplete: () => console.log('Done!'),
});
countdown.start();

getRemainingTime(targetDate);  // { days, hours, minutes, seconds }
formatCountdown(targetDate, { units: ['days', 'hours'] });  // "45d 12h"

Interval

Time interval operations.

import { createInterval, intervalsOverlap, mergeIntervals } from 'ts-time-utils/interval';

const a = createInterval('2025-01-01', '2025-01-05');
const b = createInterval('2025-01-04', '2025-01-10');
intervalsOverlap(a, b);  // true
mergeIntervals([a, b]);  // Single merged interval

Range Presets

Common date range presets.

import { today, lastNDays, thisWeek, thisMonth } from 'ts-time-utils/rangePresets';

today();        // { start, end } for today
lastNDays(7);   // Last 7 days
thisWeek();     // Current week
thisMonth();    // Current month

Non-Gregorian Calendars

Convert dates between calendar systems using Intl.DateTimeFormat.

import { toHebrewDate, toIslamicDate, toJapaneseDate, getChineseZodiac } from 'ts-time-utils/calendars';

toHebrewDate(new Date());        // { year: 5785, month: 4, day: 23, calendar: 'hebrew' }
toIslamicDate(new Date());       // { year: 1446, month: 7, day: 1, calendar: 'islamic-umalqura' }
toJapaneseDate(new Date());      // { year: 6, era: 'Reiwa', calendar: 'japanese' }
getChineseZodiac(2024);          // 'Dragon'

// Supported: Hebrew, Islamic, Buddhist, Japanese, Persian, Chinese

Temporal API Compatibility

Future-proof with Temporal-like objects that work with native Date.

import { toPlainDate, toPlainDateTime, toZonedDateTime, toInstant } from 'ts-time-utils/temporal';

const date = toPlainDate(2024, 3, 25);
date.add({ days: 7 });           // PlainDate
date.until(otherDate);           // Duration
date.dayOfWeek;                  // 1 (Monday, ISO)

const zdt = toZonedDateTime(new Date(), 'America/New_York');
zdt.hour;                        // Hour in that timezone
zdt.toInstant();                 // Epoch-based Instant

const instant = toInstant(Date.now());
instant.toZonedDateTime('UTC');  // Convert to any timezone

High-Precision Utilities

Nanosecond timestamps, BigInt support, DST handling, and leap seconds.

import {
  createNanosecondTimestamp, nowNanoseconds,
  toBigIntMs, ValidDate, isInDSTGap, leapSecondsBetween
} from 'ts-time-utils/precision';

// Nanosecond precision
const ts = createNanosecondTimestamp(Date.now(), 500000);
ts.totalNanoseconds;             // BigInt

// BigInt timestamps for large date ranges
toBigIntMs(new Date());          // BigInt milliseconds

// DST transition detection
isInDSTGap(new Date('2024-03-10T02:30:00'));

// Validated dates (never invalid)
const valid = ValidDate.from(date);  // Throws if invalid
const maybe = ValidDate.tryFrom(date);  // Returns null if invalid

// Leap second awareness
leapSecondsBetween(date1, date2);  // Number of leap seconds

Parse

Date parsing from various formats.

import { parseDate, parseTime, guessDateFormat } from 'ts-time-utils/parse';

parseDate('Dec 25, 2025');
parseDate('25/12/2025', 'DD/MM/YYYY');
parseTime('2:30 PM');  // { hour: 14, minute: 30 }
guessDateFormat('2025-09-14');  // 'YYYY-MM-DD'

Scheduling

Appointment slots, availability, and booking conflict detection.

import {
  generateSlots, getAvailableSlots, findNextAvailable,
  hasConflict, findConflicts, mergeBookings
} from 'ts-time-utils/scheduling';

// Generate 30-min slots for a day
const slots = generateSlots(new Date(), {
  slotDuration: 30,
  workingHours: {
    workingDays: [1, 2, 3, 4, 5],
    hours: { start: 9, end: 17 }
  }
});

// Find available slots (excluding existing bookings)
const available = getAvailableSlots(new Date(), existingBookings, config);

// Find next available slot of specific duration
findNextAvailable(new Date(), bookings, 60, config);  // 60-min slot

// Check for conflicts
hasConflict(bookings, proposedSlot);  // true/false
findConflicts(bookings, proposedSlot);  // Array of conflicting bookings

// Merge adjacent bookings
mergeBookings(bookings);

Finance

Market hours, trading days, and settlement date calculations.

import {
  isMarketOpen, isTradingDay, getMarketHours,
  getNextMarketOpen, addTradingDays, getSettlementDate,
  eachTradingDay, getOptionsExpiration
} from 'ts-time-utils/finance';

// Check market status
isMarketOpen(new Date(), 'NYSE');      // Is NYSE open right now?
isTradingDay(new Date(), 'NASDAQ');    // Is today a trading day?
getMarketHours('NYSE');                // { open: {hour:9,minute:30}, close: {hour:16,minute:0} }

// Market timing
getNextMarketOpen(new Date());         // Next market open time
addTradingDays(new Date(), 5);         // 5 trading days from now

// Settlement (T+2, etc.)
getSettlementDate(tradeDate, 2);       // T+2 settlement date

// Iterate trading days
eachTradingDay(start, end);            // Array of trading days

// Options expiration (3rd Friday)
getOptionsExpiration(2025, 3);         // March 2025 expiration

Healthcare

Medication scheduling, shift patterns, and compliance windows.

import {
  getMedicationTimes, getNextMedicationTime, parseMedicationFrequency,
  generateShiftSchedule, getShiftForTime, isOnShift,
  createOnCallRotation, getOnCallStaff,
  getComplianceDeadline, timeUntilDeadline
} from 'ts-time-utils/healthcare';

// Medication times (BID = twice daily, TID = 3x daily, etc.)
getMedicationTimes(new Date(), 'BID');   // [8am, 8pm] (default wake/sleep times)
getMedicationTimes(new Date(), 'q6h');   // Every 6 hours
getNextMedicationTime(now, 'TID');       // Next scheduled dose
parseMedicationFrequency('twice daily'); // 'BID'

// Shift scheduling
generateShiftSchedule(start, end, { pattern: '12hr', startTime: { hour: 7, minute: 0 } });
getShiftForTime(now, shiftConfig);       // Current shift
isOnShift(now, shiftStart, config);      // Is within shift?

// On-call rotations
const rotation = createOnCallRotation(start, end, ['Dr. Smith', 'Dr. Jones'], 24);
getOnCallStaff(now, rotation);           // Who's on call?

// Compliance windows
getComplianceDeadline(eventDate, 72);    // 72-hour window
timeUntilDeadline(eventDate, deadline);  // Duration remaining

Plugin System

Extend ChainedDate with custom functionality.

import { chain, ChainedDate } from 'ts-time-utils/chain';
import { extend } from 'ts-time-utils/plugins';

extend('business', {
  addBusinessDays(this: ChainedDate, days: number) {
    // Implementation
    return this;
  },
  isBusinessDay(this: ChainedDate) {
    const day = this.toDate().getDay();
    return day !== 0 && day !== 6;
  }
});

chain(new Date())
  .addBusinessDays(5)
  .isBusinessDay();  // true/false

API Reference

For complete API documentation, see the Playground & Docs.

All Modules

Module Description
format Duration formatting, time ago, date patterns
calculate Date arithmetic, differences, rounding
validate Date validation, comparisons, type checks
duration Immutable Duration class with arithmetic
chain Fluent chainable API
timezone Timezone conversions, DST handling
calendar ISO weeks, quarters, holidays, grids
calendars Non-Gregorian calendars (Hebrew, Islamic, etc.)
temporal Temporal API compatibility layer
precision Nanoseconds, BigInt, DST, leap seconds
dateRange Range operations: overlap, gaps, merge
recurrence RRULE-inspired recurring patterns
cron Cron expression parsing and matching
fiscal Fiscal year utilities
compare Date sorting, grouping, statistics
iterate Date iteration and counting
naturalLanguage Natural language date parsing
holidays International holidays (20 countries)
locale Multi-language formatting (40+ locales)
workingHours Business hours calculations
serialize JSON date serialization
performance Async utilities, benchmarking
age Age calculations, birthdays
countdown Timer and countdown utilities
interval Time interval operations
rangePresets Common date range presets
parse Date parsing from various formats
scheduling Appointment slots, availability, booking
finance Market hours, trading days, settlement
healthcare Medication schedules, shifts, on-call
plugins Plugin system for extensions
constants Time constants and types

Development

npm install      # Install dependencies
npm run build    # Build both CJS and ESM
npm test         # Run tests
npm run test:package  # Verify built package exports after build
npm run lint     # Lint code

Releasing

Releases are automated via GitHub Actions with npm trusted publishing (OIDC).

To release a new version:

git tag vX.Y.Z        # Create a semantic version tag for the release
git push --tags       # Push tag -> triggers publish workflow

The workflow automatically:

  1. Sets package.json version from tag
  2. Runs npm run release:verify
  3. Publishes to npm with provenance

Version format: Tags must match v* pattern (e.g., v1.2.3, v1.2.3-beta.1)

Before tagging, run:

npm run release:verify

License

MIT

About

A lightweight, zero-dependency TypeScript library providing utilities for working with dates, times, durations, and timezones.

Topics

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages