calendar()
Returns a context-aware calendar label in the style of messaging apps and file managers: "Today at 2:05 PM", "Yesterday at 9:30 AM", "Monday at 3:00 PM", "Apr 9, 2026".
Signature
function calendar(
temporalObj: CalendarInput,
reference?: Temporal.PlainDate,
options?: CalendarOptions
): string
type CalendarInput =
| Temporal.PlainDate
| Temporal.PlainDateTime
| Temporal.ZonedDateTime
| Temporal.Instant
interface CalendarOptions {
/** BCP 47 locale for weekday/month names and the time portion. */
locale?: string
/**
* Moment.js-style token string for the time suffix.
* @default 'h:mm A'
*/
timeFormat?: string
/** Override the day words and the "at" connector. */
labels?: {
today?: string // @default 'Today'
yesterday?: string // @default 'Yesterday'
tomorrow?: string // @default 'Tomorrow'
at?: string // @default 'at'
}
}Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
temporalObj | CalendarInput | required | The date (or datetime) to label. PlainTime is unsupported. |
reference | Temporal.PlainDate | Temporal.Now.plainDateISO() | The "today" to compare against. |
options.locale | string | runtime default | Locale for names and the time suffix. |
options.timeFormat | string | 'h:mm A' | Token string for the appended time. |
options.labels | object | English defaults | Override the day words and at connector. |
Day-range logic
calendar() compares calendar days between temporalObj and reference (via PlainDate.until), not elapsed seconds.
| Day difference | Output | Example |
|---|---|---|
0 | today label + at + time | Today at 2:05 PM |
-1 | yesterday label + at + time | Yesterday at 9:30 AM |
+1 | tomorrow label + at + time | Tomorrow at 3:00 PM |
-6 … -2 and +2 … +6 | weekday name + at + time | Monday at 2:05 PM |
≤ -7 or ≥ +7 | MMM D, YYYY (date only) | Apr 9, 2026 |
The far-past/future fallback always includes the year. When the input is a PlainDate there is no time component, so the at + time suffix is omitted entirely (Today, Monday, …).
Examples
PlainDate (no time suffix)
import { calendar } from 'moment-less'
const ref = Temporal.PlainDate.from('2026-04-09')
calendar(Temporal.PlainDate.from('2026-04-09'), ref) // → "Today"
calendar(Temporal.PlainDate.from('2026-04-08'), ref) // → "Yesterday"
calendar(Temporal.PlainDate.from('2026-04-10'), ref) // → "Tomorrow"
calendar(Temporal.PlainDate.from('2026-04-06'), ref) // → "Monday"
calendar(Temporal.PlainDate.from('2025-11-15'), ref) // → "Nov 15, 2025"PlainDateTime (with time)
import { calendar } from 'moment-less'
const ref = Temporal.PlainDate.from('2026-04-09')
calendar(Temporal.PlainDateTime.from('2026-04-09T11:00'), ref) // → "Today at 11:00 AM"
calendar(Temporal.PlainDateTime.from('2026-04-08T09:30'), ref) // → "Yesterday at 9:30 AM"
calendar(Temporal.PlainDateTime.from('2026-04-07T15:00'), ref) // → "Tuesday at 3:00 PM"Instant (UTC)
An Instant is compared and displayed in UTC. For local time, convert to a ZonedDateTime first.
import { calendar } from 'moment-less'
const ref = Temporal.PlainDate.from('2026-04-09')
const sent = Temporal.Instant.from('2026-04-09T09:15:00Z')
calendar(sent, ref) // → "Today at 9:15 AM"Custom time format
timeFormat takes any format() token string.
import { calendar } from 'moment-less'
const ref = Temporal.PlainDate.from('2026-04-09')
const dt = Temporal.PlainDateTime.from('2026-04-09T14:05')
calendar(dt, ref, { timeFormat: 'HH:mm' }) // → "Today at 14:05"
calendar(dt, ref, { timeFormat: 'h:mm:ss A' }) // → "Today at 2:05:00 PM"Localization
Set locale for names and time, and override labels (including the at connector) for the surrounding copy.
import { calendar } from 'moment-less'
const ref = Temporal.PlainDate.from('2026-04-09')
const dt = Temporal.PlainDateTime.from('2026-04-08T14:05')
calendar(dt, ref, {
locale: 'fr',
timeFormat: 'HH:mm',
labels: { yesterday: 'Hier', at: 'à' },
})
// → "Hier à 14:05"Throws for PlainTime
A Temporal.PlainTime has no calendar component, so calendar() cannot place it on a day:
calendar(Temporal.PlainTime.from('14:05'))
// Error: calendar() cannot be used with Temporal.PlainTime — it has no date context