You would have to take the current time and diff the tiem you are formatting to get the milliseconds.

Now, you need to figure out the closest unit to round down to and format using that unit.

Just swap Intl.RelativeTimeFormat for intl.formatRelativeTime where applicable, but the algorithm should remain the same.

if (Date.prototype.getUTCTime === undefined) {
  Date.prototype.getUTCTime = function() {
    return this.getTime() - (this.getTimezoneOffset() * 60000);
  };
}

const
  WEEK_IN_MILLIS = 6.048e8,
  DAY_IN_MILLIS = 8.64e7,
  HOUR_IN_MILLIS = 3.6e6,
  MIN_IN_MILLIS = 6e4,
  SEC_IN_MILLIS = 1e3;

// For testing only, remove the constructor argument in production.
const getCurrentUTCTime = () => new Date('2021-04-26T14:21:51.771Z').getUTCTime();

const timeFromNow = (date, formatter) => {
  const
    millis = typeof date === 'string' ? new Date(date).getUTCTime() : date.getUTCTime(),
    diff = millis - getCurrentUTCTime(); 
  if (Math.abs(diff) > WEEK_IN_MILLIS)
    return formatter.format(Math.trunc(diff / WEEK_IN_MILLIS), 'week');
  else if (Math.abs(diff) > DAY_IN_MILLIS)
    return formatter.format(Math.trunc(diff / DAY_IN_MILLIS), 'day');
  else if (Math.abs(diff) > HOUR_IN_MILLIS)
    return formatter.format(Math.trunc((diff % DAY_IN_MILLIS) / HOUR_IN_MILLIS), 'hour');
  else if (Math.abs(diff) > MIN_IN_MILLIS)
    return formatter.format(Math.trunc((diff % HOUR_IN_MILLIS) / MIN_IN_MILLIS), 'minute');
  else
    return formatter.format(Math.trunc((diff % MIN_IN_MILLIS) / SEC_IN_MILLIS), 'second');
};

const dateFormat = new Intl.RelativeTimeFormat('en', { style: 'long' });

