2시간 30분과 같은 시간 간격을 포맷팅하는 방법

자동 현지화로 사용자의 언어로 지속 시간 표시

소개

무언가의 소요 시간을 표시할 때, 사용자가 이해할 수 있는 방식으로 그 기간을 표시해야 합니다. 비디오는 2시간 30분의 재생 시간을 보여주고, 운동 앱은 운동 시간을 추적하며, 프로젝트 관리 도구는 작업 완료 시간을 표시합니다. 현지화 없이는 다음과 같은 코드를 작성할 수 있습니다:

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

이는 언어에 관계없이 모든 사용자에게 "2h 30m"을 생성합니다. 프랑스 사용자는 "2 h 30 min"을 기대할 때 "2h 30m"을 보게 됩니다. 독일 사용자는 "2 Std. 30 Min" 대신 영어 약어를 보게 됩니다. 스페인 사용자는 단위 사이에 "y" 접속사가 없습니다.

JavaScript는 사용자의 언어와 문화적 관습에 따라 시간 간격을 형식화하는 Intl.DurationFormat API를 제공합니다. 이 수업에서는 기간 포맷터를 생성하고, 기간 객체를 구축하며, 모든 로케일에 대해 시간 간격을 올바르게 표시하는 방법을 설명합니다.

시간 간격이란 무엇인가

시간 간격은 특정 시점이 아닌 시간의 길이를 나타냅니다. 150분이라는 숫자는 기간입니다. 2025년 3월 15일 오후 2시 30분은 날짜와 시간입니다.

이 구분은 중요합니다. 날짜는 달력, 시간대 및 역사적 규칙을 포함하기 때문입니다. 시간 간격은 달력 컨텍스트 없이 경과 시간을 측정합니다. 기간은 특정 순간과 독립적으로 존재하기 때문에 기간에 시간대를 추가할 수 없습니다.

시간 간격에는 Intl.DurationFormat을 사용하세요. 날짜와 시간에는 Intl.DateTimeFormat을 사용하세요. "2시간 전"과 같은 상대적 표현에는 Intl.RelativeTimeFormat을 사용하세요.

기간 포맷터 생성하기

'Intl.DurationFormat' 생성자는 로케일과 옵션 객체를 받습니다. 로케일은 출력 언어를 결정합니다. 옵션은 형식 스타일과 단위 표시를 제어합니다.

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

형식화된 문자열을 생성하려면 기간 객체와 함께 format()을 호출하세요. 기간 객체는 시간 단위에 대한 숫자 속성을 포함합니다.

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

API는 로케일에 따라 약어, 접속사, 단어 순서 및 간격을 자동으로 처리합니다.

기간 객체 구축하기

기간 객체는 시간 단위에 대한 속성을 가진 일반 JavaScript 객체입니다. 표시하고자 하는 단위만 포함하세요.

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

API는 다음과 같은 시간 단위를 지원합니다: years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds.

모든 단위를 포함할 필요는 없습니다. 표시하지 않으려는 단위는 생략하세요.

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"

포맷팅 스타일 선택하기

'style' 옵션은 출력 밀도를 제어합니다. 네 가지 스타일을 사용할 수 있습니다: long, short, narrow, digital.

긴(long) 스타일은 전체 단어를 사용합니다. 본문과 주요 콘텐츠 영역에 이 스타일을 사용하세요.

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

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

짧은(short) 스타일은 일반적인 약어를 사용합니다. 공간이 제한되어 있지만 가독성이 중요할 때 이 스타일을 사용하세요.

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

좁은(narrow) 스타일은 최소한의 문자를 사용합니다. 모바일 인터페이스나 데이터 테이블과 같은 컴팩트한 디스플레이에 이 스타일을 사용하세요.

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

디지털(digital) 스타일은 콜론이 있는 타이머와 같은 출력을 생성합니다. 미디어 플레이어와 카운트다운 디스플레이에 이 스타일을 사용하세요.

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

디지털 스타일은 가장 큰 단위부터 가장 작은 단위까지 모든 단위를 포함해야 합니다. 시간과 분을 포맷팅하는 경우 초도 포함해야 합니다.

