npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

iso-time

v1.11.3

Published

strict ISO 8601 timestamps, date stamps, and durations with runtime validation

Readme

iso-time

test publish

A glossary of intuitive, universally unambiguous time, date, and duration domain literals.

purpose

declare a universally unambiguous serialization format for dates, times, and datetimes via strict ISO 8601 types with runtime validation.

the core insight: time types are either stamps or floats

  • stamp = absolute instant in history (anchored to a specific point in time)
  • float = detached time pattern (unanchored, could refer to many instants)

etymology:

  • Stamp — matches PostgreSQL timestamp semantics; a time "stamped" into history
  • Float — a time that floats, not anchored; could land on many different instants

usecases:

  • clarify the format a date string should be in (const since: IsoDateStamp = '2024-12-15')
  • cast inputs to strict iso format (const birthday: IsoDateStamp = asIsoDateStamp(new Date()))
  • narrow type of strings with runtime validation (if (!isIsoDateStamp(since)) throw new Error('wrong format'))

types

stamps (absolute instants)

  • IsoTimeStamp = yyyy-MM-ddTHH:mm:ssZ (e.g., 2024-06-15T14:30:00Z)
  • IsoDateStamp = yyyy-MM-dd (e.g., 2024-06-15)
  • IsoMonthStamp = yyyy-MM (e.g., 2024-06)
  • IsoYearStamp = yyyy (e.g., 2024)

floats (detached patterns)

  • IsoTimeFloat = HH:mm:ss (e.g., 14:30:00)
  • IsoHourFloat = HH (e.g., 14)
  • IsoMinuteFloat = mm (e.g., 30)
  • IsoMonthFloat = MM (e.g., 06)
  • IsoDayFloat = dd (e.g., 15)
  • IsoWeekdayFloat = i (e.g., 1 for Monday)

ranges (time spans)

  • IsoTimeStampRange = { since: IsoTimeStamp; until: IsoTimeStamp }
  • IsoDateStampRange = { since: IsoDateStamp; until: IsoDateStamp }

durations (time quantities)

  • IsoDurationWords = iso 8601 string format with compile-time validation (e.g., 'P1Y', 'PT30S', 'P1DT2H')
  • IsoDurationShape = structured object format (e.g., { years: 1 }, { seconds: 30 }, { days: 1, hours: 2 })
  • IsoDuration = union of both formats — use whichever is more convenient

install

npm install iso-time

use

stamps

cast to strict iso format

import { asIsoTimeStamp, asIsoDateStamp } from 'iso-time';

const timestamp = asIsoTimeStamp(new Date()); // '2024-06-15T14:30:00Z'
const datestamp = asIsoDateStamp(new Date()); // '2024-06-15'

validate format

import { isIsoTimeStamp, isIsoDateStamp } from 'iso-time';

isIsoTimeStamp('2024-06-15T14:30:00Z'); // true
isIsoTimeStamp('2024-06-15'); // false

isIsoDateStamp('2024-06-15'); // true
isIsoDateStamp('06/15/2024'); // false

type declarations

import type { IsoTimeStamp, IsoDateStamp } from 'iso-time';

const occurredAt: IsoTimeStamp = asIsoTimeStamp(new Date());
const birthday: IsoDateStamp = asIsoDateStamp('1990-01-15');

floats

extract time components

import { asIsoHourFloat, asIsoMinuteFloat, asIsoWeekdayFloat } from 'iso-time';

const hour = asIsoHourFloat(new Date()); // '14'
const minute = asIsoMinuteFloat(new Date()); // '30'
const weekday = asIsoWeekdayFloat(new Date()); // '1' (Monday)

ranges

create time ranges

import { asIsoTimeStampRange, asIsoDateStampRange } from 'iso-time';

const standup = asIsoTimeStampRange({
  since: new Date('2024-06-15T10:00:00Z'),
  until: new Date('2024-06-15T11:00:00Z'),
});

