Back Time

Relative Time Calculator

This relative time calculator turns any timestamp into a human-readable phrase like "3 days ago" or "in 2 hours", the same style used in GitHub issues, Slack messages, and most modern UIs. Paste a Unix epoch (seconds, milliseconds, or microseconds), an ISO 8601 string, an RFC 2822 date, or virtually any date format your browser can parse — the tool auto-detects the format, shows the picked unit, breaks the delta down across seconds/minutes/hours/days/weeks/months/years, and displays both local and UTC times. A bulk mode converts whole lists at once, and the live clock keeps sub-minute phrases accurate.

Last updated: May 2026 · Reviewed by FreeDevTool i18n engineering team
just now
Type a timestamp to get started

Bulk mode — one timestamp per line

Copied!

Relative time formatting — the "3 days ago" pattern that ships in every modern UI

Every successful product UI shows times relatively rather than absolutely. GitHub commits, Slack messages, Twitter posts, Stripe transactions, Linear issues — they all tell you "5 minutes ago" or "yesterday" rather than dumping a full ISO timestamp. Relative formatting feels human; absolute formatting feels like log output. This guide covers the threshold buckets that produce natural-sounding phrases, the Intl.RelativeTimeFormat API that ships in every modern browser, the i18n surprises (English uses "yesterday"; Japanese has separate "昨日(kinou)" for spoken vs written), and the live-update pattern that keeps "1 minute ago" from going stale.

The threshold buckets — where the boundaries are

The convention used by Twitter, GitHub, Slack, and the JavaScript Intl spec all converge on roughly these thresholds:

DeltaPhraseNotes
0–10 s"just now"Sub-resolution; treats "right now" as a single bucket.
10–60 s"42 seconds ago"Show seconds for live feeds; some products skip and jump to "less than a minute ago".
1–60 min"5 minutes ago"The most common bucket for UI activity feeds.
1–24 h"3 hours ago"Round down (3.7 h shows as "3 hours ago", not "4").
1–7 days"yesterday" / "3 days ago""Yesterday" is special-cased.
1–4 weeks"2 weeks ago"Some UIs prefer "10 days ago" up to ~14 days, then switch.
1–12 months"6 months ago"Calendar months, not 30-day blocks.
≥ 1 year"3 years ago" or absolute dateGitHub switches to absolute "Mar 2023" past 1 year.

Intl.RelativeTimeFormat — the platform API

The browser ships an i18n-aware relative-time formatter as of 2018 (Chrome 71, Firefox 65, Safari 14). It handles plurals, future vs past, and locale-specific phrasing automatically:

const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
rtf.format(-1, 'day');     // "yesterday"
rtf.format( 0, 'day');     // "today"
rtf.format( 1, 'day');     // "tomorrow"
rtf.format(-3, 'day');     // "3 days ago"
rtf.format( 5, 'minute');  // "in 5 minutes"

// numeric: 'always' forces numbers (for log displays)
new Intl.RelativeTimeFormat('en', { numeric: 'always' })
  .format(-1, 'day');      // "1 day ago"

// Different locales handle phrasing differently
new Intl.RelativeTimeFormat('ja', { numeric: 'auto' })
  .format(-1, 'day');      // "昨日"
new Intl.RelativeTimeFormat('de', { numeric: 'auto' })
  .format(-1, 'day');      // "gestern"

This is the recommended approach in 2026. It eliminates the "I shipped my homegrown relative formatter and it broke for German users" class of bug.

The live-update pattern — keeping "1 min ago" fresh

If the user lands on the page and a comment was posted 30 seconds ago, the UI shows "30 seconds ago". One minute later it should say "1 minute ago", then "2 minutes ago", and so on. The standard implementation:

function startRelativeTimeUpdates() {
  const elements = document.querySelectorAll('time[datetime]');

  function updateAll() {
    const now = Date.now();
    elements.forEach(el => {
      const t = new Date(el.dateTime).getTime();
      el.textContent = formatRelative(t - now);
    });
  }

  updateAll();
  setInterval(updateAll, 30_000);   // refresh every 30s
}

For a feed with hundreds of timestamps, batching updates like this beats a per-element timer. Stop the interval when the tab is hidden (use the Page Visibility API) to save battery.

Why "n minutes ago" vs "in n minutes" matters more than it seems

Future and past tense in relative time should never collide. A naive implementation might show:

Always preserve the sign of the delta. "Created 5 min ago" and "Expires in 5 min" are different states; collapsing them to the same phrase has caused real bugs in dashboard expiry warnings.

Time-zone surprises — the "yesterday" trap

"Yesterday" is a calendar concept, not a duration. A timestamp 18 hours ago might be "yesterday" or "today" depending on the user's local time:

