@pvorona/duration
v0.2.3
Published
An immutable ESM-only duration type with unit conversions, comparisons, and basic arithmetic.
Readme
@pvorona/duration
An immutable ESM-only duration type with unit conversions, comparisons, and basic arithmetic.
Install
npm i @pvorona/durationThis package is ESM-only. Import it from ESM modules instead of require(...).
Usage
Create and convert
import { duration, TimeUnit } from '@pvorona/duration';
const d = duration(2, TimeUnit.Minute);
d.toSeconds(); // 120
d.toMilliseconds(); // 120_000
duration(250, TimeUnit.Millisecond).toSeconds(); // 0.25Compose from multiple units
import { duration } from '@pvorona/duration';
const total = duration({ minutes: 1, seconds: 30 });
total.toSeconds(); // 90
const negative = duration({ hours: -1, minutes: -30 });
negative.toMilliseconds(); // -5_400_000duration(parts) accepts only the exact plural keys milliseconds, seconds, minutes, hours, days, weeks, months, and years.
All non-zero parts must share the same sign. For example, duration({ hours: 1, minutes: -30 }) throws TypeError.
Compare
import { milliseconds, seconds } from '@pvorona/duration';
const a = seconds(1);
const b = milliseconds(900);
a.greaterThan(b); // true
a.compare(b); // 1Arithmetic
import { add, milliseconds, seconds } from '@pvorona/duration';
const total = add(seconds(1), milliseconds(500));
total.toMilliseconds(); // 1500Between dates
import { since } from '@pvorona/duration';
const startedAt = new Date(Date.now() - 2_000);
const elapsed = since(startedAt);
elapsed.toSeconds(); // ~2Apply a duration to a date
import { addTo, seconds, subtractFrom } from '@pvorona/duration';
const start = new Date(1_000);
addTo(start, seconds(2)).getTime(); // 3_000
subtractFrom(start, seconds(2)).getTime(); // -1_000Important semantics
- ESM-only package: use
import, notrequire(...). - Duration values are frozen branded objects created by this package. Use
isDuration(...)for unknown inputs instead of duck typing. - Compare durations by value with
d.equals(other)orisEqual(a, b), not===. - Do not spread, JSON-serialize, or structured-clone
Durationvalues as a transport format. Serialize your own explicit shape and reconstruct withmilliseconds(...)for finite values orinfinitefor the sentinel. DurationPartsis constructor input only. It is not a serializedDurationformat and should not be treated as one.instantis the exported zero-duration constant, but any zero-valued duration hasisInstant === true.- The public millisecond APIs are
TimeUnit.Millisecond,milliseconds(...), andtoMilliseconds(). Monthis treated as 30 days andYearas 365.25 days. These are approximations, not calendar-aware durations.- Negative finite durations are valid.
between(...),since(...), arithmetic, and comparisons can produce or operate on negative values. - Constructors accept only finite numeric inputs.
infiniteis the only supported non-finite duration. duration(parts)requires at least one supported plural key, rejects unknown keys, rejects non-finite part values, and rejects mixed-sign non-zero parts.add(infinite, x),subtract(infinite, finite),multiply(infinite, positive finite), anddivide(infinite, positive finite)returninfinite.addTo(date, duration)andsubtractFrom(date, duration)do exact timestamp arithmetic equivalent tonew Date(date.getTime() +/- duration.toMilliseconds()).addTo(...)andsubtractFrom(...)reject invalid dates, rejectinfinite, and reject result timestamps outside the JavaScriptDaterange.addTo(...)andsubtractFrom(...)are timestamp-based, not calendar-aware. Local clock time may shift across DST or timezone offset transitions.Datevalues only have millisecond precision, so the date helpers follow native JavaScriptDatesemantics for fractional-millisecond timestamps.- Invalid units, invalid dates, non-finite scalar inputs, divide-by-zero,
subtract(infinite, infinite),subtract(finite, infinite),multiply(infinite, 0),multiply(infinite, negative), anddivide(infinite, negative)throwTypeError.
API
TimeUnit
The package exports both:
type TimeUnit: the union of the supported runtime unit valuesconst TimeUnit: the runtime constants accepted byduration(value, unit)andd.to(unit)TimeUnit.MillisecondTimeUnit.SecondTimeUnit.MinuteTimeUnit.HourTimeUnit.DayTimeUnit.WeekTimeUnit.MonthTimeUnit.Year
type Duration
An opaque, immutable duration value. Duration instances are branded by the package, so use isDuration(...) for unknown inputs and compare values with equals(...) / isEqual(...) rather than ===.
Properties
isFinite: boolean:truefor finite durationsisInfinite: boolean:trueonly forinfiniteisInstant: boolean:truefor any zero duration (includinginstant)
Conversions
to(unit: TimeUnit): number: convert to an arbitrary unittoMilliseconds(): numbertoSeconds(): numbertoMinutes(): numbertoHours(): numbertoDays(): numbertoWeeks(): numbertoMonths(): number(30-day months)toYears(): number(365.25-day years)
Comparisons
equals(other: Duration): booleanlessThan(other: Duration): booleanlessThanOrEqual(other: Duration): booleangreaterThan(other: Duration): booleangreaterThanOrEqual(other: Duration): booleancompare(other: Duration): -1 | 0 | 1
Example:
import { milliseconds, seconds, type Duration } from '@pvorona/duration';
function isShort(d: Duration) {
return d.isFinite && d.toSeconds() < 5;
}
const a = seconds(1);
const b = milliseconds(900);
isShort(a); // true
a.greaterThan(b); // truetype DurationParts
Strict constructor input for duration(parts). Supported keys:
milliseconds?: numberseconds?: numberminutes?: numberhours?: numberdays?: numberweeks?: numbermonths?: numberyears?: number
At least one key is required. All non-zero keys must share the same sign.
Function API
Constructors
duration(value: number, unit: TimeUnit): Durationduration(parts: DurationParts): Durationmilliseconds(value: number): Durationseconds(value: number): Durationminutes(value: number): Durationhours(value: number): Durationdays(value: number): Durationweeks(value: number): Durationmonths(value: number): Durationyears(value: number): Duration
Date helpers
between(start: Date, end: Date): Durationsince(start: Date): DurationaddTo(date: Date, duration: Duration): DatesubtractFrom(date: Date, duration: Duration): Date
Arithmetic helpers
add(a: Duration, b: Duration): Durationsubtract(a: Duration, b: Duration): Durationmultiply(a: Duration, b: number): Durationdivide(a: Duration, b: number): Duration
Constants
instant: Durationinfinite: Duration
Guards / equality
isDuration(value: unknown): value is DurationisEqual(a: Duration, b: Duration): boolean
Example:
import { isDuration, seconds } from '@pvorona/duration';
const maybe: unknown = seconds(1);
if (isDuration(maybe)) {
maybe.toMilliseconds(); // ok, narrowed
}