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

isotropic-duration-to-string

v0.4.0

Published

Format a duration or the time between two moments as human-readable text

Readme

isotropic-duration-to-string

npm version License

A utility that converts time durations into human-readable string representations with intelligent unit handling.

Why Use This?

  • Human-Readable Format: Converts durations into natural language with appropriate units
  • Flexible Input Formats: Accepts durations as a number of milliseconds, a numeric string, an ISO 8601 duration string, a plain object, or a Temporal.Duration
  • Built on Temporal: Uses the native Temporal API for accurate, dependency-free date and time math
  • Time Unit Intelligence: Automatically determines and displays the appropriate time units
  • Date-Aware Calculations: Accurately handles months, leap years, daylight saving time, and other calendar complexities
  • Multiple Calculation Methods: Calculate durations using an explicit duration or a pair of begin/end times

Requirements

This package depends on the global [Temporal](https://tc39.es/proposal-temporal/docs/) object and ships as native ES modules. It targets newer Node.js versions, where Temporal is available without a flag.

Installation

npm install isotropic-duration-to-string

Usage

import _durationToString from 'isotropic-duration-to-string';

// Basic usage with a number of milliseconds
_durationToString({
    duration: 63245986
});
// 17 hours 34 minutes 5.986 seconds

// Using begin and end times
_durationToString({
    beginTime: '2025-01-01T00:00:00Z',
    endTime: '2025-01-02T12:30:45.500Z'
});
// 1 day 12 hours 30 minutes 45.500 seconds

// Using a Temporal.Duration
_durationToString({
    duration: Temporal.Duration.from({
        days: 2
    })
});
// 2 days 0.000 seconds

API

durationToString(options)

Converts a duration to a formatted string representation.

Parameters

  • options (Object): Configuration options object with the following properties:
    • beginTime (Temporal.ZonedDateTime | Temporal.Instant | Temporal.PlainDateTime | Date | String | Number): Start time for calculating the duration. Optional.
    • duration (Number | String | Object | Temporal.Duration): The duration to format. Optional if beginTime and/or endTime are provided.
    • endTime (Temporal.ZonedDateTime | Temporal.Instant | Temporal.PlainDateTime | Date | String | Number): End time for calculating the duration. Optional.

A duration may be expressed as:

  • a number of milliseconds (e.g. 63245986)
  • a numeric string of milliseconds (e.g. '63245986')
  • an ISO 8601 duration string (e.g. 'PT17H34M5.986S', 'P2D')
  • a plain object of integer-valued calendar/clock fields (e.g. { hours: 17, minutes: 34, seconds: 5, milliseconds: 986 })
  • a **Temporal.Duration** instance

A beginTime or endTime may be expressed as:

  • a **Temporal.ZonedDateTime** (carries its own time zone)
  • a **Temporal.Instant**
  • a **Temporal.PlainDateTime**
  • a **Date** instance
  • an ISO 8601 string, an instant (e.g. '2025-01-01T00:00:00Z'), a zoned date-time (e.g. '2025-01-01T00:00:00-05:00[America/New_York]'), or a local date-time (e.g. '2025-01-01T00:00:00')
  • a number of milliseconds since the Unix epoch

Values that are not zone-aware (a Temporal.Instant, Temporal.PlainDateTime, Date, epoch number, or non-zoned ISO string) are interpreted in the system's current time zone for calendar and daylight-saving calculations. A Temporal.ZonedDateTime, or an ISO string that includes a [Time/Zone] annotation, carries its own time zone.

Returns

  • (String): A formatted string representation of the duration (e.g. '2 hours 30 minutes 15.750 seconds')

Behavior

  • If a duration is provided with neither beginTime nor endTime, the duration is formatted on its own.
  • If a duration is provided with a beginTime or an endTime, the duration is added to (or subtracted from) that anchor. If both anchors are provided along with a duration, the beginTime is used and the endTime is ignored.
  • If a beginTime and an endTime are provided with no duration, the elapsed time between them is formatted.
  • If only one of beginTime or endTime is provided with no duration, the other end defaults to the current time.
  • If the begin time is after the end time, the (positive) magnitude of the elapsed time is returned.
  • Output precision is milliseconds; sub-millisecond components are truncated.

Examples

Different Duration Input Formats

import _durationToString from 'isotropic-duration-to-string';

// Number of milliseconds
_durationToString({
    duration: 1000
});
// 1.000 seconds

// Numeric string of milliseconds
_durationToString({
    duration: '60000'
});
// 1 minute 0.000 seconds

// ISO 8601 duration string
_durationToString({
    duration: 'PT1H30M45.5S'
});
// 1 hour 30 minutes 45.500 seconds

// ISO 8601 duration string with days
_durationToString({
    duration: 'P2DT6H30M'
});
// 2 days 6 hours 30 minutes 0.000 seconds

// Object format (integer-valued fields)
_durationToString({
    duration: {
        days: 1,
        hours: 6,
        milliseconds: 500,
        minutes: 30,
        seconds: 15
    }
});
// 1 day 6 hours 30 minutes 15.500 seconds

// Temporal.Duration
_durationToString({
    duration: Temporal.Duration.from({
        hours: 1,
        minutes: 30
    })
});
// 1 hour 30 minutes 0.000 seconds

Using Begin and End Times

import _durationToString from 'isotropic-duration-to-string';

// Using Date instances
_durationToString({
    beginTime: new Date('2025-01-01T00:00:00Z'),
    endTime: new Date('2025-02-01T00:00:00Z')
});
// 1 month 0.000 seconds

// Using ISO strings
_durationToString({
    beginTime: '2025-01-01T12:00:00Z',
    endTime: '2025-01-01T14:30:45Z'
});
// 2 hours 30 minutes 45.000 seconds

// Using a number of milliseconds since the Unix epoch
_durationToString({
    beginTime: 1735689600000,
    endTime: 1735776000000
});
// 1 day 0.000 seconds

// Using Temporal types
_durationToString({
    beginTime: Temporal.PlainDateTime.from('2025-01-01T00:00:00'),
    endTime: Temporal.PlainDateTime.from('2025-03-01T00:00:00')
});
// 2 months 0.000 seconds

Calendar- and Time-Zone-Aware Calculations

import _durationToString from 'isotropic-duration-to-string';

// Duration crossing a month boundary
_durationToString({
    beginTime: '2025-01-31T00:00:00Z',
    endTime: '2025-03-01T00:00:00Z'
});
// 1 month 1 day 0.000 seconds

// Duration crossing a year boundary
_durationToString({
    beginTime: '2022-12-31T00:00:00Z',
    endTime: '2023-01-01T00:00:00Z'
});
// 1 day 0.000 seconds

// An exact (millisecond) duration reflects daylight saving time.
// 2,592,000,000 ms (30 × 24 h) added across the US spring-forward
// transition lands one wall-clock hour ahead.
_durationToString({
    beginTime: Temporal.ZonedDateTime.from('2025-03-01T00:00:00-05:00[America/New_York]'),
    duration: 2592000000
});
// 30 days 1 hour 0.000 seconds

// A calendar duration of the same nominal length adds 30 calendar
// days, absorbing the transition.
_durationToString({
    beginTime: Temporal.ZonedDateTime.from('2025-03-01T00:00:00-05:00[America/New_York]'),
    duration: 'P30D'
});
// 30 days 0.000 seconds

// A begin/end pair represents the true elapsed time, even across a
// daylight saving transition: a calendar day containing the US
// spring-forward transition is 23 elapsed hours.
_durationToString({
    beginTime: Temporal.ZonedDateTime.from('2025-03-08T00:00:00-05:00[America/New_York]'),
    endTime: Temporal.ZonedDateTime.from('2025-03-09T00:00:00-04:00[America/New_York]')
});
// 23 hours 0.000 seconds

Handling Special Cases

Zero Duration

import _durationToString from 'isotropic-duration-to-string';

_durationToString({
    duration: 0
});
// 0.000 seconds

_durationToString({
    beginTime: '2025-01-01T00:00:00Z',
    endTime: '2025-01-01T00:00:00Z'
});
// 0.000 seconds

Defaulting to Current Time

import _durationToString from 'isotropic-duration-to-string';

// Begin time with no end time (end time defaults to now)
_durationToString({
    beginTime: '2025-01-01T00:00:00Z'
});
// Result depends on the current time

// End time with no begin time (begin time defaults to now)
_durationToString({
    endTime: '2225-01-01T00:00:00Z'
});
// Result depends on the current time

Error Handling

The module throws errors created by isotropic-error, with the name ArgumentError, for invalid input:

  • Missing required arguments (when no duration, beginTime, or endTime is provided)
  • Invalid duration format (including a non-numeric string that is not a valid ISO 8601 duration, or an object with non-integer fields)
  • Invalid beginTime or endTime

Each error includes a descriptive message to help diagnose the issue.

Migrating from the Moment-Based Version

Earlier releases used moment-timezone. This version removes that dependency in favor of the native Temporal API. If you are upgrading:

  • The moment-timezone dependency has been removed. The package now uses the global Temporal object.
  • Moment instances and Moment durations are no longer accepted. Pass a Temporal type or a Date instead.
  • Duration strings are now ISO 8601 (e.g. 'PT1H30M', 'P2D') rather than hh:mm:ss.SSS / d.hh:mm:ss.SSS.
  • Duration objects must use integer values, matching Temporal.Duration. Fractional fields (e.g. { seconds: 5.986 }) throw; express the remainder in a smaller unit instead (e.g. { seconds: 5, milliseconds: 986 }).
  • Date instances, ISO strings, and epoch numbers continue to work as beginTime/endTime values.

Contributing

Please refer to CONTRIBUTING.md for contribution guidelines.

Issues

If you encounter any issues, please file them at https://github.com/ibi-group/isotropic-duration-to-string/issues