CS · Time · Backend

시간대 - 팻말은 나라가 고쳐 단다

시간대는 지도에서 잘라 낸 세로줄이 아니라 정부가 정하고 수시로 바꾸는 규칙이다. 그래서 컴퓨터는 그 규칙 목록을 따로 들고 다녀야 하고, 그 목록이 낡으면 조용히 한 시간이 어긋난다.

·시간이라는 데이터 4편
목차
  1. 지도를 세로로 자른 게 아니다
  2. 팻말은 실제로 바뀐다
  3. 그래서 규칙 목록을 따로 들고 다닌다
  4. 이름이 나라가 아니라 도시인 이유
  5. 약어는 믿을 게 못 된다
  6. 과거의 팻말도 들어 있다
  7. 실무에서: 목록을 최신으로 유지하는 것도 일이다
  8. 정리

앞 글에서 시간대는 규칙 묶음이라고 했다. 이번 글은 그 규칙을 누가 쓰고 어떻게 내 프로그램까지 오는지를 따라간다.

지도를 세로로 자른 게 아니다

시간대를 떠올리면 지구본에 세로줄이 그어진 그림이 먼저 온다. 경도 15도마다 한 시간씩, 깔끔하게.

현실은 전혀 그렇지 않다.

  • 중국은 동서로 아주 넓은데 시간대가 하나다. 서쪽 끝은 해가 중천일 때가 오후 3시쯤이다
  • 인도는 +05:30이라 이웃 나라와 30분이 어긋난다
  • 스페인은 지리적으로 영국과 같은 줄인데 이웃 나라를 따라 한 시간 앞선 시각을 쓴다

시간대는 천문의 결과가 아니라 행정의 결과다. 나라가 정하고, 나라가 바꾼다. 이 한 문장이 시간 다루기가 어려운 이유의 절반이다.

팻말은 실제로 바뀐다

“바꾼다”가 이론적인 이야기가 아니다.

  • 서머타임을 시작하거나 없애는 결정이 매년 어딘가에서 일어난다
  • 어떤 나라는 표준 오프셋 자체를 바꾼다. 한 나라가 통째로 30분이나 한 시간을 옮긴 사례가 여럿 있다
  • 발표가 시행 몇 주 전에 나오기도 한다

그러니까 팻말의 숫자는 코드에 적어 둘 수 있는 상수가 아니다. 바깥에서 계속 받아 와야 하는 데이터다.

그래서 규칙 목록을 따로 들고 다닌다

세상의 시간대 규칙을 한곳에 모아 두는 데이터베이스가 있다. IANA 시간대 데이터베이스, 흔히 tzdata라 부른다.

정부 발표 규칙을 바꾼다
tzdata 목록에 반영
서버 · 컨테이너 사본이 낡았다
내 프로그램 옛 규칙으로 계산
여기서 끊기면 에러 없이 한 시간이 어긋난다

시간대 규칙은 코드가 아니라 밖에서 내려오는 데이터다. 사슬 한 군데만 낡아도 끝까지 도달하지 못한다.

각 나라가 규칙을 바꾸면 그 내용이 이 데이터베이스에 반영되고, 갱신된 목록이 운영체제와 언어 런타임을 거쳐 내 프로그램까지 내려온다. 1년에 여러 번 갱신된다.

여기서 중요한 건 이 사슬이 한 군데만 낡아도 끊긴다는 점이다. 서버 OS의 목록이 낡았거나, 컨테이너 이미지에 시간대 데이터가 아예 없거나, 언어 런타임이 자기 사본을 쓰는데 그게 오래됐거나. 어느 경우든 증상은 같다. 한 시간이 조용히 어긋난다.

에러도 안 나고 로그도 안 남는다. 계산은 성공하고 값만 틀리다.

이름이 나라가 아니라 도시인 이유

시간대 이름은 Asia/Seoul, America/New_York, Europe/Paris처럼 대륙/도시 꼴이다. Asia/Korea가 아니다.

나라 이름을 안 쓴 이유가 둘이다.

나라 하나에 시간대가 여럿인 경우가 많다. 미국은 본토만 넷이다. America/New_York과 America/Chicago는 같은 나라지만 다른 규칙을 따른다.

나라는 생기고 없어지고 이름이 바뀐다. 도시 이름이 더 오래간다. 국경이 바뀌어도 그 자리의 도시는 대개 그대로 있다.