다양한 언어로 시간 간격 포맷팅하기

동일한 시간 간격이 각 언어마다 다르게 포맷팅됩니다. API는 모든 현지화를 자동으로 처리합니다.

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分"

각 로케일이 서로 다른 단어와 접속사를 사용하는 것에 주목하세요. 프랑스어는 "et", 독일어는 "und", 스페인어는 "y"를 사용하고, 일본어는 접속사를 사용하지 않습니다. API는 모든 로케일에 대한 이러한 규칙을 알고 있습니다.

짧은(short) 스타일과 좁은(narrow) 스타일도 올바르게 현지화됩니다.

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."

사용자 로케일에 맞게 시간 간격 포맷팅하기

로케일을 하드코딩하는 대신 브라우저에서 사용자가 선호하는 언어를 사용하세요. navigator.language 속성은 사용자의 최상위 언어 선호도를 반환합니다.

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

const duration = { hours: 2, minutes: 30 };
formatter.format(duration);
// 출력은 사용자의 로케일에 따라 달라집니다
// en-US의 경우: "2 hr and 30 min"
// de-DE의 경우: "2 Std. und 30 Min."
// fr-FR의 경우: "2 h et 30 min"

이렇게 하면 수동으로 로케일을 선택할 필요 없이 각 사용자의 기대에 맞게 시간 간격을 표시할 수 있습니다.

밀리초를 기간 객체로 변환하기

시간 계산은 종종 밀리초를 생성합니다. 적절한 요소로 나누어 밀리초를 기간 객체로 변환하세요.

const milliseconds = 9000000; // 2시간 30분

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"

특별히 표시하고 싶지 않다면 0 값은 생략하세요.

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"

두 날짜 간의 시간 간격 계산하기

두 날짜 사이의 기간을 계산하려면 타임스탬프를 빼고 결과를 기간 객체로 변환하세요.

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"

이 접근 방식은 밀리초를 생성하는 모든 시간 계산에 적용됩니다.

비디오 플레이어 재생 시간 포맷팅

비디오 플레이어는 컨트롤에 재생 시간을 표시합니다. 컴팩트한 표시를 위해 디지털 또는 좁은 스타일을 사용하세요.

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"

이 방식은 필요할 때만 시간을 조건부로 포함하여 짧은 비디오는 "5:30"으로, 긴 콘텐츠는 "2:30:00"으로 표시합니다.

운동 시간 형식 지정

피트니스 앱은 운동 시간을 추적합니다. 세션 요약에는 긴 스타일을 사용하고 간결한 목록 보기에는 좁은 스타일을 사용하세요.

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"

프로젝트 작업 시간 형식 지정

프로젝트 관리 도구는 작업 소요 시간을 표시합니다. 대시보드 표시에는 짧은 스타일을 사용하세요.

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."

다양한 시간 단위 형식 지정

시간 범위는 시간과 분에만 국한되지 않습니다. 지원되는 단위의 모든 조합에 형식을 지정할 수 있습니다.

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"

API는 모든 단위 조합에 대해 적절한 접속사와 구분 기호를 처리합니다.

초 단위만 있는 시간 간격 포맷팅

지속 시간이 1분 미만인 경우, 초만 포함하세요.

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

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

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

매우 짧은 지속 시간의 경우, 밀리초를 포함할 수 있습니다.

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

성능을 위해 포맷터 인스턴스 재사용

새 포맷터를 생성하면 로케일 데이터를 로드하고 옵션을 처리하는 과정이 포함됩니다. 동일한 로케일과 스타일로 여러 시간 간격을 포맷팅할 때는 포맷터를 한 번 생성하고 재사용하세요.

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"]

이 패턴은 루프나 반복적인 렌더링에서 많은 지속 시간을 포맷팅할 때 성능을 향상시킵니다.

브라우저 지원

'Intl.DurationFormat' API는 2025년 3월에 기준선이 되었습니다. 최신 버전의 Chrome, Edge, Firefox 및 Safari에서 작동합니다. 이전 브라우저는 이 API를 지원하지 않습니다.

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

이는 이전 브라우저에 대한 대체 방안을 제공하면서 가능한 경우 네이티브 API를 사용합니다.