무역 거래 도메인의 ERP 서비스를 개발하면서 날짜와 시간에 대한 개념에 많이 접하게 되었습니다. 시스템에서 다루는 시간 정보와 사용자가 입력하는 날짜 정보 간의 차이, 그리고 timestamp와 date time과 같은 데이터 타입을 어떻게 다루어 통신하는지 이해할 수 있었습니다. 또한, 글로벌하게 어떤 표기법이 통용되는지도 새로이 알게 되었습니다. 이 글에서는 시간/날짜 값의 기반으로 사용되는 UTC와 세계 표준적인 표현법인 ISO 8601에 대해 알아보고자 합니다. 해당 표준법을 JavaScript에서는 어떻게 적용되는지에 대한 개념을 적립할 수 있을 것이라 기대합니다.
목차
1️⃣ UTC
2️⃣ ISO 8601
3️⃣ JavaScript Libraries
4️⃣ Conclusion
JavaScript에서 시간을 나타내기 위해 🔗 Date 객체를 사용합니다. Date 객체는 UTC의
“1970년 1월 1일 UTC(협정 세계시) 자정”과의 시간 차이를 “밀리초(ms)”로 나타내는 “정수 값”을 담습니다.
위 문장에서 엿볼 수 있듯이, UTC는 특정한 시간값이 아닌 하나의 기준값임을 알 수 있습니다.
UTC의 정확한 의미에 대해 한 번 살펴보겠습니다.
- UTC(협정 세계시)
아시는 것처럼 1일은 24시간, 1시간은 60분, 1분은 60분입니다. 즉, 하루는 🔗 그레고리력(태양력)을 기반으로 86,400초입니다. 그러나 지구의 자전 주기가 일정하지 않기 때문에, 세계시 UT1과 0.9초의 차이가 생길 때 협정 세계시에서는 하루의 마지막 1분을 59초나 61초로 해서 이 차이를 수정합니다.(윤초)
UTC는 1972년 1월 1일부터 시행된 국제 표준시이며, 1970년 1월 1일 자정을 0 밀리초로 설정하여 기준 삼습니다. 그리고 그 후의 시간의 흐름을 밀리초로 계산합니다. Date 객체를 사용하며 접했던 개념이죠. 더불어 UTC는 🔗 그리니치 평균시(GMT)에 기반하여 혼용되기도 합니다. ”GMT+0900 (대한민국 표준시)"와 같은 표기가 이에 해당합니다. 다음과 같이 런던을 기준으로 시간 차이를 두어 표현합니다.
런던(LON) :: GMT+0, 0:00
파리(PAR) :: GMT+1, 1:00
…
서울(SEL) :: GMT+9, 9:00
다시 JavaScript Date 객체로 넘어와서, 해당 객체는 UTC 기준의 정수 값을 담는 형태로 생성됩니다. 그리고 Date 객체의 내장된 getDate(), getYear() 등의 메서드를 통해 시간 및 날짜를 문자열로 표현합니다. 다시 말해, Date 객체는 UTC 기준의 시간 값을 가지지만, 메서드들로는 System’s location 기반의 시간대를 사용할 수 있습니다.
UTC 기반의 값을 통해 국제적으로 같은 시간을 분명하게 측정할 수 있게 됩니다.
하지만 문제가 있습니다. 나라/지역마다 시간/날짜를 표기하는 표기법이 다르다는 것이죠. 당장 위의 예시만 보더라도, UTC 기준의 시간은 “GMT+0900”로 쓸수도, “GMT+9”로 쓸수도, “+09:00”로 쓸수도 있을 것이 추측됩니다. 이를 위해 🔗 ISO 8601는 표준적인 표기법을 제시합니다.
잠깐. ISO 8601 이전에, ISO는 무슨 의미일까요?
- ISO(International Organization for Standardization)
ISO는 여러 나라의 표준 제정 단체들의 대표들로 이루어진 표준화 기구입니다. 이는 1947년에 공식적으로 출범하여 약 164개의 회원국이 가입, 활동하고 있습니다. 아래와 같은 표준들을 설립하여 공표하였습니다.
639 :: 언어 이름의 국제 표준
646 :: ASCII(미국 표준 코드 정보 교환)
8601 :: 날짜와 시간의 표기에 대한 국제 표준
9362 :: 금융기관의 식별 코드에 대한 국제 표준 (SWIFT/BIC 코드)
15924 :: 문자의 코드에 대한 국제 표준
위에서 언급했듯이, 전 세계적으로는 시간/날짜를 표기하는 표기법이 다릅니다.
예를 들어, 북미인들은 보통 “2024.2.26”과 같이 날짜의 전월을 표기합니다. 그러나 유럽인들은 일반적으로 “26.2.2024”과 같이 월 앞의 날짜를 적습니다. 이와 같이, ‘year-month-day의 순서는 어떻게 정해야 할지?’, ‘달(month)을 이름으로 적을지, 숫자로 적을지?’, ‘한 주의 시작은 무슨 요일일지?’, … 상상 이상의 다양함이 존재합니다.
ISO 8601은 이러한 유형의 혼동을 없애는 국제 표준을 제공합니다. 1988년에 첫 판으로 공개되었고, 현재의 세번째 판은 2004년에 공개되었습니다. ISO 8601의 날짜 표현은 1582년 계절 주기를 더 잘 수용하기 위해 율리우스력을 대체한 그레고리력을 기반으로 합니다. ISO 표준은 윤년도 고려합니다. 또한 24시간 시계(군사 시간이라고도 함)를 기반으로 하며, 다양한 시간대와 협정 세계시(UTC)와의 차이점을 수용합니다.
ISO 8601의 보편적인 원칙은 다음과 같습니다.
날짜와 시간 값은 시간 단위의 가장 큰 것부터 가장 작은 것으로 정렬된다: 년도, 월(혹은 주), 일, 시, 분, 초, 그리고 초보다 더 작은 단위. 그러므로 연대순에 대응하는, 표현의 사전식 순서는 음수 년도를 이끄는 날짜 표현은 제외한다. 이것은 날짜가, 예를 들자면 파일 시스템에 의해 자연스럽게 정렬되는 것을 허용한다.
날짜와 시간은 각각 앞에 0을 붙여서(leading zeros) 유지해야 하는 고정된 자릿수(fixed number of digits)를 갖는다.
표현은 두 가지 형식 중 한 가지로 이루어질 수 있다. 하나는 최소한의 구분자를 이용한 기본 형식이고 다른 하나는 가독성을 높이기 위해 추가된 구분자를 이용한 확장 형식이다. 표준에서 "평문에서 기본 형식은 피해야 한다"라고 적어놓고 있다. 날짜 값들(년도, 월, 주, 그리고 일) 사이에서 사용되는 구분자는 붙임표인데 반해, 시간 값(시, 분, 그리고 초) 사이에서 사용되는 구분자는 쌍점이다. 예를 들어, 2009년 1월 6일은 확장 형식으로 "2009-01-06"으로 쓰일 수 있으며, 애매모호함 없이 기본 형식에서는 "20090106"으로 간단히 쓰일 수 있다.
줄어든 정밀도를 위해, 날짜와 시간 표현에서 값들 중 어떤 숫자든지 제외될 수 있으나, 큰 단위가 앞에, 작은 단위가 뒤로 오는 순서는 유지해야 한다. 예를 들어, "2004-05"는 2004년 5월을 가리키는 유효한 ISO 8601 날짜이다. 이 형식은 결코 2004년의 지정되지 않은 달의 5일을 표현하는 것이 아니며 2004년부터 2005년 사이의 기간을 말하는 것이 아니다.
특정 애플리케이션에서의 필요성으로, 표준에서는 표현 내에서의 가장 작은 단위의 시간 값에 십진수 기반의 분수를 지원하고 있다.
원칙을 기반으로 “2024년 02월 26”일 기준의 대표적인 표준은 다음과 같습니다.
날짜 (Date) :: “2024-02-26”
조합된 UTC 날짜 및 시간 (Combined UTC Date and Time) :: “2024-02-26T00:00:00+00:00” 또는 “2024-02-26T00:00:00Z” 또는 “20240226T000000Z”
주 (Week) :: “2024-W09”
주 번호가 포함된 날짜 (Date with Week Number) :: “2024-W09-1”
연도가 없는 날짜 (Date without Year) :: “-02-26”
연중 일자 (Day of the Year) :: “2024-57”
가장 큰 시간 용어(year)가 왼쪽에 놓이며, 더 작은 용어들은 이전 용어의 우측에 놓이게 됩니다. 이는 왼쪽에서 오른쪽의 방향을 가지는 아랍에서도 통용되는 표준이 됩니다.
또한, “Jan”, “Thursday”같은 허용되지 않고, 아라비아 숫자와 표준 내에서 특정 의미를 제공하는 문자들("-", ":", "T", "W", "Z", …)만 표현될 수 있습니다.
ISO 8601 표준은 날짜 및 시간 데이터를 애플리케이션과 기계가 국경, 다양한 문화, 혹은 서로 다른 시간대 간에 전달할 때 발생할 수 있는 불확실성을 최소화하며, 이를 통해 날짜 및 시간 정보를 교환하는 데 광범위하게 활용되고 있습니다.
이렇게, ISO 8601까지 살펴보았습니다. 그렇다면 애플리케이션 엔지니어 입장에서, 특히 JavaScript 개발자는 이와 같은 표준을 어떻게 적용하는지 살펴보도록 하겠습니다.
역시나 대표적인 라이브러리가 있습니다. ISO 8601을 기반의 다양한 포맷팅을 지원해줍니다. 이를 통해 기존의 JavaScript Date 객체로써 표현한 정수값 혹은 문자열 값으로 우리가 원하는 형식의 시간/날짜를 표기할 수 있습니다.
대표적인 라이브러리는 다음과 같습니다.
Moment.js
강력하고 다양한 API를 제공하고, 많은 언어로의 지역화를 지원합니다. 가장 대표적인 라이브러리로 사용되었습니다. 하지만 번들 사이즈가 다소 크고 불변성을 지원하지 않는 이슈가 있습니다. 해당 이슈를 🔗 라이브러리 측도 인지하지만, 안정성을 위한 유지보수 개발 방향으로 유지하고 있습니다.
Date-fns
함수형 프로그래밍 패러다임을 따르며, 함수가 순수 함수로 독립적이라서 필요한 함수만 가져와 사용할 수 있어 경량화가 가능합니다. 또한 불변성을 지원합니다. API가 atomic하고 제한적이지만, Moment.js의 대안으로 많이 사용되는 추세입니다.
Day.js
Moment.js의 API를 거의 그대로 따르면서도 크기가 훨씬 작습니다. 또한 플러그인 시스템을 통해 필요한 기능만 추가할 수 있습니다.
Luxon
Moment.js 개발팀에서 만든 라이브러리로, Moment.js의 단점을 보완하고자 만들어졌습니다. 불변성을 지원하며, 다양한 날짜/시간 연산을 지원합니다. Moment.js 사용자들 중 일부가 이동하며 점차 인기를 얻고 있습니다.
TC39에서는 Date 객체 대신 🔗 Temporal 객체를 작업 중이라고 (mdn)문서에 기재되어 있습니다. (FYI, ECMAScript 언어의 표준인 ECMA-262 명세가 있습니다. Ecma 인터내셔널의 여러 기술 위원회(Technical Committee; TC) 중 TC39는, 해당 명세의 관리를 맡습니다.) Temporal은 시간대 간 날짜 변환, 일광 절약 시간을 고려하면서 더하기와 빼기, 날짜만 또는 시간만 데이터로 작업, 심지어 그레고리력이 아닌 달력의 날짜 처리 등 Date로는 어렵거나 불가능했던 작업을 쉽게 수행할 수 있게 해준다고도 합니다. JavaScript built-in 객체로써 라이브러리와 같은 강력한 API들을 기대할 수 있으리라 생각 듭니다.
이렇게 timestamp의 기준시가 되어주는 UTC와, date time을 불확실성 없이 교환할 수 있게끔 공표된 ISO 8601 개념에 대해 살펴보았습니다.
새로운 개념에 대해 접하고 고려할 수 있었던 경험을 하게 돼 즐거웠습니다. 이러한 표준법들을 학습하고 데이터에서부터 사용자와 인터랙션 하는 데까지 문제 없게끔 서비스를 만들고자 합니다.
글에 잘못된 부분이 있다면 언제든지 피드백 해주시면 감사하겠습니다!
글 읽어주셔서 감사합니다 :)
Ref