최근 나는 팀에서 관리하는 자바스크립트 캘린더 라이브러리에 타임존 기능을 추가하는 작업을 한 적이 있다. 자바스크립트의 타임존 지원이 다른 언어에 비해 부실하다는 얘기는 이미 들어 알고 있었지만, 기존 Date 객체를 잘 추상화하면 어렵지 않게 구현할 수 있을거라 생각했다.
하지만 작업을 진행할수록 자바스크립트에서 타임존을 다루는 것이 결코 쉬운 일이 아니란 것을 깨달았다. 특히 단순히 시간 정보를 포맷팅하는 정도가 아니라, 캘린더처럼 시간 정보에 대한 복잡한 연산이 더해지게 되면 타임존은 더욱 더 골칫덩어리가 된다. 덕분에 오랜만에 문제를 해결할 때마다 새로운 문제가 쏙 쏙 생겨나는 심장 쫄깃한 경험을 할 수 있었다.
이 글은 이러한 개인적인 경험을 바탕으로 자바스크립트에서 타임존을 다룰 때의 문제와 해결방법을 정리해 볼 예정이다. 글을 쓰면서 (왜 글이 점점 길어질까 고민하다가)알게 된 사실인데, 이 고난의 가장 큰 원인은 타임존이라는 도메인에 대한 이해가 부족했기 때문이라고 생각한다. 그래서 글을 1, 2부로 나누어 1부에서는 타임존과 관련 표준에 대해 자세히 알아보고 2부에서 본격적으로 자바스크립트와 관련된 내용을 풀어보도록 하겠다.
타임존은 동일한 로컬 시간을 따르는 지역을 의미하며, 주로 해당 국가에 의해 법적으로 지정된다. 보통 국가별로 각자의 고유한 타임존을 사용하고 있으며, 미국이나 캐나다처럼 면적이 넓은 나라인 경우 지역별로 각기 다른 타임존을 사용하기도 한다. (반면 중국은 그 넓은 면적에도 불구하고 하나의 타임존을 이용하고 있는데, 이로 인해 중국 서쪽 지역에서는 오전 10시는 되어야 해를 볼 수 있다)
한국의 타임존은 보통 GMT+09:00 으로 표현된다. 여기서 GMT는 Greenwich Mean Time의 약자로서 경도 0도에 위치한 영국 그리니치 천문대를 기준으로 하는 태양 시간을 의미한다. GMT 시간은 1925년 2월 5일부터 사용하기 시작하였으며, 1972년 1월 1일까지 세계 표준시로 사용되었다.
GMT는 또한 UTC라고도 불리는데, 두 용어가 혼용되어서 사용되고 있기는 하지만 엄밀히 구분하자면 둘은 다른 의미를 가진다. UTC는 지구 자전주기의 흐름이 늦어지고 있는 문제를 해결하기 위해 1972년에 세슘 원자의 진동수에 기반한 국제 원자시를 기준으로 다시 지정된 시간대이다. 즉, UTC는 좀더 정확한 시간측정을 위해서 GMT를 대체하기 위해 제정된 새로운 표준이며, 시간적으로는 둘 사이에 아주 미세한 차이밖에 없지만, 소프트웨어에서 사용할 때는 UTC라고 하는 것이 더 정확한 표현일 것이다.
UTC라는 약자가 탄생한 배경은 좀 재미있는데, 처음에 영어권에서는 CUT(Coordinated Universal Time)를, 프랑스어권에서는 TUC(Temps Universel Coordonn)를 사용하기를 원했다고 한다. 하지만 결국 한쪽이 이기지는 못하고, 두 언어 모두 C, T, U로 되어 있으니 중재안으로 UTC가 채택되었다고 한다.
UTC+09:00 에서 +09:00 의 의미는 UTC의 기준시간보다 9시간이 빠르다는 의미이다. 즉 UTC 기준으로 현재 낮 12시라면 한국시간으로는 오후 9시가 될 것이다. 이렇게 UTC와의 차이를 나타낸 것을 오프셋이라고 하며, +09:00 혹은 -03:00 등과 같이 표현된다.
보통 국가나 지역들마다 자신들이 사용하는 타임존에 대해 고유의 이름을 부여한다. 예를 들어 대한민국의 타임존은 KST(Korea Standard Time)이라고도 불리는데, 이는 앞서 설명했듯이 특정 오프셋을 지칭하므로 KST = UTC+09:00 이라고 이해하면 된다. 하지만 +09:00 오프셋은 한국 뿐만 아니라 일본, 인도네시아 등 여러 지역에서 사용하고 있으므로, 오프셋과 타임존 이름들의 관계는 1:1 관계가 아닌 1:N 관계이다. +09:00를 사용하는 국가 혹은 지역의 목록은 UTC+09:00에서 확인할 수 있다.
오프셋은 한시간 단위가 아닌 경우도 있다. 예를 들어 북한은 +08:30을 기준시로 사용하고 있고, 호주의 경우 지역에 따라 +08:45, 혹은 +09:30을 기준시로 이용하고 있다.
전체 UTC 오프셋 및 이들 이름의 목록은 List of UTC Time offsets에서 확인할 수 있다.
앞서 말했듯이 우리가 타임존을 말할 때는 보통 KST, JST(Japan Standard Time) 등과 같이 오프셋과 동일한 의미로 쓰인다. 하지만, 특정 지역의 타임존을 단순히 오프셋이라고 지칭하기는 어려운데, 이는 아래의 두 가지 이유 때문이다.
국내에서는 여전히 생소한 개념이지만, 해외 여러 국가에서는 서머 타임(summer time)이 존재한다. 사실 서머 타임은 주로 영국에나 유럽에서 쓰이는 용어인데, 좀더 범용적인 용어로는 일광 시간 절약제, 즉 DST(Daylight Saving Time)라고 불린다. 이는 하절기에 표준시를 원래 시간보다 한 시간 앞당긴 시간으로 이용하는 것을 의미한다.
위키피디아에 따르면 국내에서도 1948 ~ 1951, 1955 ~ 1960, 1987 ~ 1988년에 DST가 시행된 적이 있다고 한다.
예를 들어 미국의 캘리포니아 지역은 평소에는 PST(Pacific Standard Time)을 기준시로 이용하고, 하절기에는 PDT(Pacific Daylight Time, UTC-07:00)를 기준시로 이용한다. 두 시간대를 사용하는 타임존을 묶어서 Pacific Time(PT)라고 부르기도 하며, 미국의 여러 지역 및 캐나다 일부 지역에서도 사용하고 있다.
그러면 이 DST가 적용되는 하절기는 정확히 언제를 의미하는걸까? 사실 DST 적용에는 보편적인 규칙이 있는 것이 아니고, 국가나 지역의 법에 따라서 다르게 적용된다. 예를 들어 미국과 캐나다는 2006년까지 각 지역시간 기준으로 4월의 첫 일요일 오전 2시에 시작하여 10월의 마지막 일요일 오전 12시에 해제하였지만, 2007년부터는 3월의 두 번째 일요일 오전 2시에 시작하여 11월 첫 일요일 2시에 해제하도록 변경되었다. 그리고 유럽에서는 DST가 일괄적으로 적용되는 반면, 미국에서는 각 지역의 시간대별로 순차적으로 적용된다.
앞에서도 잠깐 언급했었지만, 각 지역이 어떤 타임존을 이용할 지는 해당 지역 혹은 국가가 법적으로 결정하기 때문에, 정치적 혹은 경제적 이유로 변경될 수 있다. 예를 들어, 미국에서 2007년부터 DST의 적용시점이 변경된 것은 미국 2005년에 조지 부시 대통령이 서명한 에너지 정책법에 의해서였다. 또한 이집트나 러시아는 기존에 DST를 이용했었지만, 2011년부터 DST를 이용하지 않기로 결정했다.
DST 뿐만 아니라 국가의 표준시가 변경되기도 하는데, 예를 들어 사모아 섬의 경우 원래 UTC-10:00 의 오프셋을 기준시로 사용했었지만, 2011년에 오스트레일리아, 뉴질랜드와의 무역에서 날짜 차이에 따른 손실을 줄이기 위해 UTC+14:00 로 변경하면서 하루를 앞당겼고, 이로 인해 2011년 12월 30일이 사라지게 되면서 크게 뉴스가 되기도 했었다. (뉴스 링크)
네덜란드의 경우에는 1909년부터 +0:19:32.13 이라는 필요 이상으로 정확한 오프셋을 사용하고 있었는데, 1937년부터 +00:20 으로 변경했다가, 1940년부터는 +01:00 을 기준시로 사용하고 있다.
위에서 살펴본 내용을 정리해보자면, 한 지역의 타임존은 하나 혹은 그 이상의 오프셋을 가지며, 어느 시점에 어떤 오프셋을 기준시로 이용할지는 해당 지역의 정치/경제적 상황에 따라 계속해서 달라진다고 할 수 있다.
일상 생활에서는 이런 상황이 큰 문제가 없을지도 모르지만, 이를 규칙에 따라 시스템화 시키려고 하면 문제가 발생한다. 내 스마트폰의 기준시를 오프셋을 이용해서 지정한다고 생각해보자. 만약 내가 DST가 적용되는 지역에 살고 있다면 스마트폰의 기준시를 DST가 적용되고 해제될 때마다 변경해 주어야 할 것이다. 이런 경우 위에서 언급했던 Pacific Time 처럼 표준시와 DST를 묶어서 하나의 타임존으로 인식할 수 있는 또 다른 개념이 필요할 것이다.
하지만, 이를 단순히 몇가지 규칙으로 지정하기는 어렵다. 예를 들어 미국의 경우 2007년을 기준으로 DST 를 적용하는 시점이 변경되었기 때문에, 2006년 3월 31일은 PDT(-07:00)가 기준시가 되지만, 2007년 3월 31일은 PST(-08:00)가 기준시가 되어야 할 것이다. 즉, 특정 지역의 타임존을 지칭하기 위해서는 역사적으로 표준시간대 혹은 DST 적용 룰이 언제 변경되었는지에 대한 데이터를 모두 갖고 있어야만 정확한 시간을 계산할 수 있는 것이다. 즉, 우리는 "뉴욕의 타임존은 PST(-08:00)야" 라고 말할 수 없다. 약간 더 정확히 말하자면 "뉴욕의 타임존은 현재 PST야" 라고 할 수 있을 것이다. 하지만 시스템의 관점에서 더 엄밀하게 말하자면 타임존이라는 단어 없이 "뉴욕은 현재 PST를 기준시로 사용하고 있어" 라고 말하는 것이 가장 정확할 것이다.
그러면 우리는 특정 지역의 타임존을 오프셋이 아닌 무엇으로 지칭해야 할까? 바로 지역명이다. 정확히 이야기하면 역사적으로 표준시간대나 DST의 변경이 동일하게 적용되었던 지역을 하나의 타임존으로 묶어서 지칭할 수 있을 것이다. 앞서 잠깐 언급했던 PT(Pacific Time)과 같은 명칭이 이용될 수도 있겠지만, 이는 현재의 표준시와 DST만을 묶어놓은 개념이기 때문에 역사적인 변경내역을 모두 포함한다고 할 수는 없을 것이다. 또한 PT는 미국/캐나다 지역에서만 사용되는 이름이기 때문에, 소프트웨어에서 범용적으로 사용하기 위해서는 신뢰할 수 있는 기관에서 관리되는 표준이 필요하게 된다.
사실 타임존에 대한 표준은 단순히 규칙이기 보다는 데이터베이스에 가까운데, 역사적인 변경 내역을 모두 저장하고 있어야 하기 때문이다. 이러한 표준 데이터베이스는 여럿이 있지만, 현재로서 가장 신뢰할 수 있는 표준은 바로 IANA time zone database 이다. IANA time zone database는 보통 tz database (혹은 tzdata) 라고 불리며, 전 세계 모든 지역의 표준시와 DST 변경 내역을 담고 있다. 현재 역사적으로 확인할 수 있는 모든 데이터가 들어있다고 볼 수 있는데, UNIX 시간(1970.01.01 00:00:00) 이후의 데이터의 정확도를 보장하도록 정리되었다. (즉, 1970년 이전의 데이터도 있지만 이 시기의 데이터는 완벽한 정확성을 보장하지는 않는다)
이름은 Area / Location 의 규칙을 이용하는데, Area 는 보통 대륙이나 대양명 (Asia, America, Pacific 등)을 지정하며, Location 은 주로 국가명보다는 큰 도시 위주 (Seoul, New_York 등)로 지정된다. (도시명이 국가명보다는 더 영구적인데, 국가의 수명은 생각보다 짧다) 예를 들어 대한민국의 타임존은 Asia/Seoul 이고, 일본의 경우 Asia/Tokyo 인데, 현재 두 지역 모두 UTC+09:00을 표준시로 사용하고 있지만, 실제 역사적인 변경 내역이 다르고 현재 다른 국가에 속해있기에, 별도의 타임존으로 관리되는 것이다.
IANA time zone database는 많은 개발자들과 역사학자들의 커뮤니티들에 의해 관리되고 있으며, 역사적 발견이 추가되거나 정부 정책이 바뀌는 경우 바로 갱신되기 때문에 신뢰도가 가장 높다. 또한 리눅스, macOS등 유닉스 기반의 OS들이나 자바, PHP 등 유명 프로그래밍 언어들 등 이미 많은 시스템에서 이 데이터베이스를 내부적으로 사용하고 있다.
위의 지원 환경 목록에 Windows가 없는 것을 눈치챈 사람이 있을 것이다. 사실, Windows는 별도의 데이터베이스를 내장하고 있는데, 이를 Microsoft Time Zone Database라 부른다. 하지만 여기에는 역사적인 변경 내역들이 누락된 부분이 많고, Microsoft라는 회사에 의해서만 관리되기 때문에, 정확도나 신뢰도 면에서 IANA에 비해 떨어진다고 볼 수 있다.
서두에서 잠깐 언급했지만, 자바스크립트는 사실 타임존 지원이 미약한 편이다. 기본적으로는 현재 지역의 (좀더 정확히 이야기하자면 설치된 OS에 설정된 타임존)을 따르게 되어 있기 때문에, 명시적으로 타임존을 변경할 수 있는 방법이 없다. 뿐만 아니라 데이터베이스 표준에 대한 명세도 명확하지 않은데, 실제 ES2015의 스펙을 살펴보면 로컬 타임존과 DST의 적용에 대해 두세 줄 정도의 모호한 정의만 되어 있을 뿐이다. 예를 들면 DST의 경우 다음과 같이 정의되어 있다. 링크 : ECMAScript 2015 - Daylight Saving Time Adjustment
An implementation dependent algorithm using best available information on time zones to determine the local daylight saving time adjustment DaylightSavingTA(t), measured in milliseconds. An implementation of ECMAScript is expected to make its best effort to determine the local daylight saving time adjustment.
뭔가… 그냥 최선을 다해서 정확히 구현하라는 의미로만 보인다 (-_-). 즉, 구현하는 브라우저 벤더마다 구현이 달라질 수도 있다는 의미이다. 이런 무책임한! 이라는 생각이 들 때쯤 바로 밑에 NOTE 한 줄이 추가되어 있는 것이 눈에 보인다.
NOTE : It is recommended that implementations use the time zone information of the IANA Time Zone Database http://www.iana.org/time-zones/.
그렇다. ECMA 스펙은 이 한 줄로 책임을 살짝 피해가려 했겠지만, 결론적으로 자바스크립트에서는 사실상 명시된 표준 데이터베이스가 없고, 단지 IANA Time Zone Database가 권장될 뿐이다. 당연한 이야기지만 이로 인해 실제로 브라우저마다 타임존 연산이 다르게 동작하는 경우도 있다. 나중에 국제화 API를 위한 ECMA-402 스펙의 Intl.DateTimeFormat
에서 IANA timezone을 사용하는 옵션이 추가되긴 했지만, 여전히 다른 언어에 비해서는 신뢰할만한 타임존 지원이 많이 부족하다고 볼 수 있다.
지금까지 살펴본 것처럼, 타임존은 생각보다 복잡한 개념이며 이를 정확하게 계산하기 위해서는 단순한 연산 뿐만 아니라 표준화된 데이터베이스가 필요하다. 이러한 작업을 언어 차원의 지원 없이 직접 구현하기는 쉽지 않은데, 안타깝게도 자바스크립트는 이러한 지원이 상당히 적은 편이다. 2부에서는 실제 자바스크립트에서 타임존을 다룰 때 발생하는 문제와 이를 어떻게 해결할 수 있을지를 알아보도록 하겠다.