anywhen API reference
Overview
anywhen is a tiny date formatter built entirely on the native Intl browser API. One function, one options object, three modes. Stable since 1.0 — the public API follows semver.
The browser already knows how to format dates in 200+ languages. anywhen just makes that API pleasant to use.
import { anywhen } from 'anywhen'
anywhen(date)
// "yesterday, 2:35 PM" — smart mode (default)
anywhen(date, { mode: 'absolute', locale: 'en' })
// "Feb 5, 2016"
anywhen(date, { mode: 'relative', locale: 'en' })
// "3 hours ago"Install
npm install anywhen
# or
pnpm add anywhen
# or
yarn add anywhenOr take the whole family at once with npm install anyfamily.
anywhen()
The single entry point. Pass a date, optionally pass options.
anywhen(input)
anywhen(input, options?)
anywhen(date)
// runtime locale, smart mode
anywhen(date, { locale: 'en' })
// "yesterday, 2:35 PM"
anywhen(date, { mode: 'relative', locale: 'en', numeric: true })
// "1 day ago"
anywhen(date, {
mode: 'absolute',
locale: 'en',
format: { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' },
})
// "Friday, February 5, 2016"anywhenParts()
Same arguments as anywhen(), but returns the output as { type, value, unit? } parts instead of a string — style the number apart from the unit, or rebuild the output your own way. New in 1.0.
import { anywhenParts } from 'anywhen'
anywhenParts(date, { mode: 'relative', locale: 'en' })
// [
// { type: 'integer', value: '3', unit: 'hour' },
// { type: 'literal', value: ' hours ago' },
// ]
// React: bold the number
anywhenParts(date, { mode: 'relative' }).map((p, i) =>
p.type === 'integer' ? <b key={i}>{p.value}</b> : p.value,
)Note: part values keep the original Intl characters — the space before AM/PM can be U+202F (narrow no-break space), which some engines replace with a regular space in the joined string.
Modes
The mode option picks the rendering strategy. Each mode reads only the options that apply to it — the rest are ignored.
smart (default)
Context-aware. Picks the most readable format based on distance from now — covers past and future.
reads: locale, now, time, timeZone, style, thresholds
absolute
Plain date formatting via Intl.DateTimeFormat. Pass format to control the output shape.
anywhen(date, { mode: 'absolute', locale: 'en' })
// "Feb 5, 2016"
anywhen(date, {
mode: 'absolute',
locale: 'en',
format: { hour: '2-digit', minute: '2-digit' },
})
// "2:35 PM"
anywhen(date, {
mode: 'absolute',
locale: 'en',
format: { month: 'long', year: 'numeric' },
timeZone: 'Europe/Belgrade',
})
// "February 2016"reads: locale, format, timeZone
relative
Always relative. Past and future. Never falls back to an absolute date.
anywhen(date, { mode: 'relative', locale: 'en' })
// "3 hours ago"
// "yesterday"
// "in 2 weeks"
anywhen(date, { mode: 'relative', locale: 'en', numeric: true })
// "1 day ago" — disables auto-phrases
// "1 week ago"
anywhen(date, { mode: 'relative', locale: 'en', style: 'short' })
// "3 hr. ago"
anywhen(date, { mode: 'relative', locale: 'en', style: 'narrow' })
// "3h ago"reads: locale, now, numeric, style, thresholds
Options
mode'smart' | 'absolute' | 'relative'default: 'smart'Rendering strategy. Each mode reads only the options that apply to it.
localestring | string[]default: runtime localeAny valid BCP 47 locale tag, or a fallback array — 'en', 'en-US', 'zh-TW', ['sr-Latn-RS', 'en'].
nowDate | number | stringdefault: current timeReference time for smart and relative modes. Pass this in SSR to keep server and client output stable.
timeZonestringdefault: runtime timezoneIANA time zone for the displayed clock and smart day boundaries (today, yesterday, weekday). Used by smart and absolute modes.
timebooleandefault: trueSmart mode only. Whether to include clock time in today/yesterday/weekday output.
numericbooleandefault: falseRelative mode only. Force numeric output — disables auto-phrases like 'yesterday' or 'last week'.
style'long' | 'short' | 'narrow'default: 'long'Smart and relative modes. Maps to Intl.RelativeTimeFormat and shortens the relative phrasing — '10 min. ago', '3h ago'. Calendar labels keep their clock.
formatIntl.DateTimeFormatOptionsdefault: { day, month, year }Absolute mode only. Any options accepted by Intl.DateTimeFormat. Defaults to a short date.
thresholdsPartial<Record<unit, number>>default: built-in tableSmart and relative modes. Per-unit cutoffs (in seconds) for picking the display unit. Override any subset; the rest keep their defaults.
Thresholds
Each unit is shown while the distance from now is below its cutoff, in seconds. Defaults: second: 45, minute: 2700, hour: 79200, day: 518400, week: 2160000, month: 28512000. New in 1.0.
anywhen(date, { mode: 'relative', locale: 'en', thresholds: { minute: 5400 } })
// 50 minutes ago → "50 minutes ago" instead of "1 hour ago"
anywhen(date, { locale: 'en', thresholds: { second: 120 } })
// smart mode: "now" covers the first 2 minutesIn smart mode thresholds.secondwidens the "now" window and thresholds.minute the sub-hour minutes window, symmetrically in both directions. Calendar labels (today, yesterday, tomorrow, weekday) are not affected.
SSR
By default, smart and relative modes use the current time. In React SSR or Next.js, pass a stable now value to avoid hydration drift.
import { anywhen } from 'anywhen'
export function PostMeta({ createdAt, requestTime }: {
createdAt: string
requestTime: string
}) {
return (
<time dateTime={createdAt}>
{anywhen(createdAt, {
locale: 'en',
now: requestTime,
timeZone: 'Europe/Belgrade',
})}
</time>
)
}timeZone controls both the displayed clock and the smart calendar boundaries for today, yesterday, and weekday output.
In React, anyfamily-react's useAnywhen also keeps relative output from going stale — it re-renders on an interval so "3 minutes ago" stays true.
Input types
All inputs accept three formats interchangeably.
// Date object
anywhen(new Date())
// Unix timestamp (milliseconds)
anywhen(Date.now())
anywhen(1704499200000)
// ISO string
anywhen('2016-02-05T14:00:00Z')
anywhen('2016-02-05')Locales
Same calls in a few languages — no extra setup, no locale files.
// smart mode
anywhen(date, { locale: 'de' }) // "gestern, 14:35"
anywhen(date, { locale: 'ru' }) // "вчера, 14:35"
anywhen(date, { locale: 'fr' }) // "hier, 14:35"
// absolute mode
anywhen(date, { mode: 'absolute', locale: 'ja' }) // "2016年2月5日"
anywhen(date, { mode: 'absolute', locale: 'ar' }) // "٥ فبراير ٢٠١٦"
anywhen(date, { mode: 'absolute', locale: 'ru' }) // "5 февр. 2016 г."
// relative mode
anywhen(date, { mode: 'relative', locale: 'de' }) // "vor 3 Stunden"
anywhen(date, { mode: 'relative', locale: 'fr' }) // "il y a 3 heures"
anywhen(date, { mode: 'relative', locale: 'tr' }) // "3 saat önce"Pass any valid BCP 47 language tag — including regional variants like en-GB, zh-TW, or pt-BR. Locale is optional; when omitted, native Intl uses the runtime locale. Fallback arrays like ['sr-Latn-RS', 'en'] also work.
Compatibility
anywhen uses Intl.RelativeTimeFormat and Intl.DateTimeFormat — both widely supported.
Limitations
A few things worth knowing before you ship:
Output depends on the runtime's Intl data
anywhen delegates all formatting to native Intl. Exact output — punctuation, spacing, abbreviated month names — may vary between Node versions, browsers, and OSes. Don't hardcode expected strings in tests; use pattern matching instead.
No custom format strings
Absolute mode accepts Intl.DateTimeFormat options, so you control the pieces. But if you need 'DD/MM/YYYY' with literal slashes — use a formatting library with explicit pattern strings instead.
Smart calendar cutoff is fixed at 7 days
Unit cutoffs (seconds → minutes → hours…) are configurable via the thresholds option since 1.0. The calendar switch from weekday ('Wednesday, 11:20') to absolute date still happens at 7 days and is not configurable.
Node.js < 18
The package declares engines.node >= 18 and CI tests Node 20/22/24. Older versions down to 13 will usually work — the required Intl APIs are there — but they are unsupported and untested.