도시 이름은 가장 인구가 많은 도시를 고르는 것이 관례일 뿐, 그 도시만의 시각이라는 뜻은 아니다. 부산도 Asia/Seoul이다. 이름을 지역 대표로 읽으면 된다.

약어는 믿을 게 못 된다

KST, EST, CST 같은 세 글자 약어는 사람이 읽기엔 편하지만 저장하거나 주고받는 값으로는 못 쓴다.

가장 유명한 사고가 CST다.

CST가 가리킬 수 있는 것오프셋
미국 중부 표준시-06:00
중국 표준시+08:00
쿠바 표준시-05:00

같은 세 글자가 14시간 떨어진 시각을 가리킨다. 게다가 약어는 표준으로 관리되는 목록도 아니라서, 파싱하는 라이브러리마다 해석이 다르다.

약어는 화면에 보여 줄 때만 쓴다. 저장과 전송에는 Asia/Seoul 같은 정식 이름이나 앞 글에서 본 오프셋을 쓴다.

과거의 팻말도 들어 있다

여기서 비유가 깨진다. 공항 벽에는 지금 쓰는 팻말만 붙어 있다. tzdata는 그렇지 않다.

이 데이터베이스에는 과거의 규칙도 전부 들어 있다. 1988년 서울의 오프셋을 물으면 그 시절 서머타임까지 반영한 답이 나온다. 아주 오래된 날짜를 물으면 분 단위로 어중간한 오프셋이 나오기도 한다. 표준시가 생기기 전에는 도시마다 해를 기준으로 시각을 정했기 때문이다.

그래서 아주 오래된 날짜를 다루는 프로그램은 예상 못 한 오프셋을 만난다. 1900년 이전 날짜를 저장하는 계보 서비스나 역사 자료 시스템이라면 이걸 시험해 봐야 한다.

실무에서: 목록을 최신으로 유지하는 것도 일이다

시간대는 코드 문제이면서 운영 문제다. 코드가 완벽해도 데이터가 낡으면 틀린다.

  • 컨테이너 이미지에 시간대 데이터가 들어 있는지 확인한다. 가벼운 베이스 이미지는 이걸 빼는 경우가 많다. 빠져 있으면 지역 이름을 못 알아보거나 전부 UTC로 떨어진다
  • 런타임이 자기 사본을 쓰는지 OS 것을 쓰는지 확인한다. 자바처럼 자체 사본을 들고 다니는 쪽은 런타임을 올려야 목록이 갱신된다
  • 서버 기본 시간대에 기대지 않는다. 코드가 지역을 명시하면 서버 설정이 무엇이든 결과가 같다. 서버 설정에 기대면 내 컴퓨터에선 되는데가 시간으로 나타난다
  • 저장은 지역 이름으로 한다. 사용자의 시간대를 저장할 일이 있으면 +09:00이 아니라 Asia/Seoul을 저장한다. 규칙이 바뀌어도 따라간다

사용자의 시간대를 어떻게 아느냐도 문제다. 브라우저가 알려 주는 값을 쓰거나 프로필에서 직접 고르게 한다. IP로 추측하는 방식은 여행이나 VPN에서 곧장 틀린다. 알림처럼 시각이 중요한 기능이라면 사용자가 직접 정하게 두는 편이 낫다.

정리

  • 시간대는 지리가 아니라 행정이다. 나라가 정하고 수시로 바꾼다.
  • 그래서 규칙은 상수가 아니라 바깥에서 받아 오는 데이터(tzdata)다. 1년에 여러 번 갱신된다.
  • 이 사슬은 한 군데만 낡아도 끊기고, 증상은 에러 없이 한 시간이 어긋나는 것이다.
  • 이름이 도시인 건 나라 하나에 시간대가 여럿이고 도시가 더 오래가기 때문이다.
  • 약어는 저장·전송에 쓰지 않는다. CST 하나가 14시간 떨어진 세 시각을 가리킨다.
  • 목록에는 과거 규칙도 들어 있어 오래된 날짜는 예상 밖 오프셋이 나온다.

다음 글은 팻말이 바뀌는 순간을 확대해 본다. 1년에 두 번, 아예 존재하지 않는 시각과 두 번 오는 시각이 생긴다.