Overview
anywhen is a tiny date formatter built entirely on the native Intl browser API. One function, one options object, three modes.
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 anywhenanywhen()
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"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
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"reads: locale, now, numeric
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'.
formatIntl.DateTimeFormatOptionsdefault: { day, month, year }Absolute mode only. Any options accepted by Intl.DateTimeFormat. Defaults to a short date.
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.
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 mode cutoff is fixed at 7 days
The switch from weekday ('Wednesday, 11:20') to absolute date happens at 7 days and is not configurable. Need a custom cutoff? Use mode: 'relative' or mode: 'absolute' directly.
Node.js < 13
Older Node versions shipped with small-icu — only the 'en' locale was guaranteed. Node 13+ includes full ICU. On older versions, install the full-icu package separately.