Version v2.0.0 Size 3.7 KB gzip
Why Tempo?
Date/time bugs come from treating an instant and a wall-clock value as interchangeable. Tempo requires an explicit parse target and requires timeZone whenever a wall-clock value becomes an instant.
ts
// Before
const reminder = new Date(meeting.getTime() - 15 * 60_000);
// After
import { parse, shift, toInstant } from '@vielzeug/tempo';
const localMeeting = parse('2026-03-21T10:30:00', { as: 'plainDateTime' });
const meeting = toInstant(localMeeting, { timeZone: 'America/New_York' });
const reminder = shift(meeting, { minutes: -15 }, { timeZone: 'America/New_York' });| Feature | Tempo | date-fns | Native Date |
|---|---|---|---|
| Bundle size | 3.7 KB | ~10 kB | 0 kB |
| Zero dependencies | @js-temporal/polyfill | ||
| Explicit wall-time conversion | Manual | ||
| DST-safe arithmetic | Manual | Manual | |
| Localized formatting | Intl |
Use Tempo when you need Temporal values, explicit timezone rules, and DST-safe operations.
Consider native Date when your data is only elapsed milliseconds and you do not need calendar or timezone behavior.
Installation
sh
pnpm add @vielzeug/temposh
npm install @vielzeug/temposh
yarn add @vielzeug/tempoQuick Start
Parse a wall-clock input explicitly, attach its timezone, then format it for a user.
ts
import { format, inTimeZone, parse, shift, toInstant } from '@vielzeug/tempo';
const localMeeting = parse('2026-03-21T10:30:00', { as: 'plainDateTime' });
const meeting = toInstant(localMeeting, { timeZone: 'America/New_York' });
const reminder = shift(meeting, { minutes: -15 }, { timeZone: 'America/New_York' });
const text = format(inTimeZone(reminder, 'America/New_York'), {
locale: 'en-US',
pattern: 'short',
});Features
parse()— Requires an explicit ISO target: instant, zoned date-time, plain date-time, or plain date.toInstant()/inTimeZone()— Convert wall-clock and absolute values with explicit timezone semantics.shift()/difference()— Perform DST-safe arithmetic and duration calculation.contains()/clamp()— Use named range fields instead of ambiguous positional inputs.classifyExpiry()— Classify fixed elapsed-time thresholds in milliseconds or larger units without month or year approximation.format()/formatRelative()/formatDuration()— Render UI, relative, and duration values throughIntl.dateRange()/recurrence()— Lazily generate zoned calendar sequences.