Date는 사라지고 Temporal이 온다
(piccalil.li)- JavaScript의 기존 Date 객체의 불완전성과 비일관성을 지적하며, 이를 대체할 Temporal API의 등장을 소개
- Date는 가변 객체(mutable object) 로 동작해 실제 날짜 개념과 어긋나며, 파싱 오류·시간대 처리 한계 등 구조적 문제가 있음
- Temporal은 불변성 기반의 새로운 날짜·시간 처리 모델을 제공하며,
PlainDate,ZonedDateTime,Duration등 세분화된 클래스 포함 - Temporal의 메서드는 기존 객체를 수정하지 않고 새로운 객체를 반환, 명확하고 안전한 체이닝 연산 가능
- Temporal은 현재 표준화 단계 3(Stage 3) 에 있으며, Chrome과 Firefox 등 최신 브라우저에서 실험적으로 지원 중
JavaScript의 Date 객체 문제점
-
Date생성자는 불일치한 파싱 규칙과 비직관적 인덱싱으로 혼란을 초래- 예: 월(month)은 0부터 시작하지만, 일(day)과 연도(year)는 1부터 시작
- 문자열
"99"는 1999년으로,"100"은 0100년으로 해석되는 등 일관성 결여
-
Date는 시간(time) 을 중심으로 설계되어 있으며, 내부적으로 Unix 타임스탬프(밀리초 단위) 로 저장 - 시간대(time zone) 지원이 제한적이며, 서머타임(DST) 이나 비그레고리력을 인식하지 못함
- 이러한 한계로 인해 Moment.js, date-fns 등 대형 서드파티 라이브러리 의존이 일반적이며, 이는 성능 저하로 이어짐
불변성과 참조 개념의 충돌
- JavaScript의 원시값(primitive) 은 불변이며 값 자체로 저장되지만, 객체(object) 는 참조(reference)로 저장되어 변경 가능
-
Date는 생성자(constructor) 를 통해 만들어지는 객체이므로 가변적- 예:
setMonth()나setDate()호출 시 원본 객체가 직접 변경됨
- 예:
- 이로 인해 동일한 객체를 참조하는 변수 간에 예상치 못한 값 변경이 발생
- 예:
today를 인자로 전달한 함수가 내부에서 날짜를 수정하면, 원본today도 변경됨
- 예:
Temporal: 새로운 날짜·시간 API
-
Temporal은 생성자가 아닌 네임스페이스 객체(namespace object) 로,Math와 유사한 구조- 주요 구성:
PlainDate,PlainDateTime,PlainTime,ZonedDateTime,Duration,Now등
- 주요 구성:
-
Temporal.Now는 현재 시점의 다양한 형태를 반환-
plainDateISO()→ ISO 형식의 날짜 -
zonedDateTimeISO()→ 시간대 포함 시각
-
- Temporal 객체는 명확한 메서드 체계를 제공
-
add({ days: 1 }),subtract({ years: 2 })등으로 명시적 단위 연산 수행 - 기존 객체를 수정하지 않고 새로운 객체를 반환, 불변성 유지
-
Temporal의 동작 방식과 장점
- Temporal 객체는 여전히 객체형이지만, 의도된 불변적 사용 패턴을 따름
- 예:
today.add({ days: 1 })는 새로운 날짜 객체를 반환하며, 원본today는 변경되지 않음
- 예:
-
Date대비 간결하고 명확한 구문 제공- 예:
const today = Temporal.Now.plainDateISO(); console.log(`Tomorrow will be ${ today.add({ days: 1 }) }. Today is ${ today }.`); // 결과: Tomorrow will be 2026-01-01. Today is 2025-12-31.
- 예:
- 시간대 지정, 기간 계산, ISO 포맷 유지 등 현대적 요구에 부합
-
add,subtract,since,until등 메서드 체이닝으로 복잡한 날짜 계산을 간결하게 표현 가능
표준화 현황과 향후 전망
-
Temporal은 ECMAScript 제안 단계 3(Stage 3) 에 도달, 브라우저 구현이 권장되는 상태 - Chrome과 Firefox에서 이미 실험적 지원이 시작되었으며, 다른 브라우저도 도입 예정
- 개발자들은 현재부터 테스트 및 피드백 제공을 통해 사양 개선에 참여 가능
-
Date는 여전히 존재하겠지만, 향후에는 Temporal이 기본 날짜 처리 방식으로 자리잡을 전망 - 글은 “1995년에 교체했어야 했지만, 지금이라도 Temporal.Now가 최적의 시점”이라며 마무리
Hacker News 의견들
-
이 글은 JavaScript의 Date 생성자가 가진 여러 황당한 동작을 다루고 있음
특히'YYYY-MM-DD'형식이 UTC 자정으로 해석되어, 로컬 시간대에서는 하루가 어긋나는 문제를 설명함
원래 ISO 8601에서는 시간대 지정이 없으면 로컬 시간으로 간주해야 하지만, ES5 스펙 작성 중 실수로 “Z”(UTC)로 처리되었음
이후 ES2015에서 수정하려 했으나, 수많은 웹사이트가 기존의 잘못된 동작에 의존하고 있어 웹 호환성을 이유로 되돌려졌음
자세한 내용은 Broken Parser 섹션 참고-
'use strict'처럼'strict datetime'지시어가 있었다면 좋았을 것 같음
이렇게 하면 기존 코드와의 비호환성 문제 없이 올바른 동작을 선택적으로 적용할 수 있었을 것임
혹은import Date from 'browser:date'처럼 내부 모듈로 수정된 전역 객체를 불러오는 방식도 가능했을 것임 - 나도 예전에 문자열을 직접 분리해 시간대 없는 날짜 객체를 만들었음
생일처럼 단순히 날짜만 의미하는 값이 시간대 때문에 바뀌는 건 말이 안 됨
예전에 Outlook이 생일을 시간대 포함으로 저장해서, 나라를 옮길 때마다 생일이 하루씩 밀렸던 기억이 있음 - “웹 호환성의 제단에 바쳐졌다”는 표현이 인상적임
하지만 대안이 있었을까? 예전 IE5 시절처럼 브라우저 버전별 분기 처리를 강제하는 건 더 나빴을 것 같음 - jsdate.wtf 사이트를 보면 JS Date의 기괴한 동작들을 직접 체험할 수 있음
- 이런 문제들이 수많은 작은 버그의 근원이 되는 걸 생각하면 웃기면서도 슬픈 일임
-
-
Rails와 Ruby의 시간 처리 방식을 정말 부러워함
Time.current.in_time_zone('America/Los_Angeles') + 3.days - 4.months + 1.hour같은 API는 직관적이고 강력함
Ruby는 Time 객체를 하나의 일관된 객체로 오버로드해서, 변환이나 캐스팅 고민이 거의 없음
JS에서도new Date().add({ days: 1 })처럼 간단히 쓸 수 있다면 얼마나 좋을까 생각함- 하지만 “3.days - 4.months + 1.hour” 같은 문법이 좋은가에 대해서는 의문을 제기함
또한 코어 라이브러리를 오버로드하는 게 정말 좋은 접근인지도 논쟁의 여지가 있음
- 하지만 “3.days - 4.months + 1.hour” 같은 문법이 좋은가에 대해서는 의문을 제기함
-
Safari가 아직 Temporal API를 지원하지 않는 게 아쉬움
내년쯤에는 지원되길 기대함 -
JavaScript의 Date가 가진 문제는 많지만, 객체라는 점 자체는 큰 문제가 아니라고 생각함
불변 객체였으면 좋았겠지만, 변경 가능한 객체가 변경된다고 놀랄 일은 아님- 하지만 다른 코드가 내가 들고 있던 Date 객체를 암묵적으로 변경할 수 있다는 점이 문제임
가변성의 진짜 위험은 로컬이 아닌 비지역적 변경에서 발생함
- 하지만 다른 코드가 내가 들고 있던 Date 객체를 암묵적으로 변경할 수 있다는 점이 문제임
-
Temporal API가 윤초(leap second) 정보를 전혀 다루지 않는 게 불편함
천문 계산용 JS 도구를 만들고 싶은데, UTC 변환에는 윤초 데이터가 필요함
temporal-tai같은 우회책은 있지만, 클라이언트 측에서 윤초 파일을 유지해야 해서 번거로움
SOP(CORS 정책) 때문에 외부 사이트에서 파일을 바로 가져올 수도 없음
브라우저는 주기적으로 업데이트되는데, 왜 윤초 정보를 내장하지 않는지 의문임- SOP가 윤초 파일을 막는다는 건 오해임
서버에서Access-Control-Allow-Origin헤더를 설정하거나 JS 파일 형태로 제공하면 가능함
다만 브라우저가 자체적으로 윤초 데이터를 포함하고 유지하는 건 비용이 큰 작업일 수 있음 - “UTC만 다룬다”는 표현은 부정확함
UTC는 본래 윤초를 포함하는 시간 척도이므로, 실제로는 POSIX 시간만 다루고 있다고 보는 게 맞음
- SOP가 윤초 파일을 막는다는 건 오해임
-
코드 예시에서
"33"이 아니라"50"부터 1900년대로 처리된다고 해야 함 — 단순한 오타 지적임 -
Temporal polyfill을 사용 중인데, 지금까지 아주 만족스러움
- 다만 크기가 51KB라서 가벼운 편은 아님 (bundlephobia 링크)
서버나 대형 앱에는 괜찮지만, 작은 앱에는 부담이 될 수 있음 - moment나 luxon과 비교했을 때 어떤지 궁금하다는 질문도 나옴
- 다만 크기가 51KB라서 가벼운 편은 아님 (bundlephobia 링크)
-
글이 이상하게도
Date.now()를 전혀 언급하지 않음
Temporal과 비교하려면Date.now()를 기준으로 설명했어야 함
이 함수는 1970년 1월 1일부터의 밀리초 단위 경과 시간을 반환함
Temporal이 더 친절한 API이긴 하지만, 근본적으로는 시간의 상대적 거리를 표현하는 것이 목적임- 하지만 글에서 언급된 타임스탬프 관련 버그도 존재함
그렇다면 Date를 거치지 않고 원하는 형식으로 변환하는 방법은 무엇인지 궁금함
- 하지만 글에서 언급된 타임스탬프 관련 버그도 존재함
-
“daylight savings time”이 아니라 “daylight saving time”이 맞다는 사소하지만 중요한 교정을 남김
-
지금까지 JS Date가 이렇게 엉망인 줄 몰랐음
- 더 안타까운 건, 이런 문제들이 1995년부터 이미 명확히 보였다는 점임
그때 조금만 더 신중했다면 수많은 개발자가 이런 함정에 빠지지 않았을 것임
- 더 안타까운 건, 이런 문제들이 1995년부터 이미 명확히 보였다는 점임