console.log(timeFromNow('2021-04-24T14:21:51.771Z', dateFormat));
console.log(timeFromNow('2021-04-25T14:21:51.771Z', dateFormat));
console.log(timeFromNow('2021-04-26T14:21:51.771Z', dateFormat));
console.log(timeFromNow('2021-04-27T14:21:51.771Z', dateFormat));
console.log(timeFromNow('2021-04-28T14:21:51.771Z', dateFormat));
console.log(timeFromNow('2021-04-29T14:21:51.771Z', dateFormat));
console.log(timeFromNow('2021-04-30T14:21:51.771Z', dateFormat));
console.log(timeFromNow('2021-05-01T14:21:51.771Z', dateFormat));
console.log(timeFromNow('2021-05-02T14:21:51.771Z', dateFormat));
console.log(timeFromNow('2021-05-03T14:21:51.771Z', dateFormat));
console.log(timeFromNow('2021-05-04T14:21:51.771Z', dateFormat));
.as-console-wrapper { top: 0; max-height: 100% !important; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-messageformat/9.0.2/intl-messageformat.min.js"></script>

Here is a version of the code above written in React:

View on CodeSandbox

import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { IntlProvider, useIntl } from "react-intl";

const WEEK_IN_MILLIS = 6.048e8,
  DAY_IN_MILLIS = 8.64e7,
  HOUR_IN_MILLIS = 3.6e6,
  MIN_IN_MILLIS = 6e4,
  SEC_IN_MILLIS = 1e3;

const getUTCTime = (date) => date.getTime() - date.getTimezoneOffset() * 60000;

// For testing only, remove the constructor argument in production.
const getCurrentUTCTime = () => getUTCTime(new Date());

const defaultFormatOptions = {
  style: "long"
};

const timeFromNow = (date, intl, options = defaultFormatOptions) => {
  const millis =
      typeof date === "string" ? getUTCTime(new Date(date)) : getUTCTime(date),
    diff = millis - getCurrentUTCTime();
  if (Math.abs(diff) > WEEK_IN_MILLIS)
    return intl.formatRelativeTime(
      Math.trunc(diff / WEEK_IN_MILLIS),
      "week",
      options
    );
  else if (Math.abs(diff) > DAY_IN_MILLIS)
    return intl.formatRelativeTime(
      Math.trunc(diff / DAY_IN_MILLIS),
      "day",
      options
    );
  else if (Math.abs(diff) > HOUR_IN_MILLIS)
    return intl.formatRelativeTime(
      Math.trunc((diff % DAY_IN_MILLIS) / HOUR_IN_MILLIS),
      "hour",
      options
    );
  else if (Math.abs(diff) > MIN_IN_MILLIS)
    return intl.formatRelativeTime(
      Math.trunc((diff % HOUR_IN_MILLIS) / MIN_IN_MILLIS),
      "minute",
      options
    );
  else
    return intl.formatRelativeTime(
      Math.trunc((diff % MIN_IN_MILLIS) / SEC_IN_MILLIS),
      "second",
      options
    );
};

const CreatedConsetee = ({ date }) => {
  return <>{timeFromNow(date, useIntl())}</>;
};

ReactDOM.render(
  <StrictMode>
    <IntlProvider locale={navigator.language}>
      <div className="App">
        <h1>
          <CreatedConsetee date={new Date("2021-04-26T14:21:51.771Z")} />
        </h1>
      </div>
    </IntlProvider>
  </StrictMode>,
  document.getElementById("root")
);
Answer from Mr. Polywhirl on Stack Overflow
🌐
DEV Community
dev.to › magnificode › secret-javascript-methods-they-dont-want-you-to-see-part-4-intlrelativetimeformat-1e8k
👨‍🚀 Traversing Time with Intl.RelativeTimeFormat() - DEV Community
December 18, 2023 - In this example, Intl.RelativeTimeFormat() simplifies the representation of temporal information, offering a clearer and more engaging experience for users interacting with dates.
🌐
MDN Web Docs
developer.mozilla.org › en-US › docs › Web › JavaScript › Reference › Global_Objects › Intl › RelativeTimeFormat › format
Intl.RelativeTimeFormat.prototype.format() - JavaScript | MDN
July 20, 2025 - // Create a relative time formatter in your locale // with numeric: "auto" option value passed in. const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" }); // Format relative time using negative value (-1). rtf.format(-1, "day"); // "yesterday" rtf.format(0, "day"); // "today" // Format relative time using positive day unit (1).
🌐
V8
v8.dev › features › intl-relativetimeformat
Intl.RelativeTimeFormat · V8
October 22, 2018 - Intl.RelativeTimeFormat enables localized formatting of relative times without sacrificing performance.
Top answer
1 of 2
4

You would have to take the current time and diff the tiem you are formatting to get the milliseconds.

Now, you need to figure out the closest unit to round down to and format using that unit.

Just swap Intl.RelativeTimeFormat for intl.formatRelativeTime where applicable, but the algorithm should remain the same.

if (Date.prototype.getUTCTime === undefined) {
  Date.prototype.getUTCTime = function() {
    return this.getTime() - (this.getTimezoneOffset() * 60000);
  };
}

const
  WEEK_IN_MILLIS = 6.048e8,
  DAY_IN_MILLIS = 8.64e7,
  HOUR_IN_MILLIS = 3.6e6,
  MIN_IN_MILLIS = 6e4,
  SEC_IN_MILLIS = 1e3;

// For testing only, remove the constructor argument in production.
const getCurrentUTCTime = () => new Date('2021-04-26T14:21:51.771Z').getUTCTime();

const timeFromNow = (date, formatter) => {
  const
    millis = typeof date === 'string' ? new Date(date).getUTCTime() : date.getUTCTime(),
    diff = millis - getCurrentUTCTime(); 
  if (Math.abs(diff) > WEEK_IN_MILLIS)
    return formatter.format(Math.trunc(diff / WEEK_IN_MILLIS), 'week');
  else if (Math.abs(diff) > DAY_IN_MILLIS)
    return formatter.format(Math.trunc(diff / DAY_IN_MILLIS), 'day');
  else if (Math.abs(diff) > HOUR_IN_MILLIS)
    return formatter.format(Math.trunc((diff % DAY_IN_MILLIS) / HOUR_IN_MILLIS), 'hour');
  else if (Math.abs(diff) > MIN_IN_MILLIS)
    return formatter.format(Math.trunc((diff % HOUR_IN_MILLIS) / MIN_IN_MILLIS), 'minute');
  else
    return formatter.format(Math.trunc((diff % MIN_IN_MILLIS) / SEC_IN_MILLIS), 'second');
};

const dateFormat = new Intl.RelativeTimeFormat('en', { style: 'long' });

console.log(timeFromNow('2021-04-24T14:21:51.771Z', dateFormat));
console.log(timeFromNow('2021-04-25T14:21:51.771Z', dateFormat));
console.log(timeFromNow('2021-04-26T14:21:51.771Z', dateFormat));
console.log(timeFromNow('2021-04-27T14:21:51.771Z', dateFormat));
console.log(timeFromNow('2021-04-28T14:21:51.771Z', dateFormat));
console.log(timeFromNow('2021-04-29T14:21:51.771Z', dateFormat));
console.log(timeFromNow('2021-04-30T14:21:51.771Z', dateFormat));
console.log(timeFromNow('2021-05-01T14:21:51.771Z', dateFormat));
console.log(timeFromNow('2021-05-02T14:21:51.771Z', dateFormat));
console.log(timeFromNow('2021-05-03T14:21:51.771Z', dateFormat));
console.log(timeFromNow('2021-05-04T14:21:51.771Z', dateFormat));
.as-console-wrapper { top: 0; max-height: 100% !important; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-messageformat/9.0.2/intl-messageformat.min.js"></script>

Here is a version of the code above written in React:

View on CodeSandbox

import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { IntlProvider, useIntl } from "react-intl";

const WEEK_IN_MILLIS = 6.048e8,
  DAY_IN_MILLIS = 8.64e7,
  HOUR_IN_MILLIS = 3.6e6,
  MIN_IN_MILLIS = 6e4,
  SEC_IN_MILLIS = 1e3;

const getUTCTime = (date) => date.getTime() - date.getTimezoneOffset() * 60000;

// For testing only, remove the constructor argument in production.
const getCurrentUTCTime = () => getUTCTime(new Date());

const defaultFormatOptions = {
  style: "long"
};

const timeFromNow = (date, intl, options = defaultFormatOptions) => {
  const millis =
      typeof date === "string" ? getUTCTime(new Date(date)) : getUTCTime(date),
    diff = millis - getCurrentUTCTime();
  if (Math.abs(diff) > WEEK_IN_MILLIS)
    return intl.formatRelativeTime(
      Math.trunc(diff / WEEK_IN_MILLIS),
      "week",
      options
    );
  else if (Math.abs(diff) > DAY_IN_MILLIS)
    return intl.formatRelativeTime(
      Math.trunc(diff / DAY_IN_MILLIS),
      "day",
      options
    );
  else if (Math.abs(diff) > HOUR_IN_MILLIS)
    return intl.formatRelativeTime(
      Math.trunc((diff % DAY_IN_MILLIS) / HOUR_IN_MILLIS),
      "hour",
      options
    );
  else if (Math.abs(diff) > MIN_IN_MILLIS)
    return intl.formatRelativeTime(
      Math.trunc((diff % HOUR_IN_MILLIS) / MIN_IN_MILLIS),
      "minute",
      options
    );
  else
    return intl.formatRelativeTime(
      Math.trunc((diff % MIN_IN_MILLIS) / SEC_IN_MILLIS),
      "second",
      options
    );
};

const CreatedConsetee = ({ date }) => {
  return <>{timeFromNow(date, useIntl())}</>;
};

ReactDOM.render(
  <StrictMode>
    <IntlProvider locale={navigator.language}>
      <div className="App">
        <h1>
          <CreatedConsetee date={new Date("2021-04-26T14:21:51.771Z")} />
        </h1>
      </div>
    </IntlProvider>
  </StrictMode>,
  document.getElementById("root")
);
2 of 2
1

@mr-polywhirl I really liked your answer, so I adapted it to something a little shorter that better fit my needs:

/**
 * Adapted from https://stackoverflow.com/a/67374710/
 */
const millisecondsPerSecond = 1000;
const secondsPerMinute = 60;
const minutesPerHour = 60;
const hoursPerDay = 24;
const daysPerWeek = 7;
const intervals = {
    'week':         millisecondsPerSecond * secondsPerMinute * minutesPerHour * hoursPerDay * daysPerWeek,
    'day':          millisecondsPerSecond * secondsPerMinute * minutesPerHour * hoursPerDay,
    'hour':         millisecondsPerSecond * secondsPerMinute * minutesPerHour,
    'minute':       millisecondsPerSecond * secondsPerMinute,
    'second':       millisecondsPerSecond,
}
const relativeDateFormat = new Intl.RelativeTimeFormat('en', { style: 'long' });

function formatRelativeTime(/** @type Date */ createTime) {
    const diff = createTime - new Date();
    for (const interval in intervals) {
        if (intervals[interval] <= Math.abs(diff)) {
            return relativeDateFormat.format(Math.trunc(diff / intervals[interval]), interval);
        }
    }
    return relativeDateFormat.format(diff / 1000, 'second');
}
🌐
GitHub
github.com › tc39 › proposal-intl-relative-time
GitHub - tc39/proposal-intl-relative-time: `Intl.RelativeTimeFormat` specification [draft]
Intl.RelativeTimeFormat is a low level API to facilitate libraries and frameworks to format relative time in a localized fashion by providing internationalized messages for date and time fields, using customary word or phrase when available.
Starred by 216 users
Forked by 21 users
Languages   HTML 61.0% | JavaScript 23.4% | CSS 14.9% | Shell 0.7% | HTML 61.0% | JavaScript 23.4% | CSS 14.9% | Shell 0.7%
🌐
TC39
tc39.es › proposal-intl-relative-time
Intl.RelativeTimeFormat Spec Proposal
The RelativeTimeFormat constructor is the %RelativeTimeFormat% intrinsic object and a standard built-in property of the Intl object.
Find elsewhere
🌐
MDN Web Docs
developer.mozilla.org › en-US › docs › Web › JavaScript › Reference › Global_Objects › Intl › RelativeTimeFormat › RelativeTimeFormat
Intl.RelativeTimeFormat() constructor - JavaScript | MDN
// Create a relative time formatter in your locale // with numeric: "auto" option value passed in. const rtf = new Intl.RelativeTimeFormat("en-US", { numeric: "auto" }); // Format relative time using negative value (-1). rtf.format(-1, "day"); // "yesterday" // Format relative time using positive ...
🌐
Format.JS
formatjs.github.io › docs › polyfills › intl-relativetimeformat
Intl Relativetimeformat | FormatJS
import '@formatjs/intl-relativetimeformat/polyfill.js' // Basic usage with numeric: 'always' (default) const rtf = new Intl.RelativeTimeFormat('en', {style: 'long'}) rtf.format(-1, 'day') // "1 day ago" rtf.format(2, 'week') // "in 2 weeks" rtf.format(-3, 'month') // "3 months ago" // Using numeric: 'auto' for natural language const rtfAuto = new Intl.RelativeTimeFormat('en', { numeric: 'auto', style: 'long', }) rtfAuto.format(-1, 'day') // "yesterday" rtfAuto.format(0, 'day') // "today" rtfAuto.format(1, 'day') // "tomorrow" rtfAuto.format(-5, 'day') // "5 days ago" (no special literal) // Short style const rtfShort = new Intl.RelativeTimeFormat('en', {style: 'short'}) rtfShort.format(3, 'hour') // "in 3 hr."
🌐
Web Dev Simplified
blog.webdevsimplified.com › 2020-07 › relative-time-format
Relative Date Internationalization In JavaScript
Just like with all internationalization ... specifically called Intl.RelativeTimeFormat. This is a class which when instantiated can be used to format any relative time period....
🌐
npm
npmjs.com › package › @formatjs › intl-relativetimeformat
@formatjs/intl-relativetimeformat - npm
March 23, 2025 - Formats JavaScript dates to relative time strings.. Latest version: 11.4.13, last published: 2 months ago. Start using @formatjs/intl-relativetimeformat in your project by running `npm i @formatjs/intl-relativetimeformat`. There are 160 other ...
      » npm install @formatjs/intl-relativetimeformat
    
Published   Oct 09, 2025
Version   11.4.13
Author   Long Ho
🌐
Lingo
lingo.dev › en › javascript-i18n › intl-relative-time-format-api
Intl.RelativeTimeFormat API - JavaScript i18n
The Intl.RelativeTimeFormat API provides a native solution. It formats relative time strings with full internationalization support, handling pluralization rules and cultural conventions automatically.
🌐
GitHub
github.com › wessberg › intl-relative-time-format
GitHub - wessberg/intl-relative-time-format: A fully spec-compliant polyfill for 'Intl.RelativeTimeFormat'
Intl.RelativeTimeFormat is a really useful low-level primitive to build on top of which avoids the need to parse lots of CLDR raw data at the expense of your users and their internet connections.
Author   wessberg
Top answer
1 of 2
4

Documentation for the RelativeFormat function can be found here - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat.

The idea is that you create an instance of relative time format function, with some pre-defined settings that you want the output to follow. For example, you can set your relative time format function to return English strings in a shortened format.

 const rtf = new Intl.RelativeTimeFormat('en', { style: 'narrow' });

 console.log(rtf.format(3, 'quarters'));
 //expected output: "in 3 qtrs."

You also need to pass negative values in order to get labels intended for the past.

const rtf = new Intl.RelativeTimeFormat('en', { style: 'narrow' });

console.log(rtf.format(-3, 'quarters'));
//expected output: "3 qtrs. ago"

The next part leverages an answer given by @fearofawhackplanet here on StackOverflow

//The 'timestamp' function parameter is your timestamp passed in milliseconds.
function timeDifference(timestamp, locale) {

    const msPerMinute = 60 * 1000;
    const msPerHour = msPerMinute * 60;
    const msPerDay = msPerHour * 24;
    const msPerMonth = msPerDay * 30;
    const msPerYear = msPerDay * 365;

    const current = Date.now();
    const elapsed = current - timestamp;

    const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });

    if (elapsed < msPerMinute) {
         return rtf.format(-Math.floor(elapsed/1000), 'seconds');   
    }

    else if (elapsed < msPerHour) {
         return rtf.format(-Math.floor(elapsed/msPerMinute), 'minutes'); 
    }

    else if (elapsed < msPerDay) {
         return rtf.format(-Math.floor(elapsed/msPerHour), 'hours');  
    }

    else {
        return new Date(timestamp).toLocaleDateString(locale);   
    }
}

