Temporal에 대해 알아보자

Hant·2023년 3월 5일
1

JavaScript Basic

목록 보기
14/14
post-thumbnail

최근 평소에 동경하던 회사 동료분께서 Temporal을 주제로 짧은 테크톡을 진행해 주셨습니다. 이 내용을 완전히 내 것으로 소화하고자, 공유해주신 래퍼런스를 기반으로 복습하는 시간을 가졌습니다. 모자라고 부족한 나를 끌어주는 동료가 있다는 것만큼 행복한 환경은 없는 것 같습니다.

Temporal은 Javascript의 새로운 전역 객체로, 날짜 및 시간 작업을 위한 표준 객체와 함수를 제공합니다. 현재 Stage3이며, 폴리필 없이는 모든 브라우저에서 사용할 수 없습니다. 기존 Javascript의 Date 객체에는 어떤 문제가 있기에 새로운 date/time API가 필요해진 걸까요? 아직은 생소한 Temporal에 대해 알아봅시다.

Date 객체의 문제점

Referencs

  • 유저의 로컬 시간과 UTC를 제외한 타임 존(Time Zone)을 지원하지 않습니다.
  • 일광 절약 시간제(Daylight Saving Time, DTS) 동작이 예측 불가능합니다.
  • 계산하는 API 사용이 불편합니다.
  • 그레고리안 켈린더(Gregorian Calendar)만을 지원합니다.
  • 파서(Parser)의 동작을 신뢰할 수 없습니다.
  • Date 객체가 Mutable 합니다.

위의 문제점들은 Date 객체 업데이트로 수정이 가능한 문제와, 수정이 불가능한 문제로 나눌 수 있습니다.

수정 가능한 문제들

  • 타임존(Time Zone) 지원 문제는 새로운 Factory 함수를 제공하면 해결할 수 있습니다.
    const zoneDate = Date.inZone(zoneIdentifier, dateString);
  • 같은 방식으로 캘린더 지원 문제도 해결 가능합니다.
    const calendarDate = Date.withCalendar('Hijri', dateString);
  • 계산하는 API를 쉽게 만드는 것도 큰 일은 아닙니다.
    const myDate = new Date();
    // AS IS
    myDate.setDate(myDate.getDate() + 7);
    // TO BE
    myDate.addDays(7);
  • 일광 절약 시간제(DST) 버그도 ECMA2018에서 이 PR로 인해 수정됐습니다.

수정이 불가능한 문제들

T39 맴버들이 지속적으로 주의하는 두개의 중요한 개념이 있습니다.

  1. Web Compatibility: 현존하는 ECMAScript의 동작과 호환되지 않는 변경은 하지 않습니다.
  2. Web Reality: 스펙에 기술되지 않은 동작이라도, 현재 특정한 방식으로 동작하고 있다면, 앞으로도 계속 그렇게 동작해야 합니다.

한마디로 "Dont break the web"으로 해석할 수 있으며, Semver의 "Major Update"는 Javascript에서 일어날 수 없습니다.

Mutability 개선 (Web Compatibility 위반)

기존에 Date 객체를 Mutable하게 사용하던 많은 코드들이 망가집니다.

Parser 개선 (Web Reality 위반)

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 객체가 구상됐습니다.

Temporal 간략하게 알아보기

Referencs

  • 사용하기 쉬운 날짜 및 시간 계산 API를 제공합니다.
  • 안전한 일광 절약 시간제(DST-safe) 계산을 포함한 모든 타임존(Time Zone)을 최고 수준으로 지원합니다.
  • 고정된 날짜 및 시간 객체만을 다룹니다.
  • 엄격하게 지정된 문자열 포멧을 파싱(Parsing)합니다.
  • 그레고리안이 아닌 켈린더(non-Gregorian calendars)를 지원합니다.

Temporal은 날짜 전용(date-only), 시간 전용(time-only) 및 기타 용도에 대한 별도 클래스를 제공합니다. 이런 방식은 코드의 가독서을 높일 뿐더러, 알 수 없는 값에 대해 0, UTC, 혹은 로컬 타임존을 잘못 추측하여 발생하는 버그를 예방합니다.

문자열 영속성, 파싱, 포멧팅

모든 Temporal 타입들은 영속성과 상호운용성을 위한 문자열 표현을 가지고 있습니다.

String persistence, parsing, and formatting

이미지 출처 (Temporal Documentation - https://tc39.es/proposal-temporal/docs)

API Documentation

Temporal API에서 타임존(Time Zone)과 관련되지 않은 객체(Temporal.PlainDate, Temporal.PlainTime, 그리고 Temporal.PlainDateTime )의 이름은 "Plain"으로 시작합니다. 이런 Plain 타입과 정확한 시간 타입(Temporal.Instant 그리고 Temporal.ZonedDateTime) 사이의 변환 작업은 타임존(Time Zone)과 일광 절약 시간제(DST) 때문에 모호합니다. 개발자들은 Temporal API를 통해 이 모호함이 해결되는 방법을 구현할 수 있습니다.

Object relationship

이미지 출처 (Temporal Documentation - https://tc39.es/proposal-temporal/docs)

Temporal.Instant

July 20, 1969, at 20:17 UTC

캘린더나 지역 상관 없이 고정된 시간(혹은 정확한 시간(exact time))을 표현합니다. 사람이 읽을 용이한 현지 달력 날짜(local calendar date) 혹은 시계 시간(clock time)을 만들기 위해서는, Temporal.TimeZoneTemporal.Calendar를 사용해서 Temporal.ZonedDateTime 혹은 Temporal.PlainDateTime를 만드세요.

const instant = Temporal.Instant.from('1969-07-20T20:17Z');
instant.toString(); // => '1969-07-20T20:17:00Z'
instant.epochMilliseconds; // => -14182980000

Temporal.ZonedDateTime

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]

Temporal.PlainDate

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'

Temporal.PlainTime

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'

Temporal.PlainDateTime

December 7th, 1995 at 3:00 PM (in the Gregorian calendar)

타임 존(Time Zone) 정보와 관련 없이 날짜와 시간을 표현합니다. Temporal.TimeZone을 이용해 Temporal.ZonedDateTime으로 변환할 수 있습니다.

Temporal.PlainYearMonth

October 2020

특정 일과 관련 없는 날짜를 표현합니다.

const yearMonth = Temporal.PlainYearMonth.from({ year: 2020, month: 10 }); // => 2020-10
yearMonth.daysInMonth; // => 31
yearMonth.daysInYear; // => 366

Temporal.PlainMonthDay

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

Temporal.Duration

5 minutes and 30 seconds

시간의 길이를 표현합니다.

const duration = Temporal.Duration.from({
  hours: 130,
  minutes: 20
});

duration.total({ unit: 'second' }); // => 469200

Temporal.TimeZone

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

Temporal.Calendar

달력(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

  • 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) 반환하며 위와 동일한 특성을 가집니다.
profile
끊임없이 도전하는 프론트 개발자가 되고자 노력합니다.

0개의 댓글