notes
30장. Date
모던 JS Deep Dive — Date 객체·시간 계산과 날짜 처리 핵심 정리.
참고: 이웅모 《모던 자바스크립트 Deep Dive》 30장 · [MDN Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) · [MDN Temporal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal)
Date는 만능처럼 보이지만 파싱·타임존·변이가 지뢰다. 내부는 UTC 밀리초 타임스탬프. 표시할 때 로컬/UTC를 섞으면 “하루 밀림”이 난다. 가능하면 라이브러리(Temporal 노트·luxon·dayjs)와 역할을 나눈다.
flowchart LR
TS[ms since epoch UTC] --> DATE[Date 인스턴스]
DATE --> LOCAL[getHours 등 로컬]
DATE --> UTC[getUTCHours 등]
PARSE[Date.parse / new Date string] --> TS
생성
new Date(); // 지금
new Date(0); // epoch
new Date(2026, 8, 26); // 월은 0–11 — 9월=8
new Date('2026-09-26T00:00:00Z');
Date.now(); // 숫자 타임스탬프
Date.parse('2026-09-26'); // 구현·형식 의존 주의
월 0-인덱스와 문자열 파싱 차이가 최대 함정.
getter / setter
로컬: getFullYear, getMonth, getDate, getDay, getHours…
UTC: getUTC* 짝.
setter도 유사. 변이 객체라 공유 Date를 함부로 넘기지 말 것.
const d = new Date('2026-09-26T12:00:00Z');
console.log(d.getUTCHours(), d.getHours()); // 로컬 오프셋에 따라 다름
출력
d.toISOString(); // UTC ISO
d.toLocaleString('ko-KR', { timeZone: 'Asia/Seoul' });
d.toDateString();
서버·로그는 ISO/UTC, UI는 locale+timeZone 명시.
연산
const a = Date.now();
const b = a + 24 * 60 * 60 * 1000; // 하루 ms — DST에선 예외 가능
const diff = new Date(b) - new Date(a); // ms
일자 계산은 UTC noon 고정·날짜만 다루는 API·Temporal PlainDate가 안전하다.
예제
function formatKST(ts) {
return new Intl.DateTimeFormat('ko-KR', {
timeZone: 'Asia/Seoul',
dateStyle: 'medium',
timeStyle: 'short',
}).format(new Date(ts));
}
function startOfDayUTC(date) {
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
}
// 잘못된 파싱 의존
// new Date('2026-09-26') — 환경에 따라 UTC 자정 또는 로컬
// 명시 타임존 문자열 또는 컴포넌트 생성자 권장
헷갈리던 장면 / 함정
- 월 0–11
- 문자열 파싱 구현차
- getHours vs getUTCHours 혼용
- Date 변이로 인한 공유 버그
- DST 기간 “24시간 = 하루” 가정
- JSON.stringify(Date) → ISO 문자열 — 복원 시 new Date
표
질문 → 자답
Q. Date를 버려야 하나? A. 런타임에 남아 있다. 새 코드는 Temporal(가능할 때)·검증된 라이브러리. Date는 경계 변환용으로.
Q. 왜 Temporal 노트와 병행? A. Date의 실패 모드를 Temporal이 타입으로 나눈다(Instant/PlainDate/ZonedDateTime).
웹 보강 메모
MDN Date와 Temporal 대비표. 앱 전역 Asia/Seoul 포맷 헬퍼를 한곳에 모았다.
실무로 바로
- API: ISO UTC 문자열
- 표시: timeZone 명시
- 월: 주석 또는 라이브러리
- 불변: 복사 후 설정 / 라이브러리
타임존을 명시한 포맷터 팩토리
function makeFormatter(timeZone, locale = 'ko-KR') {
const fmt = new Intl.DateTimeFormat(locale, {
timeZone,
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit',
hourCycle: 'h23',
});
return (input) => fmt.format(input instanceof Date ? input : new Date(input));
}
const seoul = makeFormatter('Asia/Seoul');
console.log(seoul(Date.now()));
날짜만 비교
function ymdUTC(d) {
return [d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()];
}
function sameUTCDate(a, b) {
const [y1, m1, d1] = ymdUTC(a);
const [y2, m2, d2] = ymdUTC(b);
return y1 === y2 && m1 === m2 && d1 === d2;
}
로컬 자정 비교는 DST에서 깨지기 쉬움.
스케줄 슬롯
회의 길이 계산은 UTC ms 차이. “달력 날짜 +1”은 PlainDate 감각으로 별도 함수.
function addDaysUTC(date, days) {
const x = new Date(date.getTime());
x.setUTCDate(x.getUTCDate() + days);
return x;
}
여기서 잠깐 💁♂️
정규식·문자열(31–32)과 시간 포맷이 자주 만난다. Date 문자열을 정규식으로 파싱하기보다 ISO·라이브러리.
보강 — 유효 검사
function isValidDate(d) {
return d instanceof Date && !Number.isNaN(d.getTime());
}
console.log(isValidDate(new Date('nope'))); // false
보강 — 타이머와 시계
Date.now는 벽시계. 모노토닉 간격은 performance.now. 측정·애니메이션은 performance, 절대 시각은 Date/Temporal.
실무 체크리스트 (Date)
- [ ] 저장 UTC ISO 또는 epoch
- [ ] UI timeZone 명시 (Asia/Seoul)
- [ ] 월 0-인덱스 주석/헬퍼
- [ ] 문자열 파싱 최소화
- [ ] Date 변이 공유 금지
- [ ] 유효성
!Number.isNaN(d.getTime())
예제 — 상대 시간 粗
function relativeKo(ts, now = Date.now()) {
const diff = Math.round((ts - now) / 1000);
const rtf = new Intl.RelativeTimeFormat('ko', { numeric: 'auto' });
const abs = Math.abs(diff);
if (abs < 60) return rtf.format(diff, 'second');
const min = Math.round(diff / 60);
if (Math.abs(min) < 60) return rtf.format(min, 'minute');
const hour = Math.round(diff / 3600);
if (Math.abs(hour) < 48) return rtf.format(hour, 'hour');
const day = Math.round(diff / 86400);
return rtf.format(day, 'day');
}
오늘 지운 미신
“new Date(문자열)은 표준 하나로 동작” → 구현차. “getMonth()가 1–12” → 0–11. “로컬 자정 +24h = 다음날” → DST.
추가 예제 — 주차 계산 粗
function weekKeyUTC(d) {
const x = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()));
const day = x.getUTCDay() || 7;
x.setUTCDate(x.getUTCDate() + 4 - day);
const yearStart = new Date(Date.UTC(x.getUTCFullYear(), 0, 1));
const week = Math.ceil(((x - yearStart) / 86400000 + 1) / 7);
return `${x.getUTCFullYear()}-W${String(week).padStart(2, '0')}`;
}
ISO week는 더 정교한 규칙이 있다. 비즈니스 주차는 라이브러리 권장.