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 anywhen

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"

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.

< 45s→ "now"
< 1 hour→ "10 minutes ago"
future > 1h→ "in 2 weeks"
same day→ "today, 14:35"
yesterday→ "yesterday, 09:00"
< 7 days→ "Wednesday, 11:20"
older→ "Feb 5, 2016"

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 locale

Any valid BCP 47 locale tag, or a fallback array — 'en', 'en-US', 'zh-TW', ['sr-Latn-RS', 'en'].

nowDate | number | stringdefault: current time

Reference time for smart and relative modes. Pass this in SSR to keep server and client output stable.

timeZonestringdefault: runtime timezone

IANA time zone for the displayed clock and smart day boundaries (today, yesterday, weekday). Used by smart and absolute modes.

timebooleandefault: true

Smart mode only. Whether to include clock time in today/yesterday/weekday output.

numericbooleandefault: false

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

Node.js13+full ICU included by default
Chrome71+
Firefox65+
Safari14+
Edge79+
Vercel Edge Runtime
Cloudflare Workers
Deno

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.