← 글 목록

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는 더 정교한 규칙이 있다. 비즈니스 주차는 라이브러리 권장.