JavaScript Temporal이 옵니다
(developer.mozilla.org)- JavaScript의 새 Temporal 객체가 브라우저 실험 릴리스에 들어가기 시작하며, 날짜·시간·기간·캘린더 처리를 내장 API로 더 정확하고 일관되게 다루려는 변화임
- 기존
Date는 사용자 로컬 시간과 UTC 중심 구조, 불안정한 파싱, mutable 동작 때문에 시간대 처리와 디버깅이 어려웠음 - Temporal은
Temporal.Instant,Temporal.ZonedDateTime,Temporal.PlainDateTime,Temporal.Duration,Temporal.Now등으로 시점·지역 시간·기간을 분리하고 200개 이상의 유틸리티 메서드를 제공함 - 안정적인 크로스 브라우저 지원까지는 시간이 더 필요하며, Firefox Nightly에서는
javascript.options.experimental.temporal설정 뒤에서 Temporal을 시험할 수 있음 - MDN에는 이번 주 270페이지 이상의 Temporal 문서가 추가됐고,
@js-temporal/polyfill이 있는 TC39 문서 페이지에서 플래그 변경 없이 예제를 실행할 수 있음
JavaScript Date의 오래된 한계
- JavaScript가 1995년에 만들어질 때
Date객체는 Java의 초기java.util.Date구현을 복사했으며, Java는 1997년에 이를 교체했지만 JavaScript는 거의 30년 동안 같은 API를 유지해 왔음 - 가장 큰 문제는 시간대 지원 부족과 불안정한 파싱 동작임
- 사용자 로컬 시간과 UTC만 지원함
Date자체가 mutable이라 추적하기 어려운 버그를 만들 수 있음- Daylight Saving Time(DST)과 역사적 캘린더 변경을 넘나드는 계산도 다루기 어려움
- 이런 제약 때문에 많은 개발자가 애플리케이션의 날짜와 시간 처리를 위해 Moment.js나 date-fns 같은 전용 라이브러리에 의존해 왔음
Temporal이 바꾸는 날짜·시간 모델
- Temporal은
Date객체의 전체 대체재로 설계됐으며, 날짜와 시간 관리를 더 신뢰 가능하고 예측 가능하게 만드는 것을 목표로 함 - 시간대와 캘린더 표현을 지원하고, 변환·비교·계산·포매팅을 위한 내장 메서드를 제공함
- API 표면에는 200개 이상의 유틸리티 메서드가 있으며, MDN의 Temporal 문서에 설명과 예제가 추가됐음
Temporal의 주요 타입
- Temporal은 고유한 시점, 지역의 벽시계 시간, 기간을 서로 다른 타입으로 구분함
- 주요 구조는 다음과 같음
Temporal.Duration: 두 시점 사이의 차이Temporal.Instant: 타임스탬프로 표현한 고유 시점Temporal.ZonedDateTime: 시간대가 포함된 날짜·시간Temporal.PlainDateTime: 시간대가 없는 전체 날짜·시간Temporal.PlainDate: 날짜만 표현Temporal.PlainYearMonth: 연도와 월Temporal.PlainMonthDay: 월과 일Temporal.PlainTime: 시간만 표현
Temporal.Now: 현재 시간을 여러 클래스 인스턴스나 특정 형식으로 얻는 API
예제로 보는 사용 방식
- 현재 날짜와 시간을 ISO 문자열로 얻을 수 있고, 여러 메서드에 시간대를 전달할 수 있음
Temporal.Now.plainDateTimeISO()는 시스템 시간대의 현재 날짜·시간을 반환함Temporal.Now.plainDateTimeISO("America/New_York")는 New York 시간대의 현재 날짜·시간을 반환함
- Gregorian 외의 캘린더도 다룰 수 있어 Hebrew, Chinese, Islamic 같은 캘린더 체계의 날짜를 만들 수 있음
- Chinese 캘린더의
M011일을 사용해 다음 Chinese New Year를 계산함 - 작성 시점의 예제 출력에서 다음 Chinese New Year는
1/29/2025임
- Chinese 캘린더의
- Unix Epoch 밀리초 타임스탬프를
Temporal.Instant.fromEpochMilliseconds()로 시점으로 만들고,Temporal.Now.instant()와 비교해 남은 시간을 계산할 수 있음- 예제 타임스탬프
1851222399924에 대해smallestUnit: "hour"로 현재부터의 시간을 계산함 @js-temporal/polyfill출력은31,600 hr, Firefox Nightly 출력은PT31600H임
- 예제 타임스탬프
- Firefox 구현의
toLocaleString은 현재 로케일 민감 문자열을 출력하지 않음- 이 동작은 기술적 한계라기보다 설계 결정에 가까우며, duration 포매팅 자체는 가능함
- polyfill과 Firefox 구현은 나중에 수렴할 수 있음
compare()메서드는 배열 정렬에 활용 가능함durations.sort(Temporal.Duration.compare)를 쓰면PT1H,PT1H30M,PT1H45M,PT2H순서로 정렬됨
브라우저 지원과 시험 방법
- Temporal 지원은 실험 브라우저 릴리스에 천천히 포함되기 시작했으며, 현재 Firefox 구현이 가장 성숙해 보임
- Firefox에서는 Nightly 버전에
javascript.options.experimental.temporal설정 뒤에서 Temporal이 빌드되고 있음 - 구현 추적용 주요 브라우저 이슈는 다음과 같음
- Firefox: Build temporal in Nightly by default
- Safari: [JSC] Implement Temporal
- Chrome: Implement the Temporal proposal
- 전체 호환성 현황은 MDN의 Temporal object Browser Compatibility section에서 확인할 수 있음
- TC39 Temporal 문서에는
@js-temporal/polyfill이 있어, 어떤 브라우저에서도 플래그나 설정 변경 없이 개발자 도구 콘솔에서 예제를 실행할 수 있음
댓글과 토론
Hacker News 의견들
-
Temporal은 훌륭함. 한동안 polyfill로 프로덕션에서 써왔고, 기존
Date()API에서 겪었던 많은 문제를 해결해 줌
Rust의chrono, Java의 Joda Time 같은 좋은 시간 라이브러리에서 영감을 받아 쓰기 편한 API로 잘 합친 느낌임
단순 시간, 시점, 시간대 포함 시간을 분리해서 조금 더 복잡하긴 하지만, 시간 자체가 복잡하므로 개발자가 그 복잡성을 마주해야 함
“이 타임스탬프에 하루를 더한다” 같은 작업도 특정 시간대 기준인지 반드시 결정해야 하며, 그렇지 않으면 일광 절약 시간제나 사용자의 시간대 변경, 서버 시간대 차이 때문에 버그가 남
고정 오프셋 타임스탬프(2025-01-01T00:00+02:00)와 특정 시간대(Europe/Paris)의 직렬화 차이도 해결해 줌- Rust 크레이트인 Jiff도 볼 만함. Temporal에서 영감을 받았고,
chrono를 포함한 다른 날짜·시간 크레이트와의 비교도 정리해 둠: https://docs.rs/jiff/latest/jiff/_documentation/comparison/i...
개인적으로 Temporal이 Rust의chrono에서 크게 영감을 받았다고 보긴 어렵고, Joda나 요즘의java.time, 그리고 date-fns나 Moment.js 같은 JavaScript 날짜·시간 라이브러리 쪽 영향이 더 커 보임 - 어느 정도 나이가 있는 사람들은 프로덕션 환경에서 현지 시간 01:00~03:00 사이에 cron 작업을 예약하지 말라는 걸 오래전에 배웠음
- 맞는 말이지만 한 가지 걸림돌이 있음. 많은 사람이 실제 위치를 가리키는 시간대 이름을 쓰는 걸 싫어함
Europe/Paris가 프랑스 넓은 지역인지 파리만 뜻하는지 찾아보려 하지 않고, 자신이 파리에 산다고 말하는 것처럼 느껴져 짜증을 냄
온라인 커뮤니티 이벤트 같은 걸 예약할 때 이런 점이 고통스러웠음. 사람들은 고정 오프셋을 쓰고 싶어 하고,CET/CEST같은 표현은 괜찮아하지만Europe/yourexactcoordinates같은 건 싫어함
실제로 대부분의 시간대가 꽤 넓은 지역이라는 건 알지만, 프라이버시를 신경 쓰면서도 “시간”과 “시간대” 차이를 찾아볼 생각은 없는 사람이 많음 chrono와 Joda Time은 서로 다른 시대의 물건임. Rust 1.0이 나왔을 때 Java는 이미 표준 라이브러리의 java.time으로 이 접근을 구현했고, 이 용도로 서드파티 라이브러리가 필요 없었음- 고정 오프셋 타임스탬프와 특정 시간대 타임스탬프의 직렬화 차이가 어떤 문제인지 더 자세히 알고 싶음
- Rust 크레이트인 Jiff도 볼 만함. Temporal에서 영감을 받았고,
-
문서에서 찾는 데 시간이 꽤 걸렸는데, Temporal.ZonedDateTime.prototype.withTimeZone() 이 한 시간대에서 다른 시간대로 변환해 줌: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
Temporal.ZonedDateTime.from("2021-08-01T12:00[America/New_York]")로 만든 값을withTimeZone("Europe/Paris")에 넣으면2021-08-01T18:00:00+02:00[Europe/Paris]가 됨
시간대 변환과 기간 처리는 라이브러리가 반드시 다뤄야 하는 핵심 기능인데 Temporal이 둘 다 처리해서 좋음. 다만Temporal.Duration에는duration.format('H\hmm\m')처럼1h03m으로 만드는 사용자 지정format함수가 없어 보임- DurationFormatter 제안은 어떤 단위를 어떤 축약 수준으로 포맷할지 제어할 수 있게 해 줌. 예를 들어
1 yr,3 hours,3m같은 식임: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
icu4x 라이브러리에 일부 구현해 둔 적이 있음
- DurationFormatter 제안은 어떤 단위를 어떤 축약 수준으로 포맷할지 제어할 수 있게 해 줌. 예를 들어
-
지금 TC39에는 익어가는 제안이 꽤 많고, Temporal은 빨리 출시됐으면 하는 것 중 하나임
주요 브라우저가 새 기능을 지원하려면 v8, JSC, SpiderMonkey 엔지니어들의 개발 노력이 필요하다는 건 알기 때문에, 진전을 만드는 사람들에게 정말 고마움
조급한 입장에서는 “Records와 Tuples도 얼른 해 줘! Pipe 문법 전쟁은 건너뛰어도 돼!”라고 응원하게 됨- Records와 Tuples는 출시될 가능성이 낮거나, 적어도 사람들이 원하는 동등성 의미론으로 출시될 가능성은 매우 낮아 보임
브라우저 벤더들이 이 시점에 새 원시 타입을 추가하는 데 이미 꽤 강하게 반대하는 것으로 이해하고 있음. 동등성이 어떻게 동작해야 하는지의 난점은 이 스레드를 보면 설명이 있음: https://github.com/tc39/proposal-record-tuple/issues/387
BigInt가 처음엔 이렇게 많이 쓰일 거라 기대하지 않았지만, 실제로는 구조적 동등성을 가진 컬렉션이 필요할 때가 계속 생겨서 아쉬움
새 원시 타입 추가 반대가 패턴 매칭에도 영향을 주는 것으로 알고 있지만, 그 이유는 잘 모르겠고 해당 제안은 덜 따라가고 있음 - Temporal이 오는 건 좋고 다른 좋은 기능도 많겠지만, 언급한 특정 제안들에 대해서는 낙관하기 어려움
파이프라인, 패턴 매칭, Records+Tuples는 모두 4년 넘게 작업 중이고 아직 1~2단계에 머물러 있음. 지난 1년 동안 의미 있는 진전은 패턴 매칭 정도를 제외하면 거의 없어 보임
Records와 Tuples 저장소의 이슈에 따르면 값 의미론, 즉===가 동작할 수 있는지에 대한 반발이 있었음. 값 의미론을 빼면 제안의 유용성이 크게 줄어듦
이들이 3단계에 도달하려면 최소 1~2년은 더 걸리거나, 아예 못 갈 수도 있음. 그래도 배열 그룹화 함수는 생겼음 - 원하는 건 패턴 매칭임: https://tc39.es/proposal-pattern-matching/
아직 1단계라서 슬프게도 몇 년은 더 걸릴 듯함 - Records라면, Dictionary가 ECMA5 제안이었는지 아니면 AS3의 독특한 기능이었는지 궁금함
AS3의 Dictionary는 객체 참조, 문자열, 숫자를 고유 키로 쓰고 어떤 타입의 값이든 붙일 수 있는 타입이었음. 딕셔너리 객체 자체가 살아 있는 동안에는 약한 참조가 아니도록 가비지 컬렉션도 처리됐음
JavaScript의 Set을 떠올리되, 지정한 어떤 종류의 키든 강한 타입으로 쓰고 컴파일 시점에 오류를 던지는 형태였음. 그 언어가 그리움 - 주요 브라우저 제작자만의 문제가 아님. ES6가 막 나올 즈음 JavaScript 추론 엔진을 만들고 있었고 ES5는 꽤 지원했지만, 문법 업데이트가 몰려오면서 포기해야 했음
그 업데이트들이 사실상 JavaScript 생태계를 망가뜨렸고, 이제는 JS로 컴파일되는 언어만 남은 느낌임. 아직 바닐라 JS를 쓰는 사람은 어셈블리로 앱을 만드는 사람 같음
- Records와 Tuples는 출시될 가능성이 낮거나, 적어도 사람들이 원하는 동등성 의미론으로 출시될 가능성은 매우 낮아 보임
-
Temporal 안에서 각 시간대 데이터가 어떻게 최신으로 유지되는지 궁금함
TC39가 어딘가에서 데이터를 업데이트하면 각 브라우저가 그 데이터를 내부에 복사해 새 버전을 내는 구조인지, 사용자가 새 데이터가 들어간 브라우저로 업데이트하지 않았다면 내 웹사이트에서 잘못된 시간을 보게 되는지 궁금함
예를 들어 멕시코는 2022년에 일광 절약 시간제를 없앴음: https://en.wikipedia.org/wiki/Time_in_Mexico
pytz나moment-timezone같은 서드파티 라이브러리는 서버 쪽 라이브러리를 업데이트하면 되고, 표준 라이브러리에서 처리한다면 언어 버전을 올리면 되는데, Temporal과 방문자의 브라우저에서는 어떻게 되는지 모르겠음- 제안 문서의 “Time Zones and Resolving Ambiguity”를 읽어보면 IANA 시간대 데이터베이스를 사용한다고만 하고 언제 업데이트하는지는 나와 있지 않음: https://tc39.es/proposal-temporal/docs/timezone.html
사용자의 컴퓨터도 이 데이터베이스에 접근할 수 있고 업데이트를 처리할 것 같지만 확실하진 않음. 브라우저가 별도로 이 데이터베이스를 다운로드해 유지할 수도 있고, 구현에 따라 달라질 듯함
관련 데이터베이스: https://en.wikipedia.org/wiki/Tz_database - 브라우저에 내장되는 기능이므로 아마 브라우저 업데이트 때 갱신될 것 같음. 브라우저 벤더들은 빠른 업데이트를 위해 많은 작업을 해왔고, 평균적인 사용자의 컴퓨터에서 브라우저는 가장 최신으로 유지되는 소프트웨어일 가능성이 큼
- 브라우저는 이미
Intl헬퍼 때문에 로캘도 최신으로 유지해야 하므로, 그쪽을 조사하면 비슷한 답을 얻을 가능성이 큼 - Unix 계열 시스템, 즉 Linux 배포판, macOS, Android에는 시스템 차원의 IANA Time Zone Database 사본이 있음. 브라우저가 그걸 쓰는지 자체 사본을 싣는지는 잘 모르지만 Temporal은 확실히 IANA
tzdb를 사용함
사용자가 새 시간대 데이터를 반영하도록 브라우저나 시스템을 업데이트하지 않았다면 잘못된 시간을 볼 수 있다는 건 대체로 맞아 보임. 다만 이건 현재의 일반적인 관행과 크게 다르지 않음
대안은 모든 애플리케이션이 자체tzdb를 싣는 것인데, 일부 앱은 그렇게 하고 정당한 경우도 있음. 하지만 일반적으로는 낭비이고, 애플리케이션 간 불일치를 늘리며, 업데이트 주기도 더 느려질 가능성이 큼 - 구현과 시스템에 따라 달라질 것임. 구현은 결국 시간대 제공자를 정의해야 할 것이고, 이는 엔진 구현이 Temporal에 제공하는 호스트/시스템 훅으로부터 제공될 가능성이 큼
- 제안 문서의 “Time Zones and Resolving Ambiguity”를 읽어보면 IANA 시간대 데이터베이스를 사용한다고만 하고 언제 업데이트하는지는 나와 있지 않음: https://tc39.es/proposal-temporal/docs/timezone.html
-
누군가 시간대를 바꿨을 때 감지하는 이벤트를 추가해야 함
“프로그래머들이 시간에 대해 믿는 거짓” 목록에 “프로그래머는 사용 중 시간대가 고정되어 있다고 믿는다”도 들어갈 수 있음. 현실에서는 매일 수백만 명이 시간대를 이동함- 그런 이벤트는 사람을 지문 추적하고 위치를 추정하는 또 다른 데이터 포인트로 쓰일 것 같음
- 이 이벤트가 어디에 쓰일지 잘 모르겠음. 시간에 어느 정도 의존하는 대부분의 웹 애플리케이션이 영향을 받는 사용자 수를 생각했을 때 직접 이 이벤트를 듣고 반응해야 할 만큼의 가치가 있을지는 의문임
물론 라이브러리에는 도움이 될 수 있음 - 클라이언트의 시간대는 클라이언트에게만 중요해야 함. 타임스탬프를 사용자의 현지 시간으로 보여주는 편의 목적 외에는 클라이언트 시계가 중요해지는 조건이 없음. 사용자가 시간대를 바꿔도 신경 쓸 이유가 없음
- 시간대 변경을 추적하려면 상태가 필요하므로, 개발자가 어떻게 추적하고 반응할지 정의하는 편이 맞아 보임. Temporal이 그 처리 방식에 의견을 갖는 건 원치 않을 수 있음
- 비행기나 ISS에 있으면 아예 시간대가 없는 상태일 수도 있음
-
.Now가 함수 호출이 아니라는 점이 매우 아쉬움. 속성이어서는 안 됨
루프 안에서.Now를 여러 번 평가하면 매번 다른 값이 나올 수 있는데, 이는 예상 밖임
C#도 같은 선택을 했고 그러지 말았어야 했음: https://ericlippert.com/2014/05/19/when-should-i-write-a-pro...- 잘못 이해한 것 같음.
Now는 실제 값을 가져오는 함수들의 네임스페이스이고, 예를 들면Now.instant()같은 식임
- 잘못 이해한 것 같음.
-
Temporal 제안은 정말 마음에 들지만 한 가지가 걸림. 비교에서 참조 동등성에 의존함
즉Temporal.Instant.from('2020-01-01') != Temporal.Instant.from('2020-01-01')가 됨
본질적으로 나쁜 건 아니지만, 이 객체들을Map키로 쓰거나Set에 모으는 능력을 사실상 없애버림. 왜 그런 결정을 했는지는 알지만 가능하지 않다는 게 아쉬움
언젠가 Records와 Tuples가 실제로 나온다면 그 기반 버전이 생길지도 모르겠음- JavaScript의 비원시 타입 중
==에서 참조가 아닌 동등성을 갖는 게 있나? - 상황에 따라서는
.epochMilliseconds를Map/Set키나 원소로 쓸 수 있어 보임. 다만Map에서 시간대 데이터 보존이나Instant추가 연산이 필요하면 값 쪽에 원래Instant를 보존해야 함
Set의 경우Map{v.epochMilliseconds:v}처럼 써서Instant를 보존할 수 있음
훌륭하진 않지만 이건 Temporal보다 JavaScript 자체를 탓해야 할 문제임. 런타임이 처리하게 만들 수도 있겠지만 그러면 폴리필이 실패함 - 설령 된다 해도 이런 값을
Map/Set에 쓰는 건 부동소수점 숫자를Map/Set에 쓰는 것과 비슷해서 보통 나쁜 생각임 - JavaScript에서는 불가능하기 때문 아닌가? Java의 상수 문자열 풀처럼, 사용되는 모든 객체가 전체 객체 풀 안의 값에 대한 참조가 되게 하는 괴상한 해킹을 제외하면 어려움
- 그냥
String()이나'2020-01-01'을 쓰면 되지 않나?
- JavaScript의 비원시 타입 중
-
기존 API와 함께 동작하는 비교 함수를 제공하는 건 탄탄한 설계임. 새 타입을 추가할 때 필독 조건으로 삼아도 될 정도임
예를 들어Temporal.Duration.compare를sort에 넘기면PT1H,PT1H30M,PT1H45M,PT2H순서로 정렬할 수 있음. 설계자들에게 박수를 보냄 -
Temporal이라는 이름이 Moment, Datetime 같은 일반적인 시간 객체와 충돌을 피하려는 의도라는 건 알겠지만, 이름이 별로임
처음 봤을 때는 가비지 컬렉션 제어 같은 건 줄 알았음temporal은 영원성과 대비되는 시간에 관한 것, 문법 시제나 시간 구분에 관한 것, 공간과 구별되는 시간에 관한 것이라는 뜻이 있음: https://www.merriam-webster.com/dictionary/temporal
괜찮은 이름으로 들림- 브라우저 안의 워크플로 엔진 Temporal인 줄 알았음
- 이미 내려진 결정을 두고 자전거 보관소 색깔 논쟁을 하는 셈이지만, 정말 나쁜 이름이라는 데 동의함
우선Temporal은 명사가 아니라 형용사임. 시간과 관련은 있지만 직관적으로 맞아떨어지지 않음
더 중요한 건, 오래된 코드와 충돌할 확률을 낮추려고 이상한 이름을 고르면 시간이 지날수록 언어가 더 불투명해진다는 점임
Promise와Generator를 추가했을 때도 그 객체 이름을 쓰는 라이브러리는 많았음. 예약어와 달리 내장 객체는 덮어쓸 수 있으므로 큰 문제가 아니었음
표준 위원회는 언어의 장기적 건강을 위해 뚝심 있게Time을 써야 한다고 봄
물론 이 논쟁은 이미 지나갔고 브라우저 제작자와 JS 엔진 구현자들이 합의했을 것임 - 나도 끔찍한 이름이라고 봄. 차라리
DateV2가 낫겠음
하위 호환성이 필요하다는 건 알지만Temporal은 어감이 별로임.DateTime은 JS 표준에 있는 이름도 아닌데 왜 그걸 쓰지 않았는지 모르겠음. 그때까지는const DateTime = Temporal로 둘 듯함
-
관련 글들:
JavaScript Temporal Is Coming - https://news.ycombinator.com/item?id=42809834 - 2025년 1월, 댓글 18개
Mozilla: Temporal (Limited Availability) - https://news.ycombinator.com/item?id=42776548 - 2025년 1월, 댓글 1개
Is It Time for the JavaScript Temporal API? - https://news.ycombinator.com/item?id=29712118 - 2021년 12월, 댓글 100개
Temporal: Getting started with JavaScript's new date time API - https://news.ycombinator.com/item?id=27661667 - 2021년 6월, 댓글 194개
JavaScript Temporal - https://news.ycombinator.com/item?id=27395236 - 2021년 6월, 댓글 1개
JavaScript Proposal Temporal for Dates is now stage 3 - https://news.ycombinator.com/item?id=26432346 - 2021년 3월, 댓글 1개- 요즘 Web API들이 정말 훌륭해졌다는 건 고맙지만, 이 주제의 첫 제출이 거의 4년 전인 2021년 3월이었다는 건 여전히 아쉬움
큰 배는 방향을 틀기 느리다는 말이 딱 맞음
- 요즘 Web API들이 정말 훌륭해졌다는 건 고맙지만, 이 주제의 첫 제출이 거의 4년 전인 2021년 3월이었다는 건 여전히 아쉬움