npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

relative-time

v2.2.1

Published

Formats JavaScript dates to relative time strings (e.g., "3 hours ago")

Readme

Relative Time

Formats JavaScript dates to localized relative time strings (e.g., "3 hours ago", "yesterday", "in 2 weeks"). It selects a best‑fit unit for you (seconds → minutes → hours → days → weeks → months → years) and uses the platform’s Intl.RelativeTimeFormat for localization.

  • High‑level: RelativeTimeresolve unit → format
  • Low‑level: RelativeTimeResolverresolve only the { value, unit }

Built on Temporal for precise date math and Intl.RelativeTimeFormat for i18n. That means you get the same CLDR data your runtime ships with—no locale bundles to download.

Why not just Intl.RelativeTimeFormat? Intl.RelativeTimeFormat is intentionally low level: you must choose the unit yourself (e.g., day vs week) and then ask it to format the pair {value, unit}. This library adds the unit resolver layer on top—by design that’s out of scope for the built‑in API. (see more details at tc39/proposal-intl-relative-time#14)

Install

npm install relative-time

Temporal polyfill

This library expects Temporal. Use it where Temporal is available, or load the official polyfill:

npm install @js-temporal/polyfill

When using @js-temporal/polyfill, inject Temporal when constructing RelativeTime/RelativeTimeResolver:

import RelativeTime, { RelativeTimeResolver } from "relative-time";
import { Temporal } from "@js-temporal/polyfill";

const rt = new RelativeTime({ Temporal });
// or:
const resolver = new RelativeTimeResolver({ Temporal });

Quick start

import RelativeTime from "relative-time";
// If needed:
// import { Temporal } from "@js-temporal/polyfill";

const rt = new RelativeTime(); // locale inferred from the runtime

const threeHoursAgo = Temporal.Now.plainDateTimeISO().subtract({ hours: 3 });
rt.format(threeHoursAgo);
// Output: "3 hours ago"

const pt = new RelativeTime("pt");
const oneHourAgo = Temporal.Now.plainDateTimeISO().subtract({ hours: 1 });
pt.format(oneHourAgo);
// Output: "há 1 hora"

Time‑zone aware

const rt = new RelativeTime();

const laDate = Temporal.ZonedDateTime.from(
  "2016-04-09T17:00:00-07:00[America/Los_Angeles]"
);
rt.format(laDate);
// Output: "yesterday"
// Assuming now is "2016-04-10T05:00:00-07:00[America/Los_Angeles]"

const berlinDate = Temporal.ZonedDateTime.from(
  "2016-04-10T02:00:00+02:00[Europe/Berlin]"
);
rt.format(berlinDate, { now: berlinNow });
// Output: "12 hours ago"
// Assuming now is "2016-04-10T14:00:00+02:00[Europe/Berlin]"

Use Temporal.PlainDateTime when you want to ignore time‑zone rules (local calendar math), and Temporal.ZonedDateTime when offset and DST must be respected.

Resolver only

Use the resolver when you need just the unit/value.

import { RelativeTimeResolver } from "relative-time";

const resolver = new RelativeTimeResolver();
const event = Temporal.Now.plainDateTimeISO().subtract({ minutes: 3 });
const { value, unit } = resolver.resolve(event);
// Output: { value: -3, unit: "minute" }

// You can format this yourself or with Intl.RelativeTimeFormat
new Intl.RelativeTimeFormat(undefined, { numeric: "auto" }).format(value, unit);
// Output: "3 minutes ago"

API

class RelativeTime (default export)

High‑level formatter that resolves the unit and formats the string.

Constructor

new RelativeTime(
  locales?: string | string[],
  options?: Intl.RelativeTimeFormatOptions
)
  • locales — same semantics as other Intl constructors (e.g., "en", ["fr", "en"]).
  • options — forwarded to Intl.RelativeTimeFormat (e.g., { style: "short", numeric: "auto" }). Tip: numeric: "auto" yields strings like “yesterday/tomorrow”; numeric: "always" gives “1 day ago / in 1 day”.

format(date, options?) => string

format(
  date: Temporal.PlainDateTime | Temporal.ZonedDateTime,
  options?: {
    now?: Temporal.PlainDateTime | Temporal.ZonedDateTime,
    unit?: "best-fit" | "second" | "minute" | "hour" | "day" | "week" | "month" | "year"
  }
): string
  • date — the target moment.
  • options.now — reference moment (defaults to “now”, matched to date’s type).
  • options.unit — force a specific unit or let the library decide with "best-fit" (default).

How “best‑fit” works (conceptually)

The resolver promotes units using calendar boundaries and configurable step-up thresholds (e.g., sec → min → hr → day → month). If the difference crosses a calendar day, you'll get "yesterday" (instead of "20 hours ago"). But if it crosses midnight without clearing the threshold, it stays in hours, e.g., "3 hours ago" (instead of "yesterday").


class RelativeTimeResolver (named export)

Low‑level unit chooser. Use it if you want the { value, unit } pair to feed into your own formatter (including Intl.RelativeTimeFormat directly).

Constructor

new RelativeTimeResolver();

resolve(date, options?) => { value: number, unit: RTFUnit }

type RTFUnit = "second" | "minute" | "hour" | "day" | "week" | "month" | "year";

resolve(
  date: Temporal.PlainDateTime | Temporal.ZonedDateTime,
  options?: {
    now?: Temporal.PlainDateTime | Temporal.ZonedDateTime,
    unit?: "best-fit" | RTFUnit  // "best-fit" (default) or force a unit
  }
): { value: number; unit: RTFUnit }

Example

import { RelativeTimeResolver } from "relative-time";

const r = new RelativeTimeResolver();
const target = Temporal.Now.plainDateTimeISO().add({ days: 6, hours: 5 });

const { value, unit } = r.resolve(target); // e.g., { value: 1, unit: "week" }

new Intl.RelativeTimeFormat("en", { numeric: "auto" }).format(value, unit);
// "next week"

Using Intl.RelativeTimeFormat options

You can customize style and numeric behavior at construction:

const rtShort = new RelativeTime("en", { style: "short", numeric: "always" });
rtShort.format(Temporal.Now.plainDateTimeISO().subtract({ day: 1 }));
// "1 day ago"

RelativeTime is literally a thin wrapper over Intl.RelativeTimeFormat once a { value, unit } has been resolved. You get the same pluralization and grammar your engine provides.

When to use which

| You need… | Use | Why | | --------------------------------- | ---------------------------------- | ------------------------------------------------------------- | | “Just give me a localized string” | RelativeTime | Picks a sensible unit and formats it | | { value, unit } only | RelativeTimeResolver | Feed another formatter, build custom UIs, analytics, etc. | | Full control over unit and i18n | Intl.RelativeTimeFormat directly | Low‑level by design; no unit resolution provided by the spec. |

Examples

Force a unit

const rt = new RelativeTime("en");
rt.format(Temporal.Now.plainDateTimeISO().subtract({ hours: 27 }), {
  unit: "hour",
});
// "27 hours ago"

Different locales

new RelativeTime(["fr", "en"]).format(
  Temporal.Now.plainDateTimeISO().add({ minutes: 5 })
);
// "dans 5 minutes"

Notes on accuracy

This project uses Temporal for differences (calendar‑aware) and Intl.RelativeTimeFormat for localization. That combination prevents common errors around DST, month lengths, and week boundaries that “approximate threshold” libraries can exhibit.

Browser / runtime support

  • Works wherever Temporal is available; otherwise include @js-temporal/polyfill.
  • Uses your runtime’s Intl.RelativeTimeFormat, which has wide support in modern environments.

FAQ

Does this support quarters? No—units are: second, minute, hour, day, week, month, year.

Why do I sometimes see “yesterday” instead of “1 day ago”? That comes from numeric: "auto" in Intl.RelativeTimeFormat. Switch to numeric: "always" if you prefer numbers only.

How is this different from the TC39 proposal? This library’s resolver is the high‑level part omitted on purpose from the Intl.RelativeTimeFormat spec; the built‑in API only formats a supplied { value, unit }. See the historical discussion around “best‑fit” in the proposal repo.

Contributing

PRs and issues welcome!

Appendix

Relative time

In this library, we'll define relative time as what makes sense for expressions like "now", "2 days ago", "in 3 months", "last year", "yesterday", and so on. In a more formal definition, relative time is an approximate date distance given a unit. This is, relative time is the date distance of a and b ± error, where error < unit. Please, see the below examples of each unit for clarity.

second

                8:31:38.000         8:31:39.000         8:31:40.000         8:31:41.000
ms  | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
sec             |   e             d |   c             b |       a     N     |
                8:31:38             8:31:39             8:31:40             8:31:41

N: The assumed now
a: now / 0 seconds ago
b: 1 second ago
c: 1 second ago
d: 2 seconds ago
e: 2 seconds ago

minute

          8:28:00           8:29:00           8:30:00           8:31:00           8:32:00
sec |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
min       |     g        f  |     e        d  |     c        b  |        a  N     |
          8:28              8:29              8:30              8:31              8:32

N: The assumed now
a: 0 minutes ago
b: 1 minute ago
c: 1 minute ago
d: 2 minutes ago
e: 2 minutes ago
f: 3 minutes ago
g: 3 minutes ago

hour

          5:00              6:00              7:00              8:00              9:00
min |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
hr.       |     g        f  |     e        d  |     c        b  |     a  N        |
          5                 6                 7                 8                 9

N: The assumed now
a: 0 hours ago
b: 1 hour ago
c: 1 hour ago
d: 2 hours ago
e: 2 hours ago
f: 3 hours ago
g: 3 hours ago

day

       Mar 21, 00:00           Mar 22, 00:00           Mar 23, 00:00           Mar 24, 00:00
hr.  | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
day    | e                   d | c                   b |     a N               |
       Mar 21                  Mar 22                  Mar 23                  Mar 24

N: The assumed now
a: today / 0 days ago
b: yesterday / 1 day ago
c: yesterday / 1 day ago
d: 2 days ago
e: 2 days ago

week

                     Wk. 11, Sun, Mar 12  Wk. 12, Sun, Mar 19  Wk. 13, Sun, Mar 26
day   |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
wk.   e           d  |     c             b|       a    N       |
                     Wk 11                Wk 12                Wk 13

N: The assumed now
a: this week
b: last week
c: last week
d: 2 weeks ago
e: 2 weeks ago

month

      Wk. 1       Wk. 5       Wk. 9          Wk. 14
wk.   |  |  |  |  |  |  |  |  |  |  |  |  |  |
mo.   |e         d | c        b|     a   N   |
      Jan          Feb         Mar           Apr

N: The assumed now
a: this month / 0 months ago
b: last month / 1 month ago
c: last month / 1 month ago
d: 2 months ago
e: 2 months ago

Note the months distances doesn't match weeks distance or days distance uniformly.

year

          Jan               Jan               Jan               Jan               Jan
mo. |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
yr.       |g             f  |e             d  |c             b  |a  N             |
          2013              2014              2015              2016              2017

N: The assumed now
a: this year / 0 years ago
b: last year / 1 year ago
c: last year / 1 year ago
d: 2 years ago
e: 2 years ago
f: 3 years ago
g: 3 years ago

Note that (although not shown by the above ruler), the years distances doesn't match weeks distance or days distance uniformly.

License

MIT © Rafael Xavier de Souza