Lukas Klein Haneveld
Lukas Klein Haneveld

Reputation: 31

React Intl with relativeTime formatting

I'm working on a kind of dynamic timestamp for messages using Intl.

I want the timestamps to be dynamic in the way that it automatically transitions from ".. seconds ago" to "... minutes ago" to "... hours ago" to "today", after which it'll just return the date it's been posted. I know there's the <RelativeFormat> component, but I want to use the API instead.

The API has a method called intl.relativeFormat, but can't seem to figure out how to use it...

I'm a junior programmer so it's all still a bit new to me 😅😅

I appreciate your time :)

If you need more info, please let me know. I'll try to provide you with more.

Thanks!

Upvotes: 2

Views: 4918

Answers (2)

armageddon
armageddon

Reputation: 127

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

Upvotes: 1

Rusticus
Rusticus

Reputation: 65

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/

Upvotes: 4

Related Questions