Skip to content

Tempo Cookbook

A collection of recipes for solving common date and time challenges using Tempo.

Table of Contents

  1. The Basics
  2. Parsing Challenges
  3. Manipulation & Calculations
  4. Timezones & Locales
  5. Business Logic & Terms
  6. Interoperability

The Basics

How do I get the current date and time?

By default, the constructor returns "now".

typescript
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.

typescript
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?

typescript
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.

typescript
// 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 of timeZone—this validation is timezone-independent. Only if validation fails does Tempo then try compact date layouts (like ddmmyy or mmddyy), where timeZone decides 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 by timeZone.
typescript
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:15 instead of 093015.
  • Use 2026-04-01, 04/01/2026, or 01/04/2026 instead of 04012026.

Handling Relative Strings

Tempo natively understands human-readable offsets.

typescript
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).

typescript
new Tempo(1716163200000);         // Milliseconds
new Tempo(1716163200000000000n);  // Nanoseconds

Manipulation & Calculations

Add or Subtract Time

Tempo instances are immutable; add() returns a new instance.

typescript
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.

typescript
const qtrStart = new Tempo().set({ start: '#quarter' });
const sznEnd = new Tempo().set({ end: '#season' });

How long until a deadline?

typescript
const t = new Tempo();
const daysLeft = t.until('2025-01-01', 'days');
console.log(`${daysLeft} days remaining`);

Timezones & Locales

Convert Time to Another Zone

typescript
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

typescript
const utcNow = new Tempo({ timeZone: 'UTC' });

Business Logic & Terms

Is it the weekend?

typescript
const t = new Tempo();
const isWeekend = t.dow >= 6; // Saturday = 6, Sunday = 7

What Fiscal Quarter are we in?

Using the qtr Term plugin (term.qtr is a convenient alias for the full term.quarter property).

typescript
const t = new Tempo();
console.log(`Current Quarter: ${t.term.qtr}`); // "Q1", "Q2", etc.

Hemispheric Seasons

Tempo Terms are hemisphere-aware.

typescript
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.

typescript
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.

typescript
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.

typescript
// 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 resources

Fiscal Quarter Reporting

Drive internal reporting cycles precisely when a new quarter begins.

typescript
// 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.

typescript
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.

typescript
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

typescript
const date = new Tempo().toDate();
const tempo = new Tempo(new Date());

Converting to Temporal Objects

typescript
const zdt = new Tempo().toDateTime();  // Temporal.ZonedDateTime
const instant = new Tempo().toInstant(); // Temporal.Instant
const pDate = new Tempo().toPlainDate(); // Temporal.PlainDate

Sorting an array of Tempos

typescript
const dates = [new Tempo('tomorrow'), new Tempo('yesterday'), new Tempo('today')];
dates.sort(Tempo.compare); // Sorts chronologically

Released under the MIT License.