Search code examples
javascriptdatetimeformattingdate-fns

How to format duration as XX hours and YY minute(s)


I have a given duration in minutes, e.g. 100 minutes, 140 minutes.

I need to display them in "XX hours and YY minute(s)" format.

e.g.:

1 minutes -> 1 minute
100 minutes -> 1 hour and 40 minutes
141 minutes -> 2 hour and 21 minutes

I'd prefer to do that with date-fns or a plain JS.

Thank you


Solution

  • No need for date-fns, or any library for that manner.

    Your browser supports formatting time, date, and duration with i18n.

    const divMod = (n, m) => [Math.floor(n / m), n % m];
    
    const createDurationFormatter = (locale, unitDisplay = 'long') => {
      const
        timeUnitFormatter = (locale, unit, unitDisplay) =>
          Intl.NumberFormat(locale, { style: 'unit', unit, unitDisplay }).format,
        fmtHours = timeUnitFormatter(locale, 'hour', unitDisplay),
        fmtMinutes = timeUnitFormatter(locale, 'minute', unitDisplay),
        fmtList = new Intl.ListFormat(locale, { style: 'long', type: 'conjunction' });
      return (minutes) => {
        const [hrs, mins] = divMod(minutes, 60);
        return fmtList.format([
          hrs ? fmtHours(hrs) : null,
          mins ? fmtMinutes(mins) : null
        ].filter(v => v !== null));
      }
    };
    
    let durationFormatterEn = createDurationFormatter('en-US');
    console.log(durationFormatterEn(1));  // 1 minute
    console.log(durationFormatterEn(100)) // 1 hour and 40 minutes
    console.log(durationFormatterEn(141)) // 2 hours and 21 minutes
    
    let durationFormatterEs = createDurationFormatter('es-US');
    console.log(durationFormatterEs(1));  // 1 minuto
    console.log(durationFormatterEs(100)) // 1 hora y 40 minutos
    console.log(durationFormatterEs(141)) // 2 horas y 21 minutos
    
    let durationFormatterEn2 = createDurationFormatter('en-US', 'short');
    console.log(durationFormatterEn2(1));  // 1 min
    console.log(durationFormatterEn2(100)) // 1 hr and 40 min
    console.log(durationFormatterEn2(141)) // 2 hr and 21 min
    .as-console-wrapper { top: 0; max-height: 100% !important; }

    A more robust solution would be requiring milliseconds and formatting up to days:

    const divMod = (n, m) => [Math.floor(n / m), n % m];
    
    const createDurationFormatter = (locale, unitDisplay = 'long') => {
      const
        timeUnitFormatter = (locale, unit, unitDisplay) =>
          Intl.NumberFormat(locale, { style: 'unit', unit, unitDisplay }).format,
        fmtDays = timeUnitFormatter(locale, 'day', unitDisplay),
        fmtHours = timeUnitFormatter(locale, 'hour', unitDisplay),
        fmtMinutes = timeUnitFormatter(locale, 'minute', unitDisplay),
        fmtSeconds = timeUnitFormatter(locale, 'second', unitDisplay),
        fmtMilliseconds = timeUnitFormatter(locale, 'millisecond', unitDisplay),
        fmtList = new Intl.ListFormat(locale, { style: 'long', type: 'conjunction' });
      return (milliseconds) => {
        let days, hours, minutes, seconds;
        [days, milliseconds] = divMod(milliseconds, 864e5);
        [hours, milliseconds] = divMod(milliseconds, 36e5);
        [minutes, milliseconds] = divMod(milliseconds, 6e4);
        [seconds, milliseconds] = divMod(milliseconds, 1e3);
        return fmtList.format([
          days ? fmtDays(days) : null,
          hours ? fmtHours(hours) : null,
          minutes ? fmtMinutes(minutes) : null,
          seconds ? fmtSeconds(seconds) : null,
          milliseconds ? fmtMilliseconds(milliseconds) : null
        ].filter(v => v !== null));
      }
    };
    
    let durationFormatter = createDurationFormatter('en-US');
    console.log(durationFormatter(6e4));      // 1 minute
    console.log(durationFormatter(6e6));      // 1 hour and 40 minutes
    console.log(durationFormatter(846e4));    // 2 hour and 21 minutes
    console.log(durationFormatter(154849e3)); // 1 day, 19 hours, and 49 seconds
    .as-console-wrapper { top: 0; max-height: 100% !important; }