How to format time spans like 2 hours 30 minutes

Display durations in the user's language with automatic localization

Introduction

When you show how long something takes, you need to display that duration in a way users understand. A video shows runtime of 2 hours 30 minutes, a workout app tracks exercise duration, a project management tool displays task completion time. Without localization, you might write code like this:

const hours = 2;
const minutes = 30;
const timeSpan = `${hours}h ${minutes}m`;

This produces "2h 30m" for all users regardless of language. French users see "2h 30m" when they expect "2 h 30 min". German users see English abbreviations instead of "2 Std. 30 Min". Spanish users get no "y" conjunction between units.

JavaScript provides the Intl.DurationFormat API to format time spans according to the user's language and cultural conventions. This lesson explains how to create duration formatters, build duration objects, and display time spans correctly for any locale.

What time spans are

A time span represents a length of time, not a point in time. The number 150 minutes is a duration. March 15, 2025 at 2:30 PM is a date and time.

This distinction matters because dates involve calendars, timezones, and historical rules. Time spans measure elapsed time without calendar context. You cannot add a timezone to a duration because durations exist independently of any specific moment.

Use Intl.DurationFormat for time spans. Use Intl.DateTimeFormat for dates and times. Use Intl.RelativeTimeFormat for relative expressions like "2 hours ago".

Create a duration formatter

The Intl.DurationFormat constructor takes a locale and an options object. The locale determines the output language. The options control formatting style and unit display.

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

Call format() with a duration object to produce a formatted string. The duration object contains numeric properties for time units.

const duration = { hours: 2, minutes: 30 };
formatter.format(duration);
// "2 hours and 30 minutes"

The API handles abbreviations, conjunctions, word order, and spacing automatically based on the locale.

Build duration objects

A duration object is a plain JavaScript object with properties for time units. Include only the units you want to display.

const duration1 = { hours: 2, minutes: 30 };
const duration2 = { minutes: 5, seconds: 45 };
const duration3 = { hours: 1, minutes: 15, seconds: 30 };

The API supports these time units: years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds.

You do not need to include all units. Omit any unit you do not want to display.

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

formatter.format({ hours: 2, minutes: 30 });
// "2 hours and 30 minutes"

formatter.format({ minutes: 30 });
// "30 minutes"

formatter.format({ hours: 2 });
// "2 hours"

Choose a formatting style

The style option controls output density. Four styles are available: long, short, narrow, and digital.

Long style uses full words. Use this for prose and main content areas.

const duration = { hours: 2, minutes: 30 };

new Intl.DurationFormat('en', { style: 'long' }).format(duration);
// "2 hours and 30 minutes"

Short style uses common abbreviations. Use this when space is limited but readability matters.

new Intl.DurationFormat('en', { style: 'short' }).format(duration);
// "2 hr and 30 min"

Narrow style uses minimal characters. Use this for compact displays like mobile interfaces or data tables.

new Intl.DurationFormat('en', { style: 'narrow' }).format(duration);
// "2h 30m"

Digital style produces timer-like output with colons. Use this for media players and countdown displays.

new Intl.DurationFormat('en', { style: 'digital' }).format(duration);
// "2:30:00"

Digital style requires you to include all units from largest to smallest. If you format hours and minutes, you must also include seconds.

Format time spans in different languages

The same time span formats differently in each language. The API handles all localization automatically.

const duration = { hours: 2, minutes: 30 };

new Intl.DurationFormat('en', { style: 'long' }).format(duration);
// "2 hours and 30 minutes"

new Intl.DurationFormat('fr', { style: 'long' }).format(duration);
// "2 heures et 30 minutes"

new Intl.DurationFormat('de', { style: 'long' }).format(duration);
// "2 Stunden und 30 Minuten"

new Intl.DurationFormat('es', { style: 'long' }).format(duration);
// "2 horas y 30 minutos"

new Intl.DurationFormat('ja', { style: 'long' }).format(duration);
// "2時間30分"

Notice how each locale uses different words and conjunctions. French uses "et", German uses "und", Spanish uses "y", Japanese uses no conjunctions. The API knows these rules for every locale.

Short and narrow styles also localize correctly.

new Intl.DurationFormat('fr', { style: 'short' }).format(duration);
// "2 h et 30 min"

new Intl.DurationFormat('de', { style: 'narrow' }).format(duration);
// "2 Std. 30 Min."

Format time spans for the user's locale

Instead of hardcoding a locale, use the user's preferred language from the browser. The navigator.language property returns the user's top language preference.

const userLocale = navigator.language;
const formatter = new Intl.DurationFormat(userLocale, { style: 'short' });

const duration = { hours: 2, minutes: 30 };
formatter.format(duration);
// Output varies by user's locale
// For en-US: "2 hr and 30 min"
// For de-DE: "2 Std. und 30 Min."
// For fr-FR: "2 h et 30 min"

This displays time spans according to each user's expectations without requiring manual locale selection.

Convert milliseconds to duration objects

Time calculations often produce milliseconds. Convert milliseconds to duration objects by dividing by the appropriate factors.

const milliseconds = 9000000; // 2 hours 30 minutes

const hours = Math.floor(milliseconds / 3600000);
const minutes = Math.floor((milliseconds % 3600000) / 60000);
const seconds = Math.floor((milliseconds % 60000) / 1000);

