[JavaScript] Date

Timezone

timezone이란, 동일한 로컬 시간을 따르는 지역을 의미한다. 국가별로 고유한 타임존을 사용하고 있고, 면적이 넓은 나라인 경우 지역별로 다른 타임존을 사용하기도 한다.

GMT

한국의 타임존은 GMT+09:00으로 표현된다. GMT란, Greenwich Mean Time의 약자로 사용되고, 경도 0도에 위치한 영국 그리니치 천문대를 기준으로 하는 태양 시간을 말한다.

이는 1925년 2월 5일부터 사용하기 시작하였고, 1972년 1월 1일까지 세계 표준시로 사용되었다.

UTC

UTC는 지구 자전주기의 흐름이 늦어지고 있는 이슈를 해결하려고 다시 지정된 시간대이다. 좀 더 정확한 시간 측정을 위해서 GMT를 대체하기 위해 만들어진 표준이며, 미세한 차이뿌니지만, UTC를 사용하는 것이 더 정확한 표현이다.

offset

오프셋의 의미는, UTC+09:00 일때, UTC의 기준 시간보다 9시간이 빠르다는 의미를 말한다. 즉, UTC와의 차이를 나타낸 것을 말한다. +09:00 등과 같이 표현된다.

타임존은 KST, JST와 같이 오프셋과 동일한 의미로 쓰이지만, 타임존과 오프셋이 동일하다고 보진 않는다. 이 이유는 두 가지가 있다.

DST

해외 여러 국가에서는 서머타임(Daylight Saving Time)이 존재하는데, 이는 하절기에 표준시를 원래 시간보다 한 시간 앞당긴 시간으로 이용하는 것을 의미한다.

DST 적용에 관련해서는 각 나라나 지역의 법에 따라서 다르게 적용된다. 그래서 이는 여러 이유로 변경이 될 수 있다. 또한 국가의 표준시도 같은 여러 이유로 변경될 수가 있다.

타임존과 오프셋의 관계

즉, 한 지역의 타임존은 여러 오프셋을 가질 수 있다. 서머타임이든, 어떤 이유든 여러가지 종류의 오프셋으로 이용될 수 있으며, 이는 계속해서 달라질 수 있다.

따라서 이를 시스템화 시키는 데에는 문제가 발생한다. 내가 현재 살고 있는 지역이 서머타임인지, 어떤 오프셋에 적용되어 있는지, 아니면 지역이 바뀌었을 경우를 매번 체크해서 변경해주어야 할 것이다.

그래서 소프트웨어에서 특정 지역의 타임존을 제대로 사용하기 위해서는, 역사적으로 표준 시간대 혹은 서머타임 적용 룰이 언제 변경되었는지 등에 대한 모든 데이터를 갖고 있어야 정확한 시간을 계산할 수가 있다.

여기서 타임존이란, 오프셋이 아닌 역사적으로 표준시간대나 서머타임의 변경이 동일하게 적용되었던 지역을 하나의 타임존으로 묶어서 부를 수 있을 것 같다. 이를 토대로, 소프트웨어에서 Date를 맞게 사용하기 위해서는 신뢰할 수 있는 기관에서 관리되는 표준이 필요할 것이다.

IANA time zone database

위의 타임존에 대한 정의를 기반으로 하면, 타임존의 표준은 단순한 규칙이라기 보다 데이터베이스에 더 가깝다고 할 수 있다. 왜냐하면 역사적인 타임존의 변경 내역을 모두 저장하고 있어야하기 때문이다.

그 중 가장 신뢰할 수 있는 데이터베이스가 IANA time zone database이다. 이는 tz database라고도 불리며, 전 세계 모든 지역의 표준시 및 서머타임(DST) 변경 내역을 가지고 있다.

Area/Location 의 규칙을 이용한다.

  • Asia/Seoul

IANA time zone database는 많은 개발자와 역사학자들의 커뮤니티에 의해 관리되고 있다. 그래서 역사적 발견이나 정부정책에 의해 바뀌는 경우 바로 업데이트되어 관리되고 있기 때문에 신뢰성이 가장 높다. 그래서 리눅스, 맥OS 등의 유닉스 기반의 OS나 자바, php 등의 프로그래밍 언어에서 이 데이터베이스를 내부적으로 많이 사용하고 있다.

