@yabpaseri/format-duration
v1.0.0
Published
Pattern-based duration formatter for JavaScript/TypeScript with y/M/d/H/m/s/S tokens
Downloads
221
Readme
@yabpaseri/format-duration
A pattern-based duration formatter. Combine y, M, d, H, m, s, S tokens with literal text to convert durations into any string format you need.
The pattern syntax is inspired by Apache Commons Lang's DurationFormatUtils.
- Tree-shakable — import
formatDurationandformatPeriodindependently - ESM / CJS — works in Node.js and browsers
- TypeScript — type definitions included
- Zero dependencies
Installation
npm install @yabpaseri/format-duration
# or
bun add @yabpaseri/format-durationAPI
formatDuration(durationMillis, format, options?)
Format a duration given in milliseconds using a pattern string.
import { formatDuration } from '@yabpaseri/format-duration';
formatDuration(90_061_001, 'HH:mm:ss.SSS');
// => '25:01:01.001'
formatDuration(90_061_000, "d'd' HH'h' mm'm' ss's'");
// => '1d 01h 01m 01s'
formatDuration(5_000, 'ss', { padWithZeros: false });
// => '5'Parameters:
| Name | Type | Description |
| --------------------------- | --------- | -------------------------------------------- |
| durationMillis | number | Milliseconds (>= 0) |
| format | string | Pattern string |
| options.padWithZeros | boolean | Zero-pad fields (default: true) |
| options.trimLeadingZeros | boolean | Trim leading zero fields (default: false) |
| options.trimTrailingZeros | boolean | Trim trailing zero fields (default: false) |
Supported tokens: d (days), H (hours), m (minutes), s (seconds), S (milliseconds)
yandMare accepted in the pattern but always produce 0. UseformatPeriodfor calendar-aware year/month formatting.
Field rollover: Values of fields not present in the pattern automatically roll into the next smaller unit.
// d omitted → 2 days roll into hours
formatDuration(2 * 86_400_000, 'HH:mm:ss');
// => '48:00:00'formatDurationISO(durationMillis)
Format a duration in milliseconds as an ISO 8601 duration string (PnDTnHnMn.nnnS).
import { formatDurationISO } from '@yabpaseri/format-duration';
formatDurationISO(90_061_001);
// => 'P1DT1H1M1.001S'
formatDurationISO(3_600_000);
// => 'PT1H'
formatDurationISO(0);
// => 'PT0S'formatPeriod(start, end, format, options?)
Format the difference between two dates using a pattern string, with calendar-accurate year and month calculation.
import { formatPeriod } from '@yabpaseri/format-duration';
const start = new Date('2020-01-15T00:00:00Z');
const end = new Date('2021-03-20T00:00:00Z');
formatPeriod(start, end, "y'y 'M'm 'd'd'", { timezone: 'UTC' });
// => '1y 2m 5d'
formatPeriod(start, end, "M'm 'd'd'", { timezone: 'UTC' });
// => '14m 5d'Parameters:
| Name | Type | Description |
| --------------------------- | ---------------- | --------------------------------------------------------------- |
| start | number \| Date | Start date |
| end | number \| Date | End date (must be >= start) |
| format | string | Pattern string |
| options.padWithZeros | boolean | Zero-pad fields (default: true) |
| options.trimLeadingZeros | boolean | Trim leading zero fields (default: false) |
| options.trimTrailingZeros | boolean | Trim trailing zero fields (default: false) |
| options.timezone | string | IANA timezone (e.g. 'Asia/Tokyo'). Defaults to local timezone |
Supported tokens: y (years), M (months), d (days), H (hours), m (minutes), s (seconds), S (milliseconds)
formatPeriodISO(start, end, options?)
Format the period between two dates as an ISO 8601 duration string (PnYnMnDTnHnMn.nnnS).
import { formatPeriodISO } from '@yabpaseri/format-duration';
formatPeriodISO(new Date('2020-01-15'), new Date('2021-03-20'), {
timezone: 'UTC',
});
// => 'P1Y2M5D'
formatPeriodISO(
new Date('2020-01-01T10:00:00Z'),
new Date('2020-01-01T12:30:45.5Z'),
{ timezone: 'UTC' },
);
// => 'PT2H30M45.5S'Pattern Syntax
| Syntax | Description | Example |
| ---------------------------- | --------------------------------------------------------------------- | ----------------------- |
| d, H, m, s, S etc. | Field token. Outputs the value as-is | d → 1 |
| dd, HH, mm etc. | Repetition sets minimum width (zero-padded) | HH → 01 |
| 'text' | Literal text | 'hours' → hours |
| '' | Literal single quote | '' → ' |
| [...] | Optional block — omitted when all fields inside are zero (no nesting) | [d'd '] → 1d or `` |
Invalid patterns (unclosed
', unclosed[, unmatched], nested[) throw aSyntaxError.
Optional Blocks
Tokens inside [...] are only printed when at least one field in the block has a non-zero value. Literals follow the adjacent field: leading literals print if the next field is non-zero; other literals print if the preceding field is non-zero.
// All fields optional: only non-zero fields are shown
formatDuration(3_600_000, "[d'd'H'h'm'm's's']");
// => '1h'
// Mixed: d is optional, H:m:s are always shown
formatDuration(3_661_000, "[d'd ']HH:mm:ss");
// => '01:01:01'
formatDuration(90_061_000, "[d'd ']HH:mm:ss");
// => '1d 01:01:01'Trim Options
For separator-based formats like HH:mm:ss, use trimLeadingZeros / trimTrailingZeros to trim zero-valued fields from the edges.
formatDuration(330_000, 'HH:mm:ss', { trimLeadingZeros: true });
// => '05:30' (H=0 removed)
formatDuration(3_600_000, 'HH:mm:ss', { trimTrailingZeros: true });
// => '01' (m=0, s=0 removed)License
MIT
