Contents
see ListJavaScript 개발자라면 누구나 Date 객체의 불편함을 경험했을 것이다. 월이 0부터 시작하는 문제, 타임존 처리의 어려움, 불변성 부재 등 수많은 함정이 있었다. ES2026에서 정식 표준이 된 Temporal API는 이 모든 문제를 근본적으로 해결한다. moment.js나 date-fns 없이도 날짜와 시간을 정확하고 직관적으로 다룰 수 있게 되었다.
Temporal API의 핵심 개념
Temporal은 날짜와 시간을 다루는 여러 타입을 제공하며, 각 타입은 명확한 목적을 가진다. 모든 Temporal 객체는 불변(immutable)이다.
주요 타입 일람
- Temporal.PlainDate - 타임존 없는 날짜 (2026-04-05)
- Temporal.PlainTime - 타임존 없는 시간 (14:30:00)
- Temporal.PlainDateTime - 타임존 없는 날짜+시간
- Temporal.ZonedDateTime - 타임존이 포함된 완전한 날짜+시간
- Temporal.Instant - UTC 기준의 절대 시점 (에포크 나노초)
- Temporal.Duration - 기간 (P1Y2M3DT4H5M6S)
- Temporal.PlainYearMonth - 연월 (2026-04)
- Temporal.PlainMonthDay - 월일 (04-05)
기본 사용법
날짜 생성과 조작
// 현재 날짜 (시스템 타임존 기준)
const today = Temporal.Now.plainDateISO();
console.log(today.toString()); // "2026-04-05"
// 특정 날짜 생성
const date = Temporal.PlainDate.from("2026-04-05");
const date2 = Temporal.PlainDate.from({ year: 2026, month: 4, day: 5 });
// 날짜 연산 (불변 - 새 객체 반환)
const nextWeek = today.add({ days: 7 });
const lastMonth = today.subtract({ months: 1 });
const specificDate = today.with({ day: 1 }); // 이번 달 1일
console.log(nextWeek.toString()); // "2026-04-12"
console.log(lastMonth.toString()); // "2026-03-05"
// 날짜 속성 접근
console.log(today.year); // 2026
console.log(today.month); // 4 (1부터 시작!)
console.log(today.day); // 5
console.log(today.dayOfWeek); // 7 (일요일, ISO 8601 기준)
시간 다루기
// 현재 시간
const now = Temporal.Now.plainTimeISO();
console.log(now.toString()); // "14:30:45.123456789"
// 특정 시간 생성
const time = Temporal.PlainTime.from("09:30:00");
const time2 = Temporal.PlainTime.from({ hour: 9, minute: 30 });
// 시간 연산
const later = time.add({ hours: 2, minutes: 30 });
console.log(later.toString()); // "12:00:00"
// 날짜와 시간 결합
const dateTime = today.toPlainDateTime(time);
console.log(dateTime.toString()); // "2026-04-05T09:30:00"
타임존 처리
Temporal의 가장 큰 강점 중 하나가 바로 명시적이고 정확한 타임존 처리다.
// 타임존이 포함된 날짜+시간
const seoulTime = Temporal.ZonedDateTime.from(
"2026-04-05T14:30:00+09:00[Asia/Seoul]"
);
console.log(seoulTime.toString());
// "2026-04-05T14:30:00+09:00[Asia/Seoul]"
// 타임존 변환
const nyTime = seoulTime.withTimeZone("America/New_York");
console.log(nyTime.toString());
// "2026-04-05T01:30:00-04:00[America/New_York]"
// 현재 시각을 특정 타임존으로
const tokyoNow = Temporal.Now.zonedDateTimeISO("Asia/Tokyo");
const londonNow = Temporal.Now.zonedDateTimeISO("Europe/London");
// Instant: UTC 기준 절대 시점
const instant = Temporal.Now.instant();
console.log(instant.toString()); // "2026-04-05T05:30:00Z"
// Instant에서 특정 타임존의 시각 구하기
const localTime = instant.toZonedDateTimeISO("Asia/Seoul");
기간(Duration)과 날짜 비교
// Duration 생성
const duration = Temporal.Duration.from({ hours: 2, minutes: 30 });
const isoDuration = Temporal.Duration.from("P1Y2M3DT4H5M");
// 두 날짜 사이의 기간 계산
const start = Temporal.PlainDate.from("2026-01-01");
const end = Temporal.PlainDate.from("2026-04-05");
const diff = start.until(end);
console.log(diff.toString()); // "P94D" (94일)
// 더 큰 단위로 표현
const diffLarge = start.until(end, { largestUnit: "month" });
console.log(diffLarge.toString()); // "P3M4D" (3개월 4일)
// 날짜 비교
const dateA = Temporal.PlainDate.from("2026-04-01");
const dateB = Temporal.PlainDate.from("2026-04-05");
console.log(Temporal.PlainDate.compare(dateA, dateB)); // -1
console.log(dateA.equals(dateB)); // false
// since: until의 반대 방향
const sinceStart = end.since(start);
console.log(sinceStart.toString()); // "P94D"
실전 활용 패턴
예약 시스템에서 영업일 계산
function addBusinessDays(startDate, days) {
let current = startDate;
let added = 0;
while (added < days) {
current = current.add({ days: 1 });
// 토요일(6), 일요일(7) 제외
if (current.dayOfWeek <= 5) {
added++;
}
}
return current;
}
const orderDate = Temporal.PlainDate.from("2026-04-05");
const deliveryDate = addBusinessDays(orderDate, 5);
console.log(deliveryDate.toString()); // "2026-04-14" (주말 제외)
반복 일정 생성
function* generateRecurring(start, interval, count) {
let current = start;
for (let i = 0; i < count; i++) {
yield current;
current = current.add(interval);
}
}
// 매주 월요일 회의
const firstMonday = Temporal.PlainDate.from("2026-04-06");
const meetings = [...generateRecurring(
firstMonday,
{ weeks: 1 },
12
)];
meetings.forEach(date => {
console.log(`${date.toString()} (${date.dayOfWeek === 1 ? '월' : '?'})`);
});
글로벌 서비스 시간 표시
function formatMeetingTime(isoString, userTimeZone) {
const meeting = Temporal.Instant.from(isoString);
const local = meeting.toZonedDateTimeISO(userTimeZone);
return {
date: local.toPlainDate().toString(),
time: local.toPlainTime().toString({ smallestUnit: "minute" }),
timeZone: userTimeZone,
offset: local.offset
};
}
const meetingUTC = "2026-04-06T10:00:00Z";
console.log(formatMeetingTime(meetingUTC, "Asia/Seoul"));
// { date: "2026-04-06", time: "19:00", timeZone: "Asia/Seoul", offset: "+09:00" }
console.log(formatMeetingTime(meetingUTC, "America/New_York"));
// { date: "2026-04-06", time: "06:00", timeZone: "America/New_York", offset: "-04:00" }
Date에서 Temporal로 마이그레이션
// 기존 Date 객체를 Temporal로 변환
const legacyDate = new Date("2026-04-05T14:30:00Z");
const instant = Temporal.Instant.fromEpochMilliseconds(legacyDate.getTime());
const zoned = instant.toZonedDateTimeISO("Asia/Seoul");
// Temporal을 Date로 변환 (레거시 호환)
const temporal = Temporal.Instant.from("2026-04-05T05:30:00Z");
const backToDate = new Date(Number(temporal.epochMilliseconds));
브라우저 및 런타임 지원 현황
2026년 4월 기준, Chrome 126+, Firefox 130+, Safari 19+에서 Temporal API를 기본 지원한다. Node.js는 22.x LTS부터 플래그 없이 사용 가능하다. 아직 구 버전 브라우저를 지원해야 한다면 @js-temporal/polyfill 패키지를 활용할 수 있다.