다양한 시간대에서 날짜 형식을 지정하는 방법
JavaScript의 Intl.DateTimeFormat API를 사용하여 모든 시간대의 날짜와 시간 표시하기
소개
전 세계에서 동일한 순간이 서로 다른 시계 시간으로 표시됩니다. 뉴욕에서 오후 3시에 회의가 시작될 때, 런던의 시계는 오후 8시를 표시하고 도쿄의 시계는 다음 날 오전 5시를 표시합니다. 애플리케이션이 시간대를 고려하지 않고 시간을 표시하면 사용자는 잘못된 정보를 보게 됩니다.
캘리포니아의 사용자가 오후 2시에 출발하는 항공편을 예약할 때 오후 2시가 표시되기를 기대합니다. 애플리케이션이 버지니아에 있는 서버의 시간대를 사용하여 모든 시간을 형식화하면 사용자는 오후 5시를 보고 공항에 3시간 늦게 도착합니다. 이러한 혼란은 애플리케이션에 표시되는 모든 시간에서 누적됩니다.
JavaScript의 Intl.DateTimeFormat API는 모든 시간대에 대해 날짜와 시간을 포맷하기 위한 timeZone 옵션을 제공합니다. 이 레슨에서는 시간대가 존재하는 이유, JavaScript가 내부적으로 시간대를 처리하는 방법, 그리고 전 세계 어디에 있는 사용자에게도 날짜를 올바르게 포맷하는 방법을 설명합니다.
시간대가 존재하는 이유
지구가 자전하면서 서로 다른 위치에서 서로 다른 시간에 낮과 밤이 만들어집니다. 뉴욕에서 태양이 머리 위에 있을 때, 런던에서는 이미 해가 지고 도쿄에서는 아직 해가 뜨지 않았습니다. 각 위치는 서로 다른 순간에 정오를 경험합니다.
표준화된 시간대가 생기기 전에는 각 도시가 태양의 위치를 기준으로 자체 현지 시간을 유지했습니다. 이는 먼 도시를 연결하는 철도와 전신에 문제를 일으켰습니다. 1884년에 각국은 세계를 시간대로 나누기로 합의했으며, 각 시간대는 지구 자전의 한 시간에 해당하는 약 15도의 경도 폭을 가집니다.
각 시간대는 협정 세계시(UTC)로부터의 표준 오프셋을 가지고 있습니다. 뉴욕은 일광 절약 시간제에 따라 UTC-5 또는 UTC-4를 사용합니다. 런던은 UTC+0 또는 UTC+1을 사용합니다. 도쿄는 연중 UTC+9를 사용합니다.
사용자에게 시간을 표시할 때는 보편적인 순간을 사용자가 보기를 기대하는 현지 시계 시간으로 변환해야 합니다.
JavaScript가 내부적으로 시간을 저장하는 방법
JavaScript Date 객체는 1970년 1월 1일 자정 UTC 이후의 밀리초 수로 단일 시점을 나타냅니다. 이 내부 표현에는 시간대가 없습니다.
const date = new Date('2025-03-15T20:00:00Z');
console.log(date.getTime());
// Output: 1742331600000
ISO 문자열 끝의 Z는 UTC 시간을 나타냅니다. Date 객체는 타임스탬프 1742331600000를 저장하며, 이는 시간대에 관계없이 전 세계 모든 사람에게 동일한 순간을 나타냅니다.
Date 객체에서 toString()와 같은 메서드를 호출하면 JavaScript는 표시를 위해 UTC 타임스탬프를 로컬 시간대로 변환합니다. 이러한 자동 변환은 다른 시간대의 시간을 표시하려고 할 때 혼란을 야기할 수 있습니다.
timeZone 옵션이 있는 Intl.DateTimeFormat API는 포맷에 사용할 시간대를 명시적으로 제어할 수 있게 해줍니다.
timeZone 옵션 사용하기
timeZone 옵션은 날짜를 포맷할 때 사용할 시간대를 지정합니다. 포매터를 생성할 때 옵션 객체의 일부로 이 옵션을 전달하세요.
const date = new Date('2025-03-15T20:00:00Z');
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'America/New_York',
dateStyle: 'short',
timeStyle: 'short'
});
console.log(formatter.format(date));
// Output: "3/15/25, 3:00 PM"
날짜는 오후 8시 UTC를 나타냅니다. 뉴욕은 표준 시간 동안 UTC-5 또는 일광 절약 시간 동안 UTC-4입니다. 3월에는 일광 절약 시간제가 활성화되어 뉴욕은 UTC-4입니다. 포매터는 오후 8시 UTC를 오후 4시 현지 시간으로 변환하지만, 예제는 오후 3시를 표시하고 있어 이 특정 시나리오에서는 표준 시간 동안임을 시사합니다.
포매터는 변환을 자동으로 처리합니다. UTC 시간과 대상 시간대를 제공하면 API가 올바른 현지 시간을 생성합니다.
IANA 시간대 이름 이해하기
timeZone 옵션은 IANA 시간대 데이터베이스의 시간대 식별자를 허용합니다. 이러한 식별자는 Region/City 형식을 사용하며, America/New_York, Europe/London 또는 Asia/Tokyo와 같은 예가 있습니다.
const date = new Date('2025-03-15T20:00:00Z');
const zones = [
'America/New_York',
'Europe/London',
'Asia/Tokyo',
'Australia/Sydney'
];
zones.forEach(zone => {
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: zone,
dateStyle: 'short',
timeStyle: 'long'
});
console.log(`${zone}: ${formatter.format(date)}`);
});
// Output:
// America/New_York: 3/15/25, 4:00:00 PM EDT
// Europe/London: 3/15/25, 8:00:00 PM GMT
// Asia/Tokyo: 3/16/25, 5:00:00 AM JST
// Australia/Sydney: 3/16/25, 7:00:00 AM AEDT
각 시간대는 동일한 순간에 대해 서로 다른 현지 시간을 표시합니다. 뉴욕은 3월 15일 오후 4시를 표시합니다. 런던은 3월 15일 오후 8시를 표시합니다. 도쿄와 시드니는 이미 3월 16일로 진행되어 각각 오전 5시와 오전 7시를 표시합니다.
IANA 이름은 일광 절약 시간제를 자동으로 처리합니다. 포매터는 America/New_York가 동부 표준시와 동부 일광 절약 시간 사이를 전환한다는 것을 알고 있으며 모든 날짜에 대해 올바른 오프셋을 적용합니다.
유효한 IANA 시간대 이름 찾기
IANA 데이터베이스에는 수백 개의 시간대 식별자가 포함되어 있습니다. 일반적인 패턴은 다음과 같습니다.
America/New_York뉴욕America/Los_Angeles로스앤젤레스America/Chicago시카고Europe/London런던Europe/Paris파리Europe/Berlin베를린Asia/Tokyo도쿄Asia/Shanghai상하이Asia/Kolkata인도Australia/Sydney시드니Pacific/Auckland오클랜드
식별자는 EST 또는 PST와 같은 시간대 약어 대신 도시 이름을 사용합니다. 약어는 모호하기 때문입니다. EST는 북미에서는 동부 표준시를 의미하지만 호주 동부 표준시를 의미하기도 합니다. 도시 기반 이름은 모호하지 않습니다.
IANA 시간대 데이터베이스 문서에서 전체 식별자 목록을 검색할 수 있습니다.
시간대로 UTC 사용하기
특수 식별자 UTC는 본초 자오선으로부터 오프셋이 없는 협정 세계시로 날짜를 포맷팅합니다.
const date = new Date('2025-03-15T20:00:00Z');
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'UTC',
dateStyle: 'short',
timeStyle: 'long'
});
console.log(formatter.format(date));
// Output: "3/15/25, 8:00:00 PM UTC"
UTC 시간은 Date 객체에 저장된 내부 타임스탬프와 일치합니다. UTC를 사용한 형식화는 서버 로그나 데이터베이스 타임스탬프와 같이 사용자의 위치에 따라 변경되어서는 안 되는 시간을 표시할 때 유용합니다.
사용자의 시간대 가져오기
resolvedOptions() 메서드는 시간대를 포함하여 포매터가 사용하는 실제 옵션을 반환합니다. timeZone를 지정하지 않고 포매터를 생성하면 기본적으로 사용자의 시스템 시간대가 사용됩니다.
const formatter = new Intl.DateTimeFormat();
const options = formatter.resolvedOptions();
console.log(options.timeZone);
// Output: "America/New_York" (or user's actual time zone)
이를 통해 사용자의 현재 시간대에 대한 IANA 식별자를 얻을 수 있습니다. 이 식별자를 사용하여 동일한 시간대에서 다른 날짜를 형식화하거나 사용자의 시간대 기본 설정을 저장할 수 있습니다.
const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: userTimeZone,
dateStyle: 'full',
timeStyle: 'long'
});
const date = new Date('2025-03-15T20:00:00Z');
console.log(formatter.format(date));
// Output varies based on user's time zone
이 패턴은 날짜가 자동으로 사용자의 현지 시간으로 표시되도록 보장합니다.
여러 시간대에 대해 동일한 순간 형식화하기
동일한 Date 객체를 여러 시간대에 대해 형식화하여 사용자에게 이벤트가 다른 위치에서 발생하는 시간을 표시할 수 있습니다.
const meetingTime = new Date('2025-03-15T20:00:00Z');
const zones = [
{ name: 'New York', zone: 'America/New_York' },
{ name: 'London', zone: 'Europe/London' },
{ name: 'Tokyo', zone: 'Asia/Tokyo' }
];
zones.forEach(({ name, zone }) => {
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: zone,
dateStyle: 'long',
timeStyle: 'short'
});
console.log(`${name}: ${formatter.format(meetingTime)}`);
});
// Output:
// New York: March 15, 2025 at 4:00 PM
// London: March 15, 2025 at 8:00 PM
// Tokyo: March 16, 2025 at 5:00 AM
이를 통해 사용자는 회의나 이벤트가 자신의 시간대와 다른 참가자의 시간대에서 언제 발생하는지 이해할 수 있습니다.
시간대 간 시간 없이 날짜 형식화하기
시간 없이 날짜를 형식화할 때 시간대는 표시되는 달력 날짜에 영향을 줄 수 있습니다. UTC 자정의 날짜는 다른 시간대에서 다른 달력 날짜에 해당합니다.
const date = new Date('2025-03-16T01:00:00Z');
const formatter1 = new Intl.DateTimeFormat('en-US', {
timeZone: 'America/Los_Angeles',
dateStyle: 'long'
});
const formatter2 = new Intl.DateTimeFormat('en-US', {
timeZone: 'Asia/Tokyo',
dateStyle: 'long'
});
console.log(`Los Angeles: ${formatter1.format(date)}`);
console.log(`Tokyo: ${formatter2.format(date)}`);
// Output:
// Los Angeles: March 15, 2025
// Tokyo: March 16, 2025
3월 16일 오전 1시 UTC의 순간은 로스앤젤레스에서는 3월 15일 오후 5시에 해당하고 도쿄에서는 3월 16일 오전 10시에 해당합니다. 두 시간대 간에 달력 날짜가 하루 차이가 납니다.
이는 예약된 이벤트, 마감일 또는 사용자가 현지 달력을 기준으로 해석하는 모든 날짜를 표시할 때 중요합니다.
시간대 오프셋 사용
IANA 식별자 대신 +01:00나 -05:00와 같은 오프셋 문자열을 사용할 수 있습니다. 이는 UTC로부터의 고정 오프셋을 나타냅니다.
const date = new Date('2025-03-15T20:00:00Z');
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: '+09:00',
dateStyle: 'short',
timeStyle: 'long'
});
console.log(formatter.format(date));
// Output: "3/16/25, 5:00:00 AM GMT+9"
오프셋 문자열은 정확한 오프셋을 알고 있지만 특정 위치를 모를 때 유용합니다. 그러나 일광 절약 시간제를 처리하지 않습니다. 뉴욕을 나타내기 위해 -05:00를 사용하면 뉴욕이 실제로 -04:00를 사용하는 일광 절약 시간 동안 시간이 잘못됩니다.
IANA 식별자는 일광 절약 시간제를 자동으로 처리하므로 선호됩니다.
일광 절약 시간제 작동 방식 이해
많은 지역에서 일광 절약 시간제를 위해 연 2회 시계 오프셋을 변경합니다. 봄에는 시계가 1시간 앞으로 이동하고, 가을에는 시계가 1시간 뒤로 이동합니다. 이는 위치의 UTC 오프셋이 연중 변경됨을 의미합니다.
IANA 시간대 식별자를 사용하면 Intl.DateTimeFormat API가 모든 날짜에 대해 올바른 오프셋을 자동으로 적용합니다.
const winterDate = new Date('2025-01-15T20:00:00Z');
const summerDate = new Date('2025-07-15T20:00:00Z');
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'America/New_York',
dateStyle: 'long',
timeStyle: 'long'
});
console.log(`Winter: ${formatter.format(winterDate)}`);
console.log(`Summer: ${formatter.format(summerDate)}`);
// Output:
// Winter: January 15, 2025 at 3:00:00 PM EST
// Summer: July 15, 2025 at 4:00:00 PM EDT
1월에 뉴욕은 UTC-5 오프셋의 동부 표준시를 사용하여 오후 3시를 표시합니다. 7월에 뉴욕은 UTC-4 오프셋의 동부 일광 절약 시간을 사용하여 오후 4시를 표시합니다. 동일한 UTC 시간이 일광 절약 시간제 적용 여부에 따라 다른 현지 시간을 생성합니다.
어떤 날짜가 어떤 오프셋을 사용하는지 추적할 필요가 없습니다. API가 이를 자동으로 처리합니다.
이벤트 일정을 위한 시간 형식 지정
이벤트 시간을 표시할 때 이벤트 위치의 시간과 선택적으로 사용자 위치의 시간을 형식 지정합니다.
const eventTime = new Date('2025-03-15T18:00:00Z');
const eventTimeZone = 'Europe/Paris';
const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const eventFormatter = new Intl.DateTimeFormat('en-US', {
timeZone: eventTimeZone,
dateStyle: 'full',
timeStyle: 'short'
});
const userFormatter = new Intl.DateTimeFormat('en-US', {
timeZone: userTimeZone,
dateStyle: 'full',
timeStyle: 'short'
});
console.log(`Event time: ${eventFormatter.format(eventTime)} (Paris)`);
console.log(`Your time: ${userFormatter.format(eventTime)}`);
// Output (for a user in New York):
// Event time: Saturday, March 15, 2025 at 7:00 PM (Paris)
// Your time: Saturday, March 15, 2025 at 2:00 PM
이 패턴은 사용자에게 이벤트가 자체 시간대에서 발생하는 시간과 위치를 기준으로 참여해야 하는 시간을 모두 표시합니다.
사용자 시간대로 서버 타임스탬프 형식 지정
서버 로그와 데이터베이스 레코드는 종종 UTC 타임스탬프를 사용합니다. 이를 사용자에게 표시할 때는 사용자의 로컬 시간대로 변환하세요.
const serverTimestamp = new Date('2025-03-15T20:00:00Z');
const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const formatter = new Intl.DateTimeFormat(navigator.language, {
timeZone: userTimeZone,
dateStyle: 'short',
timeStyle: 'medium'
});
console.log(`Activity: ${formatter.format(serverTimestamp)}`);
// Output varies based on user's time zone and locale
// For en-US in New York: "Activity: 3/15/25, 4:00:00 PM"
이렇게 하면 사용자가 UTC나 서버 시간이 아닌 익숙한 로컬 시간으로 타임스탬프를 볼 수 있습니다.
timeZone을 다른 옵션과 결합
timeZone 옵션은 다른 모든 Intl.DateTimeFormat 옵션과 함께 작동합니다. 개별 날짜 및 시간 구성 요소를 지정하거나, 스타일 프리셋을 사용하거나, 달력 시스템을 제어할 수 있습니다.
const date = new Date('2025-03-15T20:00:00Z');
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'Asia/Tokyo',
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
timeZoneName: 'long'
});
console.log(formatter.format(date));
// Output: "Monday, March 16, 2025 at 5:00:00 AM Japan Standard Time"
timeZoneName 옵션은 출력에서 시간대 이름이 표시되는 방식을 제어합니다. 이후 레슨에서 이 옵션을 자세히 다룰 예정입니다.
피해야 할 사항
timeZone 옵션의 값으로 EST, PST 또는 GMT와 같은 시간대 약어를 사용하지 마세요. 이러한 약어는 모호하며 일관되게 지원되지 않습니다.
// Incorrect - abbreviations may not work
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'EST', // This may throw an error
dateStyle: 'short',
timeStyle: 'short'
});
항상 America/New_York와 같은 IANA 식별자 또는 -05:00와 같은 오프셋 문자열을 사용하세요.
사용자의 시간대가 서버의 시간대와 일치한다고 가정하지 마세요. 항상 올바른 시간대로 명시적으로 시간을 형식 지정하거나 사용자의 감지된 시간대를 사용하세요.
여러 시간대에서 포매터 재사용
여러 시간대에 대해 날짜를 형식 지정할 때 많은 포매터를 생성할 수 있습니다. 동일한 설정으로 많은 날짜를 형식 지정하는 경우 더 나은 성능을 위해 포매터 인스턴스를 재사용하세요.
const dates = [
new Date('2025-03-15T20:00:00Z'),
new Date('2025-03-16T14:00:00Z'),
new Date('2025-03-17T09:00:00Z')
];
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'Europe/Berlin',
dateStyle: 'short',
timeStyle: 'short'
});
dates.forEach(date => {
console.log(formatter.format(date));
});
// Output:
// "3/15/25, 9:00 PM"
// "3/16/25, 3:00 PM"
// "3/17/25, 10:00 AM"
포매터를 생성하려면 옵션을 처리하고 로케일 데이터를 로드해야 합니다. 여러 날짜에 대해 동일한 포매터를 재사용하면 이러한 오버헤드를 피할 수 있습니다.