windows 같은 경우엔, 별도의 데이터베이스를 내장하고 있다. Microsoft Time Zone Database라고 부르는 데이터베이스의 타임존을 사용하고 있는데, 여기서는 역사적인 부분들이 일부 누락된 부분도 많고, Microsoft 회사에서만 관리되어 IANA에 비해 정확도와 신뢰도가 떨어진다고 한다.

자바스크립트와 IANA

자바스크립트는 타임존 지원이 미약한 편이다. 기본적으로 현재 지역의 타임존(설치된 OS에 설정된 타임존)을 따르게 되어 있어서, 명시적으로 타임존을 변경할 수 없다. 자바스크립트의 ECMAScript 스펙에서는 단지 IANA time zone database를 권장하고 있기만하고, 명시된 표준 데이터베이스가 없다.

자바스크립트에서 지원하는 타임존 API의 한계 및 보완점

타임존을 고려할 때, 클라이언트가 서로 다른 타임존을 갖고 있을 수 있다는 점을 신경써야한다. 즉, 다양한 클라이언트의 환경을 지원하기 위해 서버에 저장되는 데이터가 타이존에 영향을 받지 않는 절대값이어야 한다. 그래서 클라이언트에서 서버로 전달하는 날짜, 시간 정보가 오프셋(UTC)에 맞춰진 값이거나, 클라이언트 환경의 타임존 정보까지 포함된 값이 되어야한다.

자바스크립트 Date 객체

자바스크립트에서 날짜, 시간에 관련된 작업은 Date 객체를 사용한다. 이는 Java의 Date 클래스에서 영향을 많이 받다고 하여, Immutable 데이터가 아닌 점이나, Month가 0으로 시작하는 단점까지 같이 가지고 있다.

자바스크립트의 Date 객체는 유닉스 시간과 같은 절대값으로 시간 데이터를 관리한다. Date 생성자는 인자로 숫자 하나만을 받게 되면 밀리초 단위의 유닉스 시간으로 인식하게 된다. 생성자나 메소드 같은 경우엔 클라이언트의 브라우저가 실행되는 운영체제에 설정된 타임존의 영향을 받는다. 그래서 사용자가 입력한 데이터를 사용하여 그대로 Date 객체를 생성하면, 클라이언트의 로컬 타임존을 그대로 반영하게 된다.

  • 사용자 입력값을 이용하여 Date 객체 생성

사용자가 타임존이 서울로 설정된 기기에서 날짜를 입력했을 때,

var d1 = new Date(2017, 2, 11, 11, 30);
d1.toString(); // Sat Mar 11 2017 11:30:00 GMT+0900 (KST)

위와 같은 결과가 나온다. 생성자를 이용하여 Date 객체를 받아오는 다른 방법은, 문자열 데이터를 이용하는 방법이 있지만 Date.parse() 이와 같은 메소드로 사용하는 것은 브라우저마다 구현상태가 다르고, 정확한 값이 나오기 어렵기 때문에 사용하지 않기를 권장한다.

  • 서버 데이터를 이용하여 Date 객체 생성

데이터가 숫자형의 유닉스 시간값이면, 간단하게 생성자를 이용하여 Date 객체를 생성할 수 있다. Date 생성자는 유닉스 시간값을 밀리초 단위로 다룬다.

var d1 = new Date(1489199400000);
d1.toString(); // Sat Mar 11 2017 11:30:00 GMT+0900 (KST)

유닉스 시간이 아닌 ISO-8601와 같은 문자열 타입은 사용하지 않는 것을 권장하지만, ES5 이후부터 ISO-8601를 지원하도록 명시되어 있어 IE9 이상의 브라우저부터 ES5를 지원하는 점만 주의한다면 이를 Date 생성자에 사용할 수 있다. 주의할 점은 최신 브라우저가 아닌 경우 Z 문자가 없으면 UTC 기준으로 해석해야 함에도 불구하고 로컬 타임을 기준으로 해석하는 경우가 있다. 즉, 문자열 데이터로 Date를 생성할 경우엔 최신 브라우저가 아니면 Z의 유무에 따라 값이 달라질 수 있다.

