라이브러리 없이 JavaScript에서 시간대가 올바른 날짜 포맷
JavaScript 내장 Intl.DateTimeFormat은 moment.js나 date-fns 없이 IANA 시간대 포맷을 처리합니다. 명시적 timeZone 옵션, 일광 절약에 안전한 표시, formatToParts, 벽시계 변환의 한계, 그리고 Temporal이나 시간대 라이브러리가 여전히 유용한 때를 배웁니다.
Date에 시간대 속성이 없는 이유
JavaScript의 Date는 항상 UTC 밀리초 카운트입니다. 객체 안에 저장된 시간대는 없습니다. 옵션 없이 .toString()이나 .toLocaleString()을 호출하면 JavaScript는 운영체제에서 가져온 런타임의 로컬 시간대를 씁니다. 같은 코드라도 뉴욕의 서버와 도쿄의 노트북에서는 기반 타임스탬프가 동일해도 다른 출력을 냅니다.
Intl.DateTimeFormat으로 포맷하기
Intl.DateTimeFormat은 로캘과 시간대를 인식하는 포맷을 위한 내장 API입니다. IANA 시간대 식별자를 지원하고, 일광 절약 시간 전환을 자동으로 처리하며, 현대 브라우저와 Node.js에서 사용할 수 있습니다. 핵심은 런타임 기본값에 의존하지 말고 timeZone 옵션을 명시적으로 넘기는 것입니다.
- new Intl.DateTimeFormat('en-US', { timeZone: 'America/New_York', dateStyle: 'full', timeStyle: 'long' }).format(date)
- date.toLocaleString('en-GB', { timeZone: 'Europe/London', hour12: false })
- date.toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' })
- new Intl.DateTimeFormat('en-US', { timeZone: 'UTC', hour12: false }).format(date)
formatToParts로 개별 부분 추출하기
formatToParts()를 사용해 날짜의 개별 구성 요소를 {type, value} 객체로 얻어 사용자 정의 날짜 문자열을 구성하세요. 이는 로캘에 따라 구두점, 순서, 문자 체계가 다르기 때문에 로컬라이즈된 날짜 문자열을 쪼개는 것보다 낫습니다.
- const parts = new Intl.DateTimeFormat('en-US', { timeZone: 'America/Chicago', year: 'numeric', month: '2-digit', day: '2-digit' }).formatToParts(date)
- parts.find(p => p.type === 'year').value → '2023'
- parts.find(p => p.type === 'month').value → '11'
- Object.fromEntries(parts.map(p => [p.type, p.value])) → { year, month, day, hour, minute, second }
벽시계 시간을 UTC로 변환하기(어려운 방향)
주어진 시간대의 벽시계 시간에서 UTC로 가는 것은 고전적인 Date API로는 더 어렵습니다. 어떤 순간을 America/New_York으로 포맷하는 것은 쉽지만, America/New_York에서 2026-03-08 02:30이 나타내는 순간을 구성하는 것은 쉽지 않습니다. 그 로컬 시간이 일광 절약 시간 전환 동안 건너뛰어지거나 반복될 수 있기 때문입니다.
- Temporal은 2026년에 Stage 4에 도달했지만 브라우저 네이티브 지원은 아직 보편적이지 않음
- 오늘날 모든 브라우저에서 프로덕션에 쓰려면 Temporal 폴리필이나 date-fns-tz의 toDate()가 여전히 실용적 선택지
- 수동 UTC 오프셋 산술을 피하세요 — 일광 절약 시간 전환은 해마다 다른 로컬 시각에 일어남
- 이 사이트의 zonedToEpochMs()는 1회 반복 오프셋 보정을 사용 — src/timeUtils.ts 참고
올바른 시간대 식별자 고르기
America/New_York, Europe/London, Asia/Tokyo, UTC 같은 IANA 시간대 이름을 쓰세요. 사용자 대상 시간에는 UTC-5 같은 고정 오프셋을 피하세요. 오프셋은 일광 절약 시간에 따라 바뀌기 때문입니다. America/New_York은 1월에 UTC-5, 7월에 UTC-4일 수 있습니다. IANA 이름은 플랫폼이 선택된 날짜에 올바른 과거 규칙을 적용하게 합니다.
- 좋음: America/Los_Angeles — 과거와 미래의 일광 절약 규칙 포함
- 좋음: Europe/Berlin — 중앙유럽 여름 시간을 자동 처리
- 좋음: Asia/Shanghai — 일광 절약 없는 안정적인 UTC+8 표시
- 로그, API 저장, 데이터베이스 타임스탬프, 리전 간 비교에는 UTC를 사용
- 제품 UI의 최종 표시에는 사용자의 IANA 시간대를 사용
시간대 포맷 FAQ
- JavaScript는 라이브러리 없이 다른 시간대의 날짜를 포맷할 수 있나요?
- 예. America/New_York이나 Asia/Tokyo 같은 timeZone 옵션과 함께 Intl.DateTimeFormat이나 toLocaleString을 쓰세요.
- Intl.DateTimeFormat이 일광 절약 시간을 처리하나요?
- 예. IANA 시간대 식별자를 쓰면 런타임이 그 시간대와 날짜에 맞는 올바른 오프셋을 적용합니다.
- 시간대 오프셋을 저장할까요, 시간대 이름을 저장할까요?
- 이벤트 순간에는 UTC를 저장하세요. 사용자의 로컬 맥락이 필요하면 숫자 오프셋만이 아니라 America/New_York 같은 IANA 시간대 이름을 저장하세요.
- JavaScript에서 사용자의 시간대를 어떻게 얻나요?
- Intl.DateTimeFormat().resolvedOptions().timeZone을 쓰면 America/New_York 같은 IANA 이름이 반환됩니다. 나중에 사용자의 로컬 시간을 재구성해야 한다면 숫자 오프셋이 아니라 이 이름을 저장하세요.