const vacation = asIsoDateStampRange({
  since: new Date('2024-07-01'),
  until: new Date('2024-07-14'),
});

durations

durations can be expressed in two formats:

  • words = iso 8601 string format ('P1Y', 'PT30S', 'P1DT2H30M')
  • shape = structured object format ({ years: 1 }, { seconds: 30 })

note

  • IsoDurationShape is easier to work with programmatically, so all operations return it by default.
  • IsoDurationWords can be convenient for inputs but is rarely convenient for manipulation.

declare durations

import type { IsoDuration, IsoDurationWords, IsoDurationShape } from 'iso-time';

// string format — concise, readable, compile-time validated
const cacheTtl: IsoDurationWords = 'PT5M';           // 5 minutes
const invoicePeriod: IsoDurationWords = 'P1M';       // 1 month
const trialPeriod: IsoDurationWords = 'P14D';        // 14 days
const sessionTimeout: IsoDurationWords = 'PT30M';   // 30 minutes

// object format — easy to manipulate programmatically
const timeout: IsoDurationShape = { seconds: 30 };
const workday: IsoDurationShape = { hours: 8 };
const sprint: IsoDurationShape = { weeks: 2 };

// union type — accepts either format
const flexible: IsoDuration = 'P1D';                 // string ✓
const alsoFlexible: IsoDuration = { days: 1 };       // object ✓

compile-time validation

IsoDurationWords catches invalid iso 8601 strings at compile time:

import type { IsoDurationWords } from 'iso-time';

// ✅ valid — compiles
const good1: IsoDurationWords = 'P1Y';
const good2: IsoDurationWords = 'PT30S';
const good3: IsoDurationWords = 'P1DT2H30M';

// ❌ invalid — compile error
const bad1: IsoDurationWords = '1 day';              // not iso format
const bad2: IsoDurationWords = 'P';                  // lacks components
const bad3: IsoDurationWords = 'T30S';               // lacks P prefix
const bad4: IsoDurationWords = { days: 1 };          // wrong type (object)

convert to milliseconds

import { toMilliseconds } from 'iso-time';

// both formats work
toMilliseconds({ hours: 1 });       // 3600000
toMilliseconds('PT1H');             // 3600000
toMilliseconds({ days: 1 });        // 86400000
toMilliseconds('P1D');              // 86400000

date arithmetic

import { addDuration, subDuration, getDuration } from 'iso-time';

const later = addDuration(asIsoTimeStamp(new Date()), { hours: 2 });
const earlier = subDuration(asIsoTimeStamp(new Date()), { days: 1 });
const earlierStill = subDuration(asIsoTimeStamp(new Date()), 'P3D');

const elapsed = getDuration({
  of: { range: { since: '2024-06-15T10:00:00Z', until: '2024-06-15T12:30:00Z' } },
}); // { hours: 2, minutes: 30 }

sum durations

import { sumDurations } from 'iso-time';

// spread syntax
const total = sumDurations({ hours: 1 }, { minutes: 30 }, { seconds: 45 });
// { hours: 1, minutes: 30, seconds: 45 }

// array syntax
const durations = [{ hours: 2 }, 'PT30M', { minutes: 15 }];
const combined = sumDurations(durations);
// { hours: 2, minutes: 45 }

// get sum in a specific unit
const totalMinutes = sumDurations(durations, { as: 'minutes' });
// { minutes: 165 }

sleep

import { sleep } from 'iso-time';

await sleep('PT5S');
await sleep({ seconds: 5 });
await sleep({ milliseconds: 100 });

observe

current

import { now, today } from 'iso-time';

const timestamp = now(); // '2024-06-15T14:30:00Z'
const datestamp = today(); // '2024-06-15'

stopwatch

import { startDurationStopwatch } from 'iso-time';

const stopwatch = startDurationStopwatch({ for: 'api call' }, { log: console });
await fetchData();
const { duration } = stopwatch.stop(); // logs elapsed time