const duration = { hours, minutes, seconds };

new Intl.DurationFormat('en', { style: 'long' }).format(duration);
// "2 hours, 30 minutes and 0 seconds"

Omit zero values unless you specifically want them displayed.

const duration = {};
if (hours > 0) duration.hours = hours;
if (minutes > 0) duration.minutes = minutes;
if (seconds > 0) duration.seconds = seconds;

new Intl.DurationFormat('en', { style: 'long' }).format(duration);
// "2 hours and 30 minutes"

Calculate time spans from two dates

Calculate the duration between two dates by subtracting timestamps, then convert the result to a duration object.

const startTime = new Date('2025-10-15T10:00:00');
const endTime = new Date('2025-10-15T12:30:00');

const diffMs = endTime - startTime;

const hours = Math.floor(diffMs / 3600000);
const minutes = Math.floor((diffMs % 3600000) / 60000);

const duration = { hours, minutes };

new Intl.DurationFormat('en', { style: 'short' }).format(duration);
// "2 hr and 30 min"

This approach works for any time calculation that produces milliseconds.

Format video player durations

Video players show duration in the controls. Use digital or narrow style for compact display.

function formatVideoDuration(totalSeconds) {
  const hours = Math.floor(totalSeconds / 3600);
  const minutes = Math.floor((totalSeconds % 3600) / 60);
  const seconds = Math.floor(totalSeconds % 60);

  const duration = hours > 0
    ? { hours, minutes, seconds }
    : { minutes, seconds };

  const locale = navigator.language;
  return new Intl.DurationFormat(locale, { style: 'digital' }).format(duration);
}

formatVideoDuration(9000); // "2:30:00"
formatVideoDuration(330);  // "5:30"

This conditionally includes hours only when needed, showing "5:30" for short videos and "2:30:00" for longer content.

Format workout durations

Fitness apps track exercise duration. Use long style for session summaries and narrow style for compact list views.

function formatWorkoutDuration(startTime, endTime, locale) {
  const diffMs = endTime - startTime;

  const hours = Math.floor(diffMs / 3600000);
  const minutes = Math.floor((diffMs % 3600000) / 60000);

  const duration = hours > 0
    ? { hours, minutes }
    : { minutes };

  return new Intl.DurationFormat(locale, { style: 'long' }).format(duration);
}

const workoutStart = new Date('2025-10-15T07:00:00');
const workoutEnd = new Date('2025-10-15T09:30:00');

formatWorkoutDuration(workoutStart, workoutEnd, 'en');
// "2 hours and 30 minutes"

formatWorkoutDuration(workoutStart, workoutEnd, 'es');
// "2 horas y 30 minutos"

Format project task durations

Project management tools display how long tasks take. Use short style for dashboard displays.

function formatTaskDuration(minutes, locale) {
  const hours = Math.floor(minutes / 60);
  const mins = minutes % 60;

  const duration = {};
  if (hours > 0) duration.hours = hours;
  if (mins > 0) duration.minutes = mins;

  return new Intl.DurationFormat(locale, { style: 'short' }).format(duration);
}

formatTaskDuration(150, 'en');
// "2 hr and 30 min"

formatTaskDuration(45, 'en');
// "45 min"

formatTaskDuration(150, 'de');
// "2 Std. und 30 Min."

Format different time units

Time spans are not limited to hours and minutes. Format any combination of supported units.

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

formatter.format({ days: 3, hours: 2 });
// "3 days and 2 hours"

formatter.format({ minutes: 45, seconds: 30 });
// "45 minutes and 30 seconds"

formatter.format({ hours: 1, minutes: 30, seconds: 45 });
// "1 hour, 30 minutes and 45 seconds"

The API handles the appropriate conjunctions and separators for any combination of units.

Format time spans with only seconds

When your duration is less than a minute, include only seconds.

const formatter = new Intl.DurationFormat('en', { style: 'short' });

formatter.format({ seconds: 45 });
// "45 sec"

formatter.format({ seconds: 5 });
// "5 sec"

For very short durations, you can include milliseconds.

formatter.format({ seconds: 5, milliseconds: 500 });
// "5 sec and 500 ms"

Reuse formatter instances for performance

Creating a new formatter involves loading locale data and processing options. When you format multiple time spans with the same locale and style, create the formatter once and reuse it.

const formatter = new Intl.DurationFormat('en', { style: 'short' });

const durations = [
  { hours: 1, minutes: 30 },
  { hours: 2, minutes: 15 },
  { minutes: 45 }
];

durations.map(d => formatter.format(d));
// ["1 hr and 30 min", "2 hr and 15 min", "45 min"]

This pattern improves performance when formatting many durations in loops or repeated renders.

Browser support

The Intl.DurationFormat API became Baseline in March 2025. It works in the latest versions of Chrome, Edge, Firefox, and Safari. Older browsers do not support this API.

Check support before using the API.

if (typeof Intl.DurationFormat !== 'undefined') {
  const formatter = new Intl.DurationFormat('en', { style: 'short' });
  return formatter.format(duration);
} else {
  return `${duration.hours}h ${duration.minutes}m`;
}

This provides a fallback for older browsers while using the native API when available.