최근 평소에 동경하던 회사 동료분께서 Temporal을 주제로 짧은 테크톡을 진행해 주셨습니다. 이 내용을 완전히 내 것으로 소화하고자, 공유해주신 래퍼런스를 기반으로 복습하는 시간을 가졌습니다. 모자라고 부족한 나를 끌어주는 동료가 있다는 것만큼 행복한 환경은 없는 것 같습니다.
Temporal은 Javascript의 새로운 전역 객체로, 날짜 및 시간 작업을 위한 표준 객체와 함수를 제공합니다. 현재 Stage3이며, 폴리필 없이는 모든 브라우저에서 사용할 수 없습니다. 기존 Javascript의 Date 객체에는 어떤 문제가 있기에 새로운 date/time API가 필요해진 걸까요? 아직은 생소한 Temporal에 대해 알아봅시다.
Referencs
위의 문제점들은 Date 객체 업데이트로 수정이 가능한 문제와, 수정이 불가능한 문제로 나눌 수 있습니다.
const zoneDate = Date.inZone(zoneIdentifier, dateString);
const calendarDate = Date.withCalendar('Hijri', dateString);
const myDate = new Date();
// AS IS
myDate.setDate(myDate.getDate() + 7);
// TO BE
myDate.addDays(7);
T39 맴버들이 지속적으로 주의하는 두개의 중요한 개념이 있습니다.
한마디로 "Dont break the web"으로 해석할 수 있으며, Semver의 "Major Update"는 Javascript에서 일어날 수 없습니다.
기존에 Date 객체를 Mutable하게 사용하던 많은 코드들이 망가집니다.
ECMAScript 표준에 따르면, 타임존(Time Zone)을 생략했을 때 날짜 전용(date-only) 형식은 UTC 타임으로, 시간 전용(time-only) 형식은 로컬 타임으로 추론됩니다.
new Date('2017-04-08').toISOString()
//"2017-04-08T00:00:00.000Z"
new Date('2017-04-08T08:30').toISOString()
//"2017-04-08T15:30:00.000Z"
ISO8601에 따르면 두 경우 전부 로컬 타임으로 추론돼야 합니다. 과거 ES5 시절 생략된 타임존과 값은 모두 "Z"로 결정됐었고, TC39에서 잘못됨을 깨닫고 이를 수정하려 했습니다. 하지만 이전 방식으로 작성된 수많은 코드들에서 버그가 보고됐고, 위원회는 유저들의 피드백을 수용하여 지금과 같이 서로 다른 타임존으로 추론하는 스펙을 정착시켰습니다. 비록 표준 정의에 어긋나고 논리적이지 않더라도, 이게 웹의 "현실성(Reality)"이기 때문입니다.
이 고통스러운 변화 과정은 거대한 커뮤니티를 만들게 되는 계기가 됐고, 이는 파서(Parser)가 변화할 수 없는 이유가 돼버렸습니다.
이 수정 불가능한 두 가지 문제점으로 인해, Temporal이라 불리는 새로운 Javascript Datetime 객체가 구상됐습니다.
Referencs
Temporal은 날짜 전용(date-only), 시간 전용(time-only) 및 기타 용도에 대한 별도 클래스를 제공합니다. 이런 방식은 코드의 가독서을 높일 뿐더러, 알 수 없는 값에 대해 0, UTC, 혹은 로컬 타임존을 잘못 추측하여 발생하는 버그를 예방합니다.
모든 Temporal
타입들은 영속성과 상호운용성을 위한 문자열 표현을 가지고 있습니다.
Temporal API에서 타임존(Time Zone)과 관련되지 않은 객체(Temporal.PlainDate
, Temporal.PlainTime
, 그리고 Temporal.PlainDateTime
)의 이름은 "Plain"으로 시작합니다. 이런 Plain 타입과 정확한 시간 타입(Temporal.Instant
그리고 Temporal.ZonedDateTime
) 사이의 변환 작업은 타임존(Time Zone)과 일광 절약 시간제(DST) 때문에 모호합니다. 개발자들은 Temporal API를 통해 이 모호함이 해결되는 방법을 구현할 수 있습니다.
July 20, 1969, at 20:17 UTC
캘린더나 지역 상관 없이 고정된 시간(혹은 정확한 시간(exact time))을 표현합니다. 사람이 읽을 용이한 현지 달력 날짜(local calendar date) 혹은 시계 시간(clock time)을 만들기 위해서는, Temporal.TimeZone
과 Temporal.Calendar
를 사용해서 Temporal.ZonedDateTime
혹은 Temporal.PlainDateTime
를 만드세요.
const instant = Temporal.Instant.from('1969-07-20T20:17Z');
instant.toString(); // => '1969-07-20T20:17:00Z'
instant.epochMilliseconds; // => -14182980000
December 7th, 1995 at 3:24 AM in US Pacific time (in Gregorian calendar)
타임 존(Time Zone)과 달력(Calendar)를 알고 있는 date/time 객체로서, 지구상의 특정 지역의 관점에서 특정한 시간에 일어난(또는 일어날) 실제 이벤트를 표현합니다.
const zonedDateTime = Temporal.ZonedDateTime.from({
timeZone: 'America/Los_Angeles',
year: 1995,
month: 12,
day: 7,
hour: 3,
minute: 24,
second: 30,
millisecond: 0,
microsecond: 3,
nanosecond: 500
}); // => 1995-12-07T03:24:30.0000035-08:00[America/Los_Angeles]
August 24th, 2006.
특정 시간과 타임 존(Time Zone)과 관련 없이 달력 날짜(calendar date)를 표현합니다.
const date = Temporal.PlainDate.from({ year: 2006, month: 8, day: 24 }); // => 2006-08-24
date.year; // => 2006
date.inLeapYear; // => false
date.toString(); // => '2006-08-24'
7:39 PM
특정 날짜와 타임 존(Time Zone)과 관련 없이 벽시계 시간(wall-clock tim)을 표현합니다.
const time = Temporal.PlainTime.from({
hour: 19,
minute: 39,
second: 9,
millisecond: 68,
microsecond: 346,
nanosecond: 205
}); // => 19:39:09.068346205
time.second; // => 9
time.toString(); // => '19:39:09.068346205'
December 7th, 1995 at 3:00 PM (in the Gregorian calendar)
타임 존(Time Zone) 정보와 관련 없이 날짜와 시간을 표현합니다. Temporal.TimeZone
을 이용해 Temporal.ZonedDateTime
으로 변환할 수 있습니다.
October 2020
특정 일과 관련 없는 날짜를 표현합니다.
const yearMonth = Temporal.PlainYearMonth.from({ year: 2020, month: 10 }); // => 2020-10
yearMonth.daysInMonth; // => 31
yearMonth.daysInYear; // => 366
14th of July
특정 년도와 고나련 없는 날짜를 푠현합니다.
const monthDay = Temporal.PlainMonthDay.from({ month: 7, day: 14 }); // => 07-14
const date = monthDay.toPlainDate({ year: 2030 }); // => 2030-07-14
date.dayOfWeek; // => 7
5 minutes and 30 seconds
시간의 길이를 표현합니다.
const duration = Temporal.Duration.from({
hours: 130,
minutes: 20
});
duration.total({ unit: 'second' }); // => 469200
IANA 타임 존(Time Zone) 혹은 UTI 자체를 표현합니다.
const timeZone = Temporal.TimeZone.from('Africa/Cairo');
timeZone.getInstantFor('2000-01-01T00:00'); // => 1999-12-31T22:00:00Z
timeZone.getPlainDateTimeFor('2000-01-01T00:00Z'); // => 2000-01-01T02:00:00
timeZone.getPreviousTransition(Temporal.Now.instant()); // => 2014-09-25T21:00:00Z
timeZone.getNextTransition(Temporal.Now.instant()); // => null
달력(Calendar) 시스템을 표현합니다. 대부분의 코드는 ISO 8601 달력을 사용하지만, 다른 달력 시스템들도 사용 가능합니다.
달력과 관련된 연산을 위해서 날짜(Date)는 Temporal.Calendar
와 연결돼있습니다.
const cal = Temporal.Calendar.from('iso8601');
const date = cal.dateFromFields({ year: 1999, month: 12, day: 31 }, {});
date.monthsInYear; // => 12
date.daysInYear; // => 365
Temporal.Now.instant()
- 현재 Temporal.Instant
를 봔환합니다.Temporal.Now.timeZone()
- 현재 Temporal.TimeZone
를 반환합니다.Temporal.Now.zonedDateTime(calendar)
- 시스템 타임 존(Time Zone)과 특정 달력(Calendar) 기준으로 Temporal.ZonedDateTime
을 반환합니다.Temporal.Now.zonedDateTimeISO()
- 시스템 타임 존(Time Zone)과 ISO-8601 달력(Calendar) 기준으로 Temporal.ZonedDateTime
을 반환합니다.Temporal.Now.plainDate(calendar)
- 시스템 타임 존(Time Zone)과 특정 달력(Calendar) 기준으로 날짜(Date)를 반환합니다.Temporal.Now.plainDateISO()
- 시스템 타임 존(Time Zone)과 ISO-8601 달력(Calendar) 기준으로 현재 날짜(Date)를 반환합니다.Temporal.Now.plainTimeISO()
- 시스템 타임 존(Time Zone)과 ISO-8601 달력(Calendar) 기준으로 현재 시간(Time) 반환합니다.Temporal.Now.plainDateTime(calendar)
- 시스템 타임 존(Time Zone)과 특정 달력(Calendar) 기준으로 현재 날짜(Date)와 시간(Time) 반환합니다. 하지만 반환된 객체는 타임 존(Time Zone)을 기억하지 못하기 때문에, 일광 절약 시간(DST)을 사용하는 타임존에서 다른 값(예: 12시간 후)을 도출하는 데 사용해서는 안 됩니다.Temporal.Now.plainDateTimeISO()
- 시스템 타임 존(Time Zone)과 ISO-8601 달력(Calendar) 기준으로 현재 날짜(Date)와 시간(Time) 반환하며 위와 동일한 특성을 가집니다.