Skip to content

API Overview

SymbolPurposeExecution modeCommon gotcha
parse()Parse ISO text to an explicit Temporal kindSyncas is required
isValid()Narrow an unknown runtime value to TimeInputSyncDoes not parse strings
toInstant()Resolve a value as an absolute instantSyncPlain values require timeZone
inTimeZone()Project a value to a zoneSyncPreserves instant, changes wall-clock fields
shift() / difference()Timezone-aware arithmeticSyncCalendar operations require a timezone
isBefore() / isAfter() / isSame()Compare absolute values or calendar unitsSyncPlain values require timeZone
contains() / clamp()Compare normalized rangesSyncBounds normalize automatically
startOf() / endOf()Resolve calendar boundariesSyncWeeks default to Monday
dateRange() / recurrence()Generate lazy zoned sequencesSyncSteps and intervals must advance
classifyExpiry()Classify fixed elapsed-time thresholdsSyncMonths and years are rejected
format() familyLocalized and machine formattingSyncUse timeZone, not tz

Package Entry Point

ImportPurpose
@vielzeug/tempoTempo utilities, errors, types, and shared Temporal namespace

Core Functions

parse(input, { as })

ts
parse(input: string, options: { as: 'instant' }): Temporal.Instant;
parse(input: string, options: { as: 'zonedDateTime' }): Temporal.ZonedDateTime;
parse(input: string, options: { as: 'plainDateTime' }): Temporal.PlainDateTime;
parse(input: string, options: { as: 'plainDate' }): Temporal.PlainDate;

Parses an ISO 8601 string as the requested temporal kind.

Parameters

ParameterTypeDescription
inputstringISO 8601 input
options.asParseAsRequired result kind

Returns: Requested Temporal value.

Example:

ts
import { parse } from '@vielzeug/tempo';

const instant = parse('2026-03-21T10:15:30Z', { as: 'instant' });

isValid(value)

ts
isValid(value: unknown): value is TimeInput;

Returns whether value is a Tempo-supported Temporal value. It does not parse ISO strings.

Example:

ts
import { isValid, parse } from '@vielzeug/tempo';

const value: unknown = parse('2026-03-21T10:15:30Z', { as: 'instant' });
const valid = isValid(value); // true

now({ timeZone }) / nowInstant()

ts
now(options: { timeZone: string }): Temporal.ZonedDateTime;
nowInstant(): Temporal.Instant;

Returns current zoned or absolute time.

Example:

ts
import { now, nowInstant } from '@vielzeug/tempo';

now({ timeZone: 'Europe/Berlin' });
nowInstant();

toInstant(input, options?) / inTimeZone(input, timeZone)

ts
toInstant(input: AbsoluteTime): Temporal.Instant;
toInstant(input: WallTime, options: { timeZone: string } & DisambiguationOptions): Temporal.Instant;
inTimeZone(input: TimeInput, timeZone: string): Temporal.ZonedDateTime;

toInstant() resolves wall-clock values. inTimeZone() projects a value into a requested timezone.

Example:

ts
import { inTimeZone, parse, toInstant } from '@vielzeug/tempo';

const local = parse('2026-11-01T01:30:00', { as: 'plainDateTime' });
const instant = toInstant(local, { disambiguation: 'later', timeZone: 'America/New_York' });
inTimeZone(instant, 'Europe/Berlin');

Arithmetic and Comparison

shift(input, duration, options?)

ts
shift(input: Temporal.ZonedDateTime, duration: Temporal.DurationLike, options?: ShiftOptions): Temporal.ZonedDateTime;
shift(input: Exclude<TimeInput, Temporal.ZonedDateTime>, duration: Temporal.DurationLike, options: ShiftOptions & { timeZone: string }): Temporal.ZonedDateTime;

Adds a duration through zoned calendar rules.

Returns: Temporal.ZonedDateTime.


difference({ start, end, ...options })

ts
difference(input: DifferenceInput): Temporal.Duration;

Returns the signed duration from start to end with optional Temporal rounding.

Returns: Temporal.Duration.


isBefore() / isAfter() / isSame()

ts
isBefore(a: TimeInput, b: TimeInput, options?: CompareOptions): boolean;
isAfter(a: TimeInput, b: TimeInput, options?: CompareOptions): boolean;
isSame(a: TimeInput, b: TimeInput, options?: CompareOptions): boolean;

