Date 객체의 함정 — 타임존, 파싱, Temporal API 소개
JavaScript의
Date객체는 1995년에 Java에서 그대로 복사해 온 것으로, 수많은 함정이 있습니다. 공부하다 보니 "이게 왜 이렇게 동작하지?" 싶은 순간이 정말 많았습니다.
Date의 대표적인 함정들
월(month)이 0부터 시작
// 2026년 3월 28일을 만들려면?
const date = new Date(2026, 2, 28); // 2가 3월!
console.log(date.getMonth()); // 2 — 3월인데 2를 반환
// 0: 1월, 1: 2월, ..., 11: 12월
const months = [
"1월", "2월", "3월", "4월", "5월", "6월",
"7월", "8월", "9월", "10월", "11월", "12월",
];
console.log(months[date.getMonth()]); // "3월"
문자열 파싱의 비일관성
// ISO 8601 형식 — UTC로 파싱
new Date("2026-03-28");
// → 2026-03-28T00:00:00.000Z (UTC)
// 슬래시 형식 — 로컬 시간으로 파싱
new Date("2026/03/28");
// → 2026-03-28T00:00:00.000+09:00 (KST)
// 하이픈 vs 슬래시로 다른 결과가 나옴!
const d1 = new Date("2026-03-28");
const d2 = new Date("2026/03/28");
console.log(d1.getTime() === d2.getTime()); // false (한국 기준)
이 차이 때문에 날짜 문자열 파싱은 항상 명시적으로 하는 것이 안전합니다.
잘못된 날짜도 허용
// 2월 30일? → 자동으로 3월로 넘어감
const invalid = new Date(2026, 1, 30);
console.log(invalid); // 2026-03-02 — 에러 없이 보정됨
// 이것을 역이용하면 해당 월의 마지막 날을 구할 수 있음
function getLastDay(year, month) {
return new Date(year, month, 0).getDate();
}
console.log(getLastDay(2026, 2)); // 28 (2월)
console.log(getLastDay(2024, 2)); // 29 (윤년)
타임존 문제
const now = new Date();
// 로컬 시간
console.log(now.toString());
// "Sat Mar 28 2026 15:30:00 GMT+0900 (한국 표준시)"
// UTC 시간
console.log(now.toISOString());
// "2026-03-28T06:30:00.000Z"
// getHours vs getUTCHours
console.log(now.getHours()); // 15 (로컬)
console.log(now.getUTCHours()); // 6 (UTC)
타임존 변환
// toLocaleString으로 다른 타임존 표시
const now = new Date();
console.log(now.toLocaleString("ko-KR", { timeZone: "Asia/Seoul" }));
// "2026. 3. 28. 오후 3:30:00"
console.log(now.toLocaleString("ko-KR", { timeZone: "America/New_York" }));
// "2026. 3. 28. 오전 1:30:00"
주요 메서드 정리
const date = new Date(2026, 2, 28, 15, 30, 0);
// Getter
date.getFullYear(); // 2026
date.getMonth(); // 2 (3월)
date.getDate(); // 28
date.getDay(); // 6 (토요일, 0=일요일)
date.getHours(); // 15
date.getMinutes(); // 30
date.getTime(); // 밀리초 타임스탬프
// Setter
date.setFullYear(2027);
date.setMonth(0); // 1월로 변경
// 비교
const d1 = new Date("2026-01-01");
const d2 = new Date("2026-12-31");
console.log(d1 < d2); // true
console.log(d1.getTime() - d2.getTime()); // 밀리초 차이
날짜 계산 패턴
// 날짜 차이 (일수)
function daysBetween(d1, d2) {
const msPerDay = 24 * 60 * 60 * 1000;
return Math.round((d2 - d1) / msPerDay);
}
const start = new Date("2026-01-01");
const end = new Date("2026-03-28");
console.log(daysBetween(start, end)); // 86
// N일 후
function addDays(date, days) {
const result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
console.log(addDays(new Date("2026-03-28"), 7));
// 2026-04-04
날짜 포맷팅
const date = new Date("2026-03-28T15:30:00");
// toLocaleDateString
date.toLocaleDateString("ko-KR"); // "2026. 3. 28."
// 옵션으로 세밀한 제어
date.toLocaleDateString("ko-KR", {
year: "numeric",
month: "long",
day: "numeric",
weekday: "long",
}); // "2026년 3월 28일 토요일"
// Intl.DateTimeFormat (같은 기능, 재사용 가능)
const formatter = new Intl.DateTimeFormat("ko-KR", {
dateStyle: "full",
timeStyle: "short",
});
formatter.format(date); // "2026년 3월 28일 토요일 오후 3:30"
Temporal API — Date의 후계자
Temporal API는 Date의 모든 문제를 해결하기 위해 설계된 새 표준입니다.
// Temporal.PlainDate — 타임존 없는 날짜
const date = Temporal.PlainDate.from("2026-03-28");
console.log(date.month); // 3 — 1부터 시작!
console.log(date.dayOfWeek); // 6 (토요일, 1=월요일)
// 날짜 계산이 직관적
const nextWeek = date.add({ days: 7 });
console.log(nextWeek.toString()); // "2026-04-04"
// 차이 계산
const diff = date.until(nextWeek);
console.log(diff.days); // 7
// Temporal.ZonedDateTime — 타임존 포함
const zdt = Temporal.ZonedDateTime.from({
timeZone: "Asia/Seoul",
year: 2026,
month: 3,
day: 28,
hour: 15,
minute: 30,
});
// Temporal.Instant — 정확한 시점 (UTC)
const instant = Temporal.Now.instant();
Date vs Temporal 비교
| 기능 | Date | Temporal |
|---|---|---|
| 월 인덱스 | 0부터 (함정) | 1부터 (직관적) |
| 불변성 | 가변 | 불변 |
| 타임존 | 로컬 + UTC만 | 모든 타임존 |
| 날짜만 | 불가 (시간 포함) | PlainDate |
| 시간만 | 불가 | PlainTime |
| 파싱 | 비일관적 | 엄격하고 일관적 |
실무에서의 선택
// 현재 — date-fns 또는 dayjs 라이브러리 사용 권장
import { format, addDays, differenceInDays } from "date-fns";
format(new Date(), "yyyy-MM-dd"); // "2026-03-28"
addDays(new Date(), 7);
differenceInDays(new Date("2026-12-31"), new Date());
// 미래 — Temporal API가 브라우저에 안착하면 라이브러리 불필요
**기억하기 **: Date의 월은 0부터 시작하고, 문자열 파싱은 형식에 따라 타임존 해석이 달라집니다. 실무에서는 date-fns 같은 라이브러리를 쓰되, Temporal API가 브라우저에 안착하면 전환을 준비하면 됩니다.
댓글 로딩 중...