examples

event schedule system

import type {
  IsoTimeStamp, IsoDateStamp, IsoTimeFloat,
  IsoWeekdayFloat, IsoDuration
} from 'iso-time';

// when exactly the event occurs (absolute)
interface ScheduledEvent {
  createdAt: IsoTimeStamp;     // '2024-01-15T14:30:00Z'
  occursOn: IsoDateStamp;      // '2024-03-20'
}

// detached schedule pattern (float)
interface DailySchedule {
  since: IsoTimeFloat;         // '09:00:00' - every day at 9am
  until: IsoTimeFloat;         // '10:00:00'
}

// scheduled standup with date range and weekday pattern
interface ScheduledStandup {
  repeats: {
    since: IsoDateStamp;       // '2024-01-15' - start date
    until: IsoDateStamp;       // '2024-12-31' - end date
  };
  occurs: {
    days: IsoWeekdayFloat[];   // ['1', '3', '5'] - mon, wed, fri
    at: IsoTimeFloat;          // '09:00:00'
    for: IsoDuration;          // { minutes: 15 } or 'PT15M'
  };
}

invoice system

import type { IsoMonthStamp, IsoDayFloat, IsoMonthFloat } from 'iso-time';

// bill period (absolute)
interface Invoice {
  period: IsoMonthStamp;  // '2024-01' - January 2024 invoice
}

// bill schedule (float)
interface BillSchedule {
  dueOn: IsoDayFloat;             // '15' - due on 15th of each month
  renewsOn: IsoMonthFloat;        // '01' - annual renewal in January
}

equipment rental

type-safe rental periods with compile-time validated durations:

import type { IsoDurationWords, IsoDuration, IsoDurationShape, IsoTimeStamp } from 'iso-time';
import { addDuration, getDuration, now } from 'iso-time';

// rental options — iso strings validated at compile time
interface RentalOption {
  label: string;
  duration: IsoDurationWords;
  pricePerDay: number;
}

const excavatorRentals: RentalOption[] = [
  { label: 'half day', duration: 'PT4H', pricePerDay: 150 },
  { label: 'full day', duration: 'P1D', pricePerDay: 250 },
  { label: 'weekend', duration: 'P2D', pricePerDay: 200 },
  { label: 'week', duration: 'P7D', pricePerDay: 175 },

  // ❌ compile error — '3 days' is not valid iso 8601
  // { label: 'bad', duration: '3 days', pricePerDay: 180 },
];

// rental agreement with typed durations
interface RentalAgreement {
  equipment: string;
  pickedUpAt: IsoTimeStamp;
  duration: IsoDuration;           // accepts both formats
  gracePeriod: IsoDuration;        // 'PT1H' — 1 hour grace
}

// compute when equipment is due back
const computeDueAt = (input: { agreement: RentalAgreement }): IsoTimeStamp => {
  const dueAt = addDuration(input.agreement.pickedUpAt, input.agreement.duration);
  return addDuration(dueAt, input.agreement.gracePeriod);
};

// check if rental is overdue
const computeOverdueHours = (input: {
  agreement: RentalAgreement;
  returnedAt: IsoTimeStamp;
}): number => {
  const dueAt = computeDueAt({ agreement: input.agreement });
  const overdue = getDuration({
    of: { range: { since: dueAt, until: input.returnedAt } },
    as: 'hours',
  });
  return overdue.hours;
};

// check how much to refund, minus early return fee
const computeRefundDuration = (input: {
  agreement: RentalAgreement;
  returnedAt: IsoTimeStamp;
}): IsoDurationShape => {
  const dueAt = computeDueAt({ agreement: input.agreement });
  const hoursUnused = getDuration({
    of: { range: { since: input.returnedAt, until: dueAt } },
    as: 'hours',
  }).hours;
  const hoursRefundable = hoursUnused * 0.8; // 20% early return fee
  return { hours: hoursRefundable };         // object format — value is computed
};