如何正确使用 intl.formatRelativeTime()

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

在我的 React 应用程序中,我必须显示 1 小时前或 1 天前创建的信息,以及 10 分钟前或 3 天前的复数形式。为此,我尝试使用此 API FormatJS 并在特定的 intl.formatRelativeTime()

到目前为止我尝试的是类似的东西

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}</>;
};

上面的结果是这样的

in 1,619,446,911,771 hours

不管是什么,总是那么大,我不知道如何正确。

我想要的预期行为是我收到一条消息说 created 7 days ago 如果我们有 1 小时 1 分钟 1 天和复数形式 2 小时 2 分钟 2 天,则相同。

您必须获取当前时间并区分您正在格式化的时间以获得毫秒数。

现在,您需要找出最接近的单位以向下舍入并使用该单位进行格式化。

只需在适用的地方将 Intl.RelativeTimeFormat 换成 intl.formatRelativeTime,但算法应该保持不变。

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>

下面是用 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")
);