// 로컬 시간을 의미하는 ISO 8601 문자열
2019-12-06T15:00:00.000

// UTC(GMT) 시간을 의미하는 ISO 8601 문자열
2019-12-06T06:00:00.000Z

// 로컬 시간을 의미하면서 UTC(GMT) 대비 +09:00 임을 의미하는 ISO 8601 문자열
2019-12-06T15:00:00.000+09:00
  • 서버로 전달할 데이터 생성

Date 객체를 앞서 설명한 방법으로 생성할 수 있다. 하지만 이를 이용해 다시 서버로 데이터를 전송하려면 데이터를 변환하는 과정이 필요하다.

유닉스 시간 형식으로 데이터가 이루어져 있을 경우, getTime() 메소드를 이용하여 만들 수 있다.

var d1 = new Date(2017, 2, 11, 11, 30);
d1.getTime(); // 1489199400000

ISO-8601형식의 문자열은 ES5 이상, IE9 이상의 브라우저만 지원한다. toISOString(), toJSON() 메소드를 활용하여 해당 형식의 문자열을 생성할 수 있다. 이의 결과값은 동일하지만, 유효하지 않은 데이터 처리는 다르다.

var d1 = new Date(2017, 2, 11, 11, 30);
d1.toISOString(); // "2017-03-11T02:30:00.000Z"
d1.toJSON();      // "2017-03-11T02:30:00.000Z"

var d2 = new Date('Hello');
d2.toISOString(); // Error: Invalid Date
d2.toJSON();      // null

이 밖에도 toGMTString(), toUTCString() 메소드가 있다. UTC 기준으로 문자열을 생성할 수 있고, 이 메소드는 RFC-1123에 맞는 문자열을 반환한다.

로컬 타임존 변경

만약 타임존을 직접적으로 변경할 경우, 데이트를 직접적으로 변경했기 때문에 서버에 해당 데이터를 전송하려면 바뀐 데이터를 다시 계산해서 보내야한다.

날짜 연산을 다시 하게 되면, 해당 지역에 관한 오프셋을 기준으로 진행해야만 정확하게 연산을 할 수 있다. 하지만 여기서, 지역에 따라 반영되는 서머타임의 경우에 또 달라질 수 있다. 이는 문제를 발생시킨다. 그래서 정확한 날짜 연산을 위해서는 IANA time zone database와 같이 타임존 변경 히스토리를 가지고 있는 전체 데이터를 사용해야한다.

Moment Timezone

그래서 여기서 Moment라는 자바스크립트에서 날짜 연산을 할 때 사용되는 라이브러리가 있다. 이는 다양한 날짜 연산이나 형식 API를 제공하고, 안정성 또한 검증받았다고 한다.

해당 라이브러리는 IANA time zone database의 데이터를 내장하고 있어 실제 오프셋이나 서머타임 등을 정확히 반영하여 계산해준다.

var seoul = moment(1489199400000).tz('Asia/Seoul');
var ny = moment(1489199400000).tz('America/New_York');

seoul.format(); // 2017-03-11T11:30:00+09:00
ny.format();    // 2017-03-10T21:30:00-05:00

seoul.date(15).format();  // 2017-03-15T11:30:00+09:00
ny.date(15).format();     // 2017-03-15T21:30:00-04:00

소감

평소에 Date 객체에 대해서 깊게 생각해본적이 없던 나는 이번 기회에 자바스크립트에서 Date 객체를 사용할 때에 어떤 한계점이 있었는지, 또한 Date를 계산할 때 UTC, GMT, 오프셋 등의 개념 등 여러 가지의 개념들이 존재한다는 것을 알 수 있었다.

일상적으로 너무 당연하게 여겨왔던 것들이 어떤 역사와 변천사를 거쳐서 지금에 이르렀는지를 알아보니 이러한 것들을 공부함으로써 앞으로 내가 자바스크립트로 Date 객체를 활용하여 개발을 할 경우에 다시 한 번 이러한 한계점 등을 깊게 생각하여 구현할 수 있을 것 같다. 이러한 점들을 공부할 수 있어 재밌었고, 생각보다 훨신 더 많은 시간을 투자하게 된 것도 놀라웠다.

참고 자료