//
// code to test the above function
//
const fifteenSecondsAgo = new Date();
const tenMinutesAgo = new Date();
const twoHoursAgo = new Date();

fifteenSecondsAgo.setSeconds(fifteenSecondsAgo.getSeconds() - 15);
tenMinutesAgo.setMinutes(tenMinutesAgo.getMinutes() - 10);
twoHoursAgo.setHours(twoHoursAgo.getHours() - 2);

console.log(timeDifference(fifteenSecondsAgo.getTime(), 'en'));
console.log(timeDifference(fifteenSecondsAgo.getTime(), 'es'));

console.log(timeDifference(tenMinutesAgo.getTime(), 'en'));
console.log(timeDifference(tenMinutesAgo.getTime(), 'es'));

console.log(timeDifference(twoHoursAgo.getTime(), 'en'));
console.log(timeDifference(twoHoursAgo.getTime(), 'es'));

Here is a JSFiddle link to see the code running - https://jsfiddle.net/mhzya237/1/

2 of 2
1

Here is a similar idea, it also deals with future/present/past times.

function getRelativeTime(time) {
  const now = new Date();
  const diff = Math.abs(time - now);
  const mark = (time - now) >> -1 || 1;

  if (diff === 0) return new Intl.RelativeTimeFormat('en').format(0,"second");

  const times = [
    { type: 'second', seconds: 1000 },
    { type: 'minute', seconds: 60 * 1000 },
    { type: 'hour', seconds: 60 * 60 * 1000 },
    { type: 'day', seconds: 24 * 60 * 60 * 1000 },
    { type: 'week', seconds: 7 * 24 * 60 * 60 * 1000 },
    { type: 'month', seconds: 30 * 24 * 60 * 60 * 1000 },
    { type: 'year', seconds: 12 * 30 * 24 * 60 * 60 * 1000 },
  ];

  let params = [];
  for (let t of times) {
    const segment = Math.round(diff / t.seconds);
    if (segment >= 0 && segment < 10) {
      params = [(segment * mark) | 0, t.type];
      break;
    }
  }
  return new Intl.RelativeTimeFormat('en').format(...params);
}

