Tempo Cookbook
A collection of recipes for solving common date and time challenges using Tempo.
Table of Contents
- The Basics
- Parsing Challenges
- Manipulation & Calculations
- Timezones & Locales
- Business Logic & Terms
- 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());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('{hh}:{mi} {mer}'); // "12:00 am"How do I check if a date is valid?
const t = new Tempo('invalid-date');
if (t.isValid) {
// ...
}Parsing Challenges
Parsing "Ambiguous" Digits (US vs UK)
Tempo uses your timezone to decide if 04012026 is April 1st or January 4th.
// US Context (en-US)
const us = new Tempo('04012026', { timeZone: 'America/New_York' });
console.log(us.format('{mon} {dd}')); // "April 01"
// UK/Elsewhere Context (en-GB)
const uk = new Tempo('04012026', { timeZone: 'Europe/London' });
console.log(uk.format('{mon} {dd}')); // "January 04"For digits-only input, Tempo checks the most likely compact interpretation first:
- A
6-digit string is always validated as compact time first (hhmiss) regardless oftimeZone—this validation is timezone-independent. Only if validation fails does Tempo then try compact date layouts (likeddmmyyormmddyy), wheretimeZonedecides month-day vs day-month order. - An
8-digit string is checked as a compact date, with month-day-year vs day-month-year decided bytimeZone.
const time = new Tempo('093015', { timeZone: 'UTC' });
console.log(time.format('{hh}:{mi}:{ss}')); // "09:30:15"
const shortDate = new Tempo('310559', { timeZone: 'Europe/London' });
console.log(shortDate.format('{yyyy}-{mm}-{dd}')); // "1959-05-31"
// Two-digit years use Tempo's sliding `pivot` window (default `pivot: 75`): values at/after the computed pivot map to the previous century (so `59` -> `1959` here), and you can change this via constructor/parser options like `new Tempo(input, { pivot: 50, timeZone: 'Europe/London' })`.
const usDate = new Tempo('04012026', { timeZone: 'America/New_York' });
console.log(usDate.format('{yyyy}-{mm}-{dd}')); // "2026-04-01"
const ukDate = new Tempo('04012026', { timeZone: 'Europe/London' });
console.log(ukDate.format('{yyyy}-{mm}-{dd}')); // "2026-01-04"To avoid ambiguity, prefer separators whenever you control the input format:
- Use
09:30:15instead of093015. - Use
2026-04-01,04/01/2026, or01/04/2026instead of04012026.
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');Parsing Unix Timestamps
Tempo handles both milliseconds (Number) and nanoseconds (BigInt).
new Tempo(1716163200000); // Milliseconds
new Tempo(1716163200000000000n); // NanosecondsManipulation & 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 });How do I jump to Term boundaries?
Use the # prefix with start or end to jump to semantic boundaries like Quarters or Seasons.
const qtrStart = new Tempo().set({ start: '#quarter' });
const sznEnd = new Tempo().set({ end: '#season' });How long until a deadline?
const t = new Tempo();
const daysLeft = t.until('2025-01-01', 'days');
console.log(`${daysLeft} days remaining`);Timezones & 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"Get "Now" in UTC
const utcNow = new Tempo({ timeZone: 'UTC' });Business Logic & 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"Unified Term Math
Tempo allows you to shift dates by semantic "steps" while preserving your relative position within the term.
const t1 = new Tempo('2024-05-15'); // Middle of Q2
const t2 = t1.add({ '#quarter': 1 }); // Middle of Q3: "2024-08-14" (approx)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"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.
Subscription Billing (Recurring Payments)
Use a seed to anchor your subscription to a specific day, then use a month-based ticker.
// Anchor to the 15th of the month
await using billing = Tempo.ticker({
months: 1,
seed: '2024-01-15'
}, (t) => processPayment(t));
// -- OR --
// Manual alternative for environments without 'await using' support:
const billing = Tempo.ticker({ months: 1, seed: '2024-01-15' }, (t) => processPayment(t));
// ... later ...
await billing[Symbol.asyncDispose](); // Explicitly clean up resourcesFiscal Quarter Reporting
Drive internal reporting cycles precisely when a new quarter begins.
// Shift automatically to the start of the current quarter
await using quarterly = Tempo.ticker({ '#quarter': 1 });
for await (const t of quarterly) {
generateReport(t.term.qtr);
}Daily Shift Management
Automatically update a UI when a daily time period (e.g., 'morning' or 'afternoon') changes.
using shiftTicker = Tempo.ticker({ '#period': 1 }, (t) => {
document.body.className = `shift-${t.term.per}`;
});
using dailyTicker = Tempo.ticker({ '#period': 'morning' }, (t) => {
document.body.className = `morning-has-broken`;
});Manual Sync (Action-Triggered)
Sometimes you want a ticker's logic but need to trigger it from an external event.
const heartbeat = Tempo.ticker({ seconds: 5 });
// Manually trigger a pulse from a UI button or WebSocket
button.onclick = () => {
const t = heartbeat.pulse();
console.log(`Manual pulse triggered at: ${t}`);
};
// Ensure cleanup on page unload or component unmount
window.onunload = () => heartbeat.stop();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 pDate = 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