Compare absolute instants or their containing calendar units.

Returns: A boolean comparison result.


contains({ value, start, end, ...options }) / clamp({ value, start, end, ...options })

ts
contains(input: ContainsInput): boolean;
clamp(input: ClampInput & { value: Temporal.ZonedDateTime }): Temporal.ZonedDateTime;
clamp(input: ClampInput): Temporal.Instant;

Normalizes reversed bounds, then checks or constrains the value.

Returns: contains() returns boolean; clamp() returns the normalized value or nearest bound.


startOf() / endOf()

ts
startOf(input: TimeInput, unit: BoundaryUnit, options?: BoundaryOptions): Temporal.ZonedDateTime;
endOf(input: TimeInput, unit: BoundaryUnit, options?: BoundaryOptions): Temporal.ZonedDateTime;

Returns the first or last nanosecond of a timezone-aware calendar unit.

Returns: Temporal.ZonedDateTime.

Calendar Sequences

dateRange(start, end, step, options?)

ts
dateRange(start: TimeInput, end: TimeInput, step: Temporal.DurationLike, options?: TimeZoneOptions): Generator<Temporal.ZonedDateTime>;

Returns an inclusive lazy zoned range and rejects steps that do not advance time.

Returns: Generator<Temporal.ZonedDateTime>.


recurrence(start, rule, options?)

ts
recurrence(start: TimeInput, rule: RecurrenceRule, options?: TimeZoneOptions): Generator<Temporal.ZonedDateTime>;

Returns a count- or date-limited lazy recurrence. Intervals must be positive safe integers; counts must be non-negative safe integers.

Returns: Generator<Temporal.ZonedDateTime>.

Formatting

format(input, options?)

ts
format(input: TimeInput, options?: FormatOptions): string;

Formats a value through Intl.DateTimeFormat.

Example:

ts
import { format, parse } from '@vielzeug/tempo';

format(parse('2026-03-21T10:15:30Z', { as: 'instant' }), {
  locale: 'en-GB',
  pattern: 'short',
  timeZone: 'UTC',
});

formatInstant() / formatZoned() / formatRelative() / formatDuration()

ts
formatInstant(input: TimeInput, options?: TimeZoneOptions): string;
formatZoned(input: TimeInput, options?: TimeZoneOptions): string;
formatRelative(input: RelativeTimeInput, options?: RelativeFormatOptions): string;
formatDuration(input: string | Temporal.DurationLike, options?: DurationFormatOptions): string;

formatInstant() produces UTC transport text (timeZone needed for wall-time input, ignored for Instant). formatZoned() produces zoned ISO text (timeZone required for non-ZonedDateTime input). formatRelative() uses fixed units for short spans and complete calendar months or years in the requested or inferred timezone. Zoned inputs with different zones require options.timeZone. formatDuration() falls back to English when Intl.DurationFormat is unavailable.

formatParts() / formatRange() / formatRangeParts()

ts
formatParts(input: TimeInput, options?: FormatOptions): Intl.DateTimeFormatPart[];
formatRange(start: TimeInput, end: TimeInput, options?: FormatOptions): string;
formatRangeParts(
  start: TimeInput,
  end: TimeInput,
  options?: FormatOptions,
): ReturnType<Intl.DateTimeFormat['formatRangeToParts']>;

Return Intl parts or localized range strings using FormatOptions.

parseDuration() / humanize()

ts
parseDuration(input: string | Temporal.DurationLike): Temporal.Duration;
humanize(diff: TimeDiffResult, options?: { locale?: Intl.LocalesArgument }): string;

humanize() localizes numbers only. Unit names remain English.

Classification

classifyExpiry({ value, thresholds, relativeTo?, timeZone? })

ts
classifyExpiry<K extends string>(input: ClassifyExpiryInput<K>): K | null;

Classifies an expiry against fixed elapsed-time thresholds in milliseconds or larger units. Months and years throw TempoInvalidInputError.

timeDiff(a, b?, options?)

ts
timeDiff(a: TimeInput, b?: TimeInput, options?: TimeZoneOptions): TimeDiffResult;

Returns absolute calendar difference in its largest meaningful unit. Uses Temporal's calendar-aware .since() for correct month and year handling.

