Contents
see List이 글은 Intl.DateTimeFormat에 대한 내용이 아닙니다. 본 가이드에서 다루는 JavaScript Temporal API는 기존 Date 객체의 근본적인 한계를 해결하기 위해 TC39에서 Stage 3까지 진행된 차세대 날짜/시간 표준입니다. 타임존, 불변성, 나노초 정밀도, 다중 캘린더를 네이티브로 지원하며, 2025년부터 주요 브라우저에 순차적으로 탑재되고 있습니다.
기존 Date의 문제점
JavaScript의 Date 객체는 1995년 Java의 java.util.Date를 10일 만에 복제한 결과물입니다. 주요 문제점은 다음과 같습니다.
- 뮤터블(Mutable) - setMonth() 등 메서드가 원본 객체를 직접 변경
- 타임존 미지원 - UTC와 로컬 타임존만 다루며, IANA 타임존 변환 불가
- 밀리초 정밀도 - 마이크로초/나노초 단위 연산 불가능
- 달력 시스템 - 그레고리력만 지원, 히브리/이슬람/일본력 등 미지원
- 월 인덱스 0 시작 - month가 0~11로 직관적이지 않음
Temporal API 핵심 클래스
Temporal은 용도별로 분리된 7개 핵심 클래스를 제공합니다.
| 클래스 | 용도 | 예시 |
|---|---|---|
| Temporal.Instant | UTC 기준 절대 시점 (나노초) | API 타임스탬프 |
| Temporal.ZonedDateTime | 타임존 + 캘린더 포함 시점 | 회의 일정 관리 |
| Temporal.PlainDateTime | 타임존 없는 날짜+시간 | 알람 설정 |
| Temporal.PlainDate | 날짜만 (연/월/일) | 생년월일 |
| Temporal.PlainTime | 시간만 (시/분/초) | 영업 시간 |
| Temporal.Duration | 시간 간격 | 타이머, 경과 시간 |
| Temporal.Now | 현재 시각 조회 | 현재 날짜/시간 |
현재 시각 조회 - Temporal.Now
// 현재 UTC 시점
const instant = Temporal.Now.instant();
console.log(instant.toString());
// "2026-04-15T02:11:00Z"
// 현재 로컬 날짜
const today = Temporal.Now.plainDateISO();
console.log(today.toString());
// "2026-04-15"
// 타임존 포함 현재 시각
const now = Temporal.Now.zonedDateTimeISO();
console.log(now.toString());
// "2026-04-15T11:11:00+09:00[Asia/Seoul]"
날짜 생성과 조작
// PlainDate 생성
const date = Temporal.PlainDate.from('2026-04-15');
const date2 = Temporal.PlainDate.from({ year: 2026, month: 4, day: 15 });
// 불변 연산 - 원본은 변하지 않음
const nextMonth = date.add({ months: 1 });
console.log(nextMonth.toString()); // "2026-05-15"
console.log(date.toString()); // "2026-04-15" (원본 유지)
// 특정 필드만 변경
const modified = date.with({ month: 12, day: 25 });
console.log(modified.toString()); // "2026-12-25"
// 날짜 차이 계산
const start = Temporal.PlainDate.from('2026-01-01');
const end = Temporal.PlainDate.from('2026-12-31');
const diff = end.since(start);
console.log(diff.toString()); // "P365D"
console.log(diff.total({ unit: 'days' })); // 365
타임존 변환 - ZonedDateTime
Temporal의 가장 강력한 기능입니다. IANA 타임존 데이터베이스를 네이티브로 지원합니다.
// 서울 시간 생성
const seoul = Temporal.ZonedDateTime.from(
'2026-04-15T14:30:00+09:00[Asia/Seoul]'
);
// 뉴욕 시간으로 변환 (자동 DST 처리)
const newYork = seoul.withTimeZone('America/New_York');
console.log(newYork.toString());
// "2026-04-15T01:30:00-04:00[America/New_York]"
// 런던 시간으로 변환
const london = seoul.withTimeZone('Europe/London');
console.log(london.toString());
// "2026-04-15T06:30:00+01:00[Europe/London]"
// DST 전환 감지
console.log(newYork.hoursInDay); // 24 (일반 날)
// DST 전환일에는 23 또는 25 반환
Duration 활용 - 시간 간격 연산
// Duration 생성
const meeting = Temporal.Duration.from({ hours: 1, minutes: 30 });
const breakTime = Temporal.Duration.from({ minutes: 15 });
// 연산
const total = meeting.add(breakTime);
console.log(total.toString()); // "PT1H45M"
console.log(total.total({ unit: 'minutes' })); // 105
// 두 시점 사이 간격
const eventStart = Temporal.PlainDateTime.from('2026-04-15T09:00');
const eventEnd = Temporal.PlainDateTime.from('2026-04-15T17:30');
const workHours = eventEnd.since(eventStart);
console.log(workHours.toString()); // "PT8H30M"
// 반올림
const rounded = workHours.round({ smallestUnit: 'hour' });
console.log(rounded.toString()); // "PT9H"
정렬과 비교
// 날짜 비교
const dates = [
Temporal.PlainDate.from('2026-12-25'),
Temporal.PlainDate.from('2026-01-01'),
Temporal.PlainDate.from('2026-07-04'),
];
// 정렬 (compare 함수 활용)
dates.sort(Temporal.PlainDate.compare);
console.log(dates.map(d => d.toString()));
// ["2026-01-01", "2026-07-04", "2026-12-25"]
// 동등 비교
const a = Temporal.PlainDate.from('2026-04-15');
const b = Temporal.PlainDate.from('2026-04-15');
console.log(a.equals(b)); // true
다중 캘린더 시스템
// 그레고리력 → 일본력 변환
const gregorian = Temporal.PlainDate.from('2026-04-15');
const japanese = gregorian.withCalendar('japanese');
console.log(japanese.toLocaleString('ja-JP-u-ca-japanese'));
// "令和8年4月15日"
// 히브리력
const hebrew = gregorian.withCalendar('hebrew');
console.log(hebrew.year, hebrew.month, hebrew.day);
실전 예제: D-Day 카운터
function getDDay(targetDate) {
const today = Temporal.Now.plainDateISO();
const target = Temporal.PlainDate.from(targetDate);
const diff = today.until(target);
const days = diff.total({ unit: 'days' });
if (days > 0) return 'D-' + days;
if (days === 0) return 'D-Day';
return 'D+' + Math.abs(days);
}
console.log(getDDay('2026-12-25')); // "D-254"
실전 예제: 글로벌 회의 스케줄러
function scheduleGlobalMeeting(baseTime, timeZones) {
const base = Temporal.ZonedDateTime.from(baseTime);
return timeZones.map(tz => ({
timezone: tz,
localTime: base.withTimeZone(tz)
.toLocaleString('en-US', {
weekday: 'short',
hour: '2-digit',
minute: '2-digit',
timeZoneName: 'short'
})
}));
}
const schedule = scheduleGlobalMeeting(
'2026-04-15T15:00:00+09:00[Asia/Seoul]',
['Asia/Seoul', 'America/New_York', 'Europe/London', 'Asia/Tokyo']
);
// [
// { timezone: 'Asia/Seoul', localTime: 'Wed, 03:00 PM KST' },
// { timezone: 'America/New_York', localTime: 'Wed, 02:00 AM EDT' },
// { timezone: 'Europe/London', localTime: 'Wed, 07:00 AM BST' },
// { timezone: 'Asia/Tokyo', localTime: 'Wed, 03:00 PM JST' }
// ]
Date vs Temporal 비교 정리
| 항목 | Date | Temporal |
|---|---|---|
| 불변성 | 뮤터블 (setMonth 등) | 완전 불변 |
| 타임존 | UTC/로컬만 | IANA 전체 지원 |
| 정밀도 | 밀리초 | 나노초 |
| 캘린더 | 그레고리력만 | 다중 캘린더 |
| 월 인덱스 | 0-11 | 1-12 |
| 파싱 | 브라우저마다 다름 | RFC 9557 표준 |
| DST 처리 | 수동 | 자동 |
브라우저 지원 현황 (2026년 4월 기준)
Chrome 129+, Edge 129+에서 플래그 없이 사용 가능하며, Firefox와 Safari도 구현 진행 중입니다. 프로덕션에서 사용하려면 폴리필(@js-temporal/polyfill 또는 temporal-polyfill)을 함께 적용하는 것을 권장합니다.
// 폴리필 설치
npm install @js-temporal/polyfill
// 사용
import { Temporal } from '@js-temporal/polyfill';
const now = Temporal.Now.plainDateISO();
Temporal API는 JavaScript 날짜/시간 처리의 패러다임을 바꾸는 표준입니다. moment.js나 date-fns 같은 라이브러리 없이도 타임존 변환, DST 처리, 나노초 정밀도 연산이 가능해집니다. 지금부터 폴리필과 함께 프로젝트에 적용해 보시기 바랍니다.