Use the local-date concept (zero-out the time portion of both timestamps and compare days) when you want to special-case "today", "yesterday", "last week".

Locale differences that bite

Locale"3 days ago""in 3 days"
English (en)3 days agoin 3 days
German (de)vor 3 Tagenin 3 Tagen
Spanish (es)hace 3 díasdentro de 3 días
French (fr)il y a 3 joursdans 3 jours
Japanese (ja)3 日前3 日後
Arabic (ar)قبل 3 أيامخلال 3 أيام
Russian (ru)3 дня назадчерез 3 дня

Languages with grammatical gender (Russian, Arabic) or different plural rules (Russian: 1 / 2-4 / 5+ all use different forms) make hand-rolling impossible. Always use Intl.RelativeTimeFormat for any product going beyond English.

Library landscape — when to reach for what

ToolBundle sizeBest for
Intl.RelativeTimeFormat0 (built-in)Everything new in 2026.
date-fns formatDistanceToNow~14 KB minified+gzippedApps already using date-fns; tree-shakable.
day.js + relativeTime plugin~3 KBLightweight Moment.js replacement.
luxon toRelative()~70 KBHeavy-duty calendars and TZ work.
Moment.js fromNow()~232 KBLegacy code only — Moment is in maintenance mode.
Vercel @formatjs/intl-relativetimeformat~6 KB + locale filesPolyfill for older browsers (still useful for India/Africa Android fleets).

Common relative-time mistakes

Frequently Asked Questions

What is a relative time calculator?
A relative time calculator converts an absolute timestamp — Unix epoch, ISO 8601, RFC 2822, or a natural date string — into a human-readable phrase relative to right now: "3 days ago", "in 2 hours", "last week", "just now". It's the same formatting vocabulary you see in GitHub issues, Slack, Twitter, and most modern UIs. Developers use it to sanity-check log timestamps, design notification copy, debug TTL/expiry fields, or translate large lists of backend timestamps into something humans can skim.
What timestamp formats does this tool accept?
Auto-detection handles the common formats: Unix seconds (10-digit integer), Unix milliseconds (13-digit — JavaScript's native epoch), Unix microseconds (16-digit, sometimes emitted by Python and Postgres), ISO 8601 (2024-01-15T12:00:00Z and offset variants), RFC 2822 (Mon, 15 Jan 2024 12:00:00 GMT), and any natural string JavaScript's Date constructor accepts. Negative integers represent pre-1970 epochs. The detected format is shown just above the result.
How are units chosen for the relative phrase?
The tool picks the largest meaningful unit given the magnitude: under 45 seconds renders as "just now"; up to 60 seconds as seconds; up to 60 minutes as minutes; up to 24 hours as hours; up to 6 days as days; up to 4 weeks as weeks; up to 12 months as months; then years. Singular/plural is handled correctly ("1 day ago", not "1 days ago"). Future timestamps are prefixed with "in " instead of suffixed with " ago". These thresholds match the Intl.RelativeTimeFormat style used by major libraries.
Does the tool account for my local timezone?
Yes, in two ways. First, the absolute time display shows the parsed instant rendered in your browser's IANA timezone, alongside the equivalent UTC — so you can quickly see that a Unix timestamp stored in a database maps to, say, 14:30 local and 19:30 UTC. Second, the relative phrase is timezone-independent — it's the elapsed duration between two instants in real time, which is the same number of seconds no matter where you or the server are located.
How does bulk mode work?
Paste a list of timestamps — one per line, any mix of formats — into the left textarea and the right column fills with the equivalent relative phrases. Useful for translating log exports, CSV columns, or database dumps into something you can skim in a PR review. Invalid lines show an invalid marker rather than aborting the whole batch, so you can copy-paste messy data without pre-cleaning it.
Why does the phrase update every second?
"Now" is a moving target — a timestamp that's 42 seconds ago becomes 43 seconds ago one second later. The tool recomputes the relative phrase every second via a setInterval so sub-minute phrases stay accurate in real time. Once the delta exceeds a minute, the visible phrase only changes when the unit threshold is crossed (e.g., from "59 minutes" to "1 hour"), but the internal calculation keeps ticking.
What's the difference between Unix seconds and milliseconds?
Both count the time since the Unix epoch (1970-01-01 00:00:00 UTC) — the difference is the scale. Seconds (10 digits as of now, e.g., 1710000000) are standard in Unix, Go, and most databases. Milliseconds (13 digits, e.g., 1710000000000) are native to JavaScript (Date.now()) and Java. Microseconds (16 digits) occasionally appear in Postgres and Python time.time_ns(). This tool detects which one you pasted based on the digit count and parses accordingly.

Browse all 50 free developer tools

All tools run in your browser, no signup required, nothing sent to a server.