DEV Community

Luvuyo Ndamase
Luvuyo Ndamase

Posted on

"In 3 days" instead of a date: Intl.RelativeTimeFormat for a fixtures list

After sorting out time zones on my fixtures page, the next complaint came from my son, who said nobody reads dates anyway. He wanted "tomorrow" and "in 3 days", the way his phone shows messages. I had a look at how the Chippa fixtures and match schedule site shows them, and it uses proper dates, so I kept those and added a short relative label next to each one.

The browser already has this built in:

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

rtf.format(1, "day");   // "tomorrow"
rtf.format(3, "day");   // "in 3 days"
rtf.format(-1, "day");  // "yesterday"
Enter fullscreen mode Exit fullscreen mode

numeric: "auto" is the part that gives you "tomorrow" instead of "in 1 day". Without it everything comes out as a number, which reads a bit like a parcel tracking page.

Counting days in the right time zone

The tricky bit is not the formatting, it's working out how many days away a match is. Subtracting two timestamps and dividing by 86 400 000 gives you hours turned into fractions, and a Saturday 15:30 kick-off viewed on Thursday evening comes out as 1.8 days, which rounds to "tomorrow". Wrong.

What I do now is turn both dates into calendar days in the ground's time zone first, and only then subtract:

const dayKey = (date, timeZone) =>
  new Intl.DateTimeFormat("en-CA", {
    timeZone, year: "numeric", month: "2-digit", day: "2-digit"
  }).format(date); // "2026-10-18"

function daysUntil(kickoff, timeZone = "Africa/Johannesburg") {
  const a = new Date(dayKey(new Date(), timeZone));
  const b = new Date(dayKey(kickoff, timeZone));
  return Math.round((b - a) / 86_400_000);
}

const label = rtf.format(daysUntil(new Date("2026-10-18T13:30:00Z")), "day");
Enter fullscreen mode Exit fullscreen mode

The "en-CA" locale is just a trick to get a YYYY-MM-DD string that new Date() parses as midnight UTC on both sides, so the difference is always a whole number of days.

When to stop being relative

Past about a week, "in 12 days" is less useful than the date itself, so the label only shows up for matches within 7 days. Anything further keeps the plain date from the first version. For results I show "yesterday" and "3 days ago" the same way, then fall back to the date.

It's about 20 lines in total. My son still says it should show the opponent's logo instead, but that's a different post.

Top comments (0)