How to use intl.formatRelativeTime() in the correct way

2k views Asked by At

In my React app, I have to show info saying create 1 hour ago or 1 day ago and also plural as 10 minutes ago or 3 days ago. To achieve that I'm trying to use this API FormatJS and in the specific intl.formatRelativeTime()

What I tried so far is something like that

const CreatedConsetee = ({ date }) => {
  // date = 2021-04-26T14:21:51.771Z
  const intl = useIntl();
  const parsedDate = new Date(date);

  const dateFormat = intl.formatRelativeTime(parsedDate, 'hour', {
    style: 'long',
  });

  return <>{dateFormat}</>;
};

The result is of the above is like this

in 1,619,446,911,771 hours

Whatever is it always that big number and I have no idea how to make it right.

The expected behavior I want is that I got a message saying created 7 days ago the same if we have 1 hour 1 minute 1 day and plural forms 2 hours 2 minutes 2 days.

1

There are 1 answers

5
Mr. Polywhirl On BEST ANSWER

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")
);