Tempo Cookbook
A collection of recipes for solving common date and time challenges using Tempo.
Table of Contents
- The Basics
- Parsing Challenges
- Manipulation and Calculations
- Timezones and Locales
- Business Logic and Terms
- Formatting and Localization
- Interoperability
The Basics
How do I get the current date and time?
By default, the constructor returns "now".
const now = new Tempo();
console.log(now.toString());Get "Now" in UTC
const utcNow = new Tempo({ timeZone: 'UTC' });How do I format a date for my UI?
Use the placeholder syntax in the .format() method.
const t = new Tempo('2024-12-25');
t.format('{dd} {mon} {yyyy}'); // "25 December 2024"
t.format('{h12}:{mi}'); // "12:00am"How do I check if a date is valid?
const t = new Tempo('invalid-date');
if (t.isValid) {
// ...
}Global Configuration
You can initialize global defaults that apply to all future Tempo instances.
Tempo.init({
timeZone: 'UTC',
locale: 'en-GB',
silent: true
});👉 Learn More: Configuration Guide
Parsing Challenges
Parsing "Ambiguous" Digits (US vs UK)
Tempo intelligently resolves ambiguous dates like 04012026 based on your timezone.
const us = new Tempo('04012026', { timeZone: 'America/New_York' });
console.log(us.format('{mon} {dd}')); // "April 01"👉 Learn More: Ambiguity Resolution Guide
Handling Relative Strings
Tempo natively understands human-readable offsets.
new Tempo('yesterday');
new Tempo('next Friday');
new Tempo('2 weeks ago');
new Tempo('tomorrow afternoon');Localized Parsing Modifiers
Tempo allows you to localize term modifiers (like next, last, this) by defining them in the registry.modifiers configuration. This lets you seamlessly mix localized terms into your relative string parsing and even use them in the # shorthand navigation engine!
Tempo.init({
locale: 'fr-FR', // Natively translates months & weekdays
registry: {
modifiers: {
'>': ['prochain', 'suivant'],
'<': ['dernier', 'passé'],
'=': ['ce', 'cette']
}
}
});
new Tempo('next Friday'); // English always works natively
new Tempo('vendredi prochain'); // Pure French localized string!
new Tempo('#qtr.dernier'); // Localized shorthand navigationParsing Unix Timestamps
Tempo handles both milliseconds (Number) and nanoseconds (BigInt).
new Tempo(1716163200000); // Milliseconds
new Tempo(1716163200000000000n); // NanosecondsManipulation and Calculations
Add or Subtract Time
Tempo instances are immutable; add() returns a new instance.
const deadline = new Tempo().add({ days: 7, hours: 2 });
const past = new Tempo().add({ months: -1 });
// You can also step by semantic Terms using the `#` prefix!
const t1 = new Tempo('2024-05-15'); // Middle of Q2
const t2 = t1.add({ '#quarter': 1 }); // Middle of Q3: "2024-08-14" (approx)Jumping to Boundaries (start, mid, end)
The .set() method allows you to jump to the boundaries of native units (like months or years) or semantic Terms (using the # prefix). You can specify whether to land on the inclusive start, inclusive end, or the exact center.
// Native Units
const monthStart = new Tempo().set({ start: 'month' });
// Semantic Terms (Lands on 30-Sep 23:59:59.999... Inclusive End)
const qtrEnd = new Tempo().set({ end: '#quarter' });
// Lands on the arithmetic nanosecond midpoint of the period
const qtrMid = new Tempo().set({ mid: '#quarter' });Slick Object Mutations
You can navigate relative to your current date by using Slick Shorthand operators inside .set(). Use the snippet shorthand keys (yy, mm, ww, dd, wkd, etc.) and provide a string payload containing a directional modifier:
const t = new Tempo('2024-05-20'); // Monday
// Jump forward two months
t.set({ mm: '>2' }); // July 20th
// Jump to the next Friday
t.set({ wkd: '>Fri' }); // May 24th
// Jump to the previous Wednesday
t.set({ wkd: '<Wed' }); // May 15thBecause .set() processes keys in insertion order, you can now effortlessly combine absolute assignments and Slick shifts in a single pass to build complex boundaries:
// Jump 2 months forward, find the next Friday, and set the time to 10:30 AM
const t2 = t.set({
mm: '>2',
wkd: '>Fri',
hour: 10,
minute: 30
});⚠️ ESLint sort-keys Warning
Because mixed object payloads execute strictly in the order they are defined, you must be careful if you use aggressive automated linters (like ESLint's sort-keys auto-fixer). If your linter alphabetically re-orders your properties, your math will execute in the wrong order! If your codebase forces alphabetical object keys, stick to chaining: t.set({ mm: '>2' }).set({ wkd: '>Fri' }).
This syntax fully supports advanced shifting (e.g. <=, >=), double-negations (>-2), and localized modifier aliases, providing a clean programmatic interface for date construction.
💡 Why can't I use Slick modifiers on timezones (tzd)?
Changing a timezone (tzd or timeZone) does not traverse the timeline—it merely changes the local representation of the exact same absolute moment in time. Because no temporal displacement occurs, applying directional Slick modifiers (like >) to a timezone is logically invalid and unsupported.
How long until a deadline? (until)
const t = new Tempo();
const daysLeft = t.until('2025-01-01', 'days');
console.log(`${daysLeft} days remaining`);Relative Time (since)
Generate human-readable relative time strings instantly.
const t = new Tempo('yesterday');
console.log(t.since()); // "1d ago"Timezones and Locales
Convert Time to Another Zone
const nyc = new Tempo('2024-05-20 10:00', { timeZone: 'America/New_York' });
const london = nyc.set({ timeZone: 'Europe/London' });
console.log(nyc.format('{hh}:{mi}')); // "10:00"
console.log(london.format('{hh}:{mi}')); // "15:00"Business Logic and Terms
Is it the weekend?
const t = new Tempo();
const isWeekend = t.dow >= 6; // Saturday = 6, Sunday = 7What Fiscal Quarter are we in?
Using the qtr Term plugin (term.qtr is a convenient alias for the full term.quarter property).
const t = new Tempo();
console.log(`Current Quarter: ${t.term.qtr}`); // "Q1", "Q2", etc.Hemispheric Seasons
Tempo Terms are hemisphere-aware.
const sydney = new Tempo('2024-07-01', { sphere: 'south' });
console.log(sydney.term.szn); // "Winter"
const london = new Tempo('2024-07-01', { sphere: 'north' });
console.log(london.term.szn); // "Summer"
// or even via the timeZone setting
console.log(new Tempo({ timeZone: 'America/New_York' }).term.szn); // "Summer"
console.log(new Tempo({ timeZone: 'Australia/Sydney' }).term.szn); // "Winter"Formatting and Localization
Semantic Formatting
Use specific Term tokens like {#quarter} or {#season} to automatically embed a Term's label (or key) into a format string.
const t = new Tempo();
console.log(t.format('We are currently in the {#quarter}')); // "We are currently in the First Quarter"Format Modifiers & Localization
Format strings support chained colon-modifiers (e.g., :upper, :locale, :ord) to dynamically change the presentation casing or delegate to the native Intl API. You can stack them to get the exact presentation required!
const t = new Tempo('2024-05-15 15:30', { locale: 'fr-FR' });
t.format('{mon:upper}'); // "MAY" (English Default -> UpperCase)
t.format('{mon:locale}'); // "mai" (Native French Intl output)
t.format('{mon:locale:upper} {dd}'); // "MAI 15" (Native French Intl output)👉 Learn More: See the Smart Formatting Guide for the complete list of available modifiers.
TIP
Tired of typing :locale?
If you find yourself repeatedly writing :locale for the same date structure, save it to the global FORMATS registry! This creates a clean, reusable shortcut:
Tempo.init({
locale: 'fr-FR',
registry: {
formats: {
'ui-date': '{wkd:locale}, {dd:raw} {mon:locale} {yyyy}'
}
}
});
t.format('ui-date'); // Resolved with all modifiers intact!Note: Format keys are resolved case-sensitively from the global registry.formats object. An error will be thrown if the requested key is not found in the registry.
Custom Format Tokens
Need a completely custom format behavior? You can define dynamic token evaluators in the registry that receive the zdt (Temporal.ZonedDateTime) instance and a context object, giving you full native access to Intl formatting or custom logic.
Tempo.init({
locale: 'fr-FR',
registry: {
tokens: {
// A simple math-based token
'myDay': (zdt) => zdt.day - 1,
// A deeply localized custom token using native Intl
'wkd-fr': (zdt, { config }) => {
const dtOptions = config?.intl?.dateTimeFormat ?? {};
return zdt.toLocaleString(config?.locale ?? 'en', { ...dtOptions, weekday: 'long' });
}
}
}
});
const t = new Tempo('2024-05-20');
t.format('{myDay}'); // "19"
t.format('{wkd-fr}'); // "lundi"👉 Learn More:
INFO
The examples below use the using and await using syntax, which require TypeScript 5.2+ and a runtime that supports TC39 Explicit Resource Management.
Ticker Plugin
The Ticker engine is a premium feature. 👉 Learn More: Ticker Plugin Documentation
Interval-Based Ticker (Recurring Billing)
Use a seed to anchor your ticker to a specific day, then use a month-based interval:
await using billing = Tempo.ticker({
months: 1,
seed: '2024-01-15'
}, (t) => processPayment(t));Term-Driven Ticker (Fiscal Quarter Reporting)
Drive internal reporting cycles precisely when a new quarter begins:
await using quarterly = Tempo.ticker({ '#quarter': 1 });
for await (const t of quarterly) {
generateReport(t.term.qtr);
}Interoperability
Converting to / from Native Date
const date = new Tempo().toDate();
const tempo = new Tempo(new Date());Converting to Temporal Objects
const zdt = new Tempo().toDateTime(); // Temporal.ZonedDateTime
const instant = new Tempo().toInstant(); // Temporal.Instant
const pdt = new Tempo().toPlainDate(); // Temporal.PlainDateSorting an array of Tempos
const dates = [new Tempo('tomorrow'), new Tempo('yesterday'), new Tempo('today')];
dates.sort(Tempo.compare); // Sorts chronologically