Types

ts
type AbsoluteTime = Temporal.Instant | Temporal.ZonedDateTime;
type WallTime = Temporal.PlainDate | Temporal.PlainDateTime;
type TimeInput = AbsoluteTime | WallTime;
type RelativeTimeInput = AbsoluteTime;
type ParseAs = 'instant' | 'plainDate' | 'plainDateTime' | 'zonedDateTime';
type Disambiguation = 'compatible' | 'earlier' | 'later' | 'reject';
type CalendarUnit = 'day' | 'month' | 'week' | 'year';
type BoundaryUnit = 'day' | 'hour' | 'minute' | 'month' | 'week' | 'year';
type WeekStartDay = 1 | 2 | 3 | 4 | 5 | 6 | 7;
type FormatPattern = 'date-only' | 'long' | 'medium' | 'short' | 'time-only';
type TempoUnit = 'day' | 'hour' | 'microsecond' | 'millisecond' | 'minute' | 'month' | 'nanosecond' | 'second' | 'week' | 'year';
type FixedDuration = Pick<Temporal.DurationLike, 'days' | 'hours' | 'microseconds' | 'milliseconds' | 'minutes' | 'nanoseconds' | 'seconds' | 'weeks'>;
type ExpiryThresholds<K extends string> = Record<K, FixedDuration>;
type TimeDiffUnit = Exclude<TempoUnit, 'microsecond' | 'nanosecond'>;
type TimeDiffResult = { unit: TimeDiffUnit; value: number };

interface TimeZoneOptions { timeZone?: string }
interface DisambiguationOptions { disambiguation?: Disambiguation }
interface ShiftOptions extends DisambiguationOptions, TimeZoneOptions {}
interface DifferenceInput extends DisambiguationOptions, TimeZoneOptions {
  end: TimeInput;
  largestUnit?: Temporal.DateTimeUnit;
  roundingIncrement?: number;
  roundingMode?: Temporal.RoundingMode;
  smallestUnit?: Temporal.DateTimeUnit;
  start: TimeInput;
}
interface BoundaryOptions extends TimeZoneOptions { weekStartsOn?: WeekStartDay }
interface CompareOptions extends TimeZoneOptions { unit?: BoundaryUnit; weekStartsOn?: WeekStartDay }
interface ContainsInput extends CompareOptions { end: TimeInput; start: TimeInput; value: TimeInput }
interface ClampInput extends CompareOptions { end: TimeInput; start: TimeInput; value: TimeInput }
type FormatOptions =
  | { intl: Intl.DateTimeFormatOptions; locale?: Intl.LocalesArgument; pattern?: never; timeZone?: string }
  | { intl?: never; locale?: Intl.LocalesArgument; pattern?: FormatPattern; timeZone?: string };
interface RelativeFormatOptions extends TimeZoneOptions {
  base?: RelativeTimeInput;
  locale?: Intl.LocalesArgument;
  numeric?: Intl.RelativeTimeFormatNumeric;
  style?: Intl.RelativeTimeFormatStyle;
}
interface DurationFormatOptions {
  locale?: Intl.LocalesArgument;
  style?: 'digital' | 'long' | 'narrow' | 'short';
}
interface ClassifyExpiryInput<K extends string> extends TimeZoneOptions {
  value: TimeInput;
  thresholds: ExpiryThresholds<K>;
  relativeTo?: Temporal.Instant;
}
type RecurrenceRule = { frequency: 'daily' | 'monthly' | 'weekly' | 'yearly'; interval?: number } &
  ({ count: number; until?: TimeInput } | { count?: number; until: TimeInput });

Errors

ErrorTriggerNotable properties
TempoErrorBase Tempo errorinstanceof TempoError narrows every subtype
TempoInvalidInputErrorInvalid parse, duration, or fixed-threshold inputExtends TempoError
TempoInvalidTzErrorInvalid IANA zone or offsetExtends TempoError
TempoMissingTzErrorWall time without required timeZoneExtends TempoError
TempoUnsupportedInputErrorNon-Temporal input passed to conversionExtends TempoError

Every Tempo error thrown from parse(), parseDuration(), and timezone validation carries the original Temporal error on its cause property (ES2022). Inspect err.cause for root-cause details when debugging.