isotropic-duration-to-string
v0.4.0
Published
Format a duration or the time between two moments as human-readable text
Maintainers
Readme
isotropic-duration-to-string
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
TemporalAPI 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-stringUsage
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 secondsAPI
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 ifbeginTimeand/orendTimeare 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
durationis provided with neitherbeginTimenorendTime, the duration is formatted on its own. - If a
durationis provided with abeginTimeor anendTime, the duration is added to (or subtracted from) that anchor. If both anchors are provided along with aduration, thebeginTimeis used and theendTimeis ignored. - If a
beginTimeand anendTimeare provided with noduration, the elapsed time between them is formatted. - If only one of
beginTimeorendTimeis provided with noduration, 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 secondsUsing 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 secondsCalendar- 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 secondsHandling 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 secondsDefaulting 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 timeError Handling
The module throws errors created by isotropic-error, with the name ArgumentError, for invalid input:
- Missing required arguments (when no
duration,beginTime, orendTimeis 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
beginTimeorendTime
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-timezonedependency has been removed. The package now uses the globalTemporalobject. - Moment instances and Moment durations are no longer accepted. Pass a
Temporaltype or aDateinstead. - Duration strings are now ISO 8601 (e.g.
'PT1H30M','P2D') rather thanhh: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 }). Dateinstances, ISO strings, and epoch numbers continue to work asbeginTime/endTimevalues.
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