const time = getRelativeTime(new Date(new Date().getTime() - 2 * 1000));
console.info('relative time is', time);

The function takes a time param, finds the seconds difference relative to now, uses the array map to calculate which type yields the closest match and uses it as a param for Intl.RelativeTimeFormat

You can improve getRelativeTime(time) function by either returning the params array and call Intl.RelativeTimeFormat from outside the function or also pass the locale (and options) to the function.

I'm sure there are smarter ways to get rid of the times array, perhaps by creating a wrapping closure but it will force you to "initialize" this utility function first

🌐
GitHub
github.com › formatjs › intl-relativeformat
GitHub - formatjs/intl-relativeformat: Formats JavaScript dates to relative time strings (e.g., "3 hours ago").
new IntlRelativeFormat('en', { units: 'second-short' }).format( Date.now() - 1000 ); // will be new Intl.RelativeTimeFormat('en', { style: 'short' }).format(-1, 'second'); new IntlRelativeFormat('en', { units: 'day-narrow' }).format( Date.now() - 48 * 3600 * 1000 ); // will be new Intl.RelativeTimeFormat('en', { style: 'narrow' }).format(-2, 'day');
Starred by 214 users
Forked by 29 users
Languages   TypeScript 80.9% | HTML 9.2% | JavaScript 8.0% | Shell 1.9% | TypeScript 80.9% | HTML 9.2% | JavaScript 8.0% | Shell 1.9%
🌐
MDN Web Docs
developer.mozilla.org › en-US › docs › Web › JavaScript › Reference › Global_Objects › Intl › RelativeTimeFormat › resolvedOptions
Intl.RelativeTimeFormat.prototype.resolvedOptions() - JavaScript | MDN
September 24, 2025 - const de = new Intl.RelativeTimeFormat("de-DE"); const usedOptions = de.resolvedOptions(); usedOptions.locale; // "de-DE" usedOptions.style; // "long" usedOptions.numeric; // "always" usedOptions.numberingSystem; // "latn"
🌐
MDN Web Docs
developer.mozilla.org › en-US › docs › Web › JavaScript › Reference › Global_Objects › Intl › RelativeTimeFormat › formatToParts
Intl.RelativeTimeFormat.prototype.formatToParts() - JavaScript | MDN
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" }); // Format relative time using the day unit rtf.formatToParts(-1, "day"); // [{ type: "literal", value: "yesterday"}] rtf.formatToParts(100, "day"); // [ // { type: "literal", value: "in " }, // { type: "integer", value: "100", unit: "day" }, // { type: "literal", value: " days" } // ]
🌐
DEV Community
dev.to › omriluz1 › intlrelativetimeformat-for-custom-time-representations-hgg
Intl.RelativeTimeFormat for Custom Time Representations - DEV Community
April 27, 2025 - One of the obstacles in achieving ... introduced Intl.RelativeTimeFormat, which simplifies the process of formatting relative time expressions like "3 days ago" or "in 2 months"....
🌐
npm
npmjs.com › package › intl-relative-time-format
intl-relative-time-format - npm
April 12, 2023 - Intl.RelativeTimeFormat is a really useful low-level primitive to build on top of which avoids the need to parse lots of CLDR raw data at the expense of your users and their internet connections.
      » npm install intl-relative-time-format