시간대가 복잡한 이유
시간대는 겉으로 보면 단순합니다. 지구를 24개 구역으로 나누고 각각 UTC에서 정수 시간만큼 오프셋을 주면 됩니다. 하지만 현실은 훨씬 복잡합니다. 활성 사용 중인 시간대 식별자는 600개가 넘습니다. 오프셋이 30분이나 15분 단위인 경우도 있고, 나라마다 규칙을 바꾸며, 서머타임 때문에 같은 시간대도 시기에 따라 오프셋이 달라집니다.
UTC: 전 세계의 기준점
협정 세계시(UTC)는 세계가 기준으로 사용하는 표준 시간입니다. 특정 지역에 묶여 있지 않고 서머타임도 없습니다. 다른 모든 시간대는 UTC로부터의 오프셋으로 정의됩니다. UTC+5:30은 인도 표준시, UTC-5:00는 미 동부 표준시(겨울)입니다.
데이터베이스에 타임스탬프를 저장하거나 시스템 간 전송할 때는 항상 UTC를 사용하세요. 로컬 시간으로의 변환은 표시 시점에만 하세요. 이 원칙 하나만 지켜도 시간대 관련 버그의 상당 부분을 막을 수 있습니다.
UTC 오프셋 vs. IANA 시간대 이름
`UTC+9`라는 오프셋은 UTC와의 현재 차이를 알려주지만, 어느 시간대인지는 알려주지 않습니다. 일본과 한국은 모두 UTC+9를 쓰지만, 일본은 서머타임을 적용하지 않고 한국은 1988년에 폐지했습니다. 오프셋만 저장하면 서머타임 전환 시 미래 시간을 정확하게 처리할 수 없습니다.
IANA 시간대 데이터베이스는 `America/New_York`, `Europe/London`, `Asia/Tokyo` 같은 이름 식별자를 제공합니다. 이 이름들은 현재 오프셋뿐만 아니라 서머타임 규칙을 포함한 모든 오프셋 변경 이력을 담고 있습니다. 미래 시간을 다루는 소프트웨어는 항상 IANA 이름을 사용해야 합니다.
서머타임: 버그의 단골 원인
서머타임(DST)은 봄에 시계를 앞으로, 가을에 뒤로 조정합니다. 약 70개국이 시행하지만 시작·종료 날짜가 제각각이며 규칙도 여러 번 바뀌었습니다.
미국에서는 3월 두 번째 일요일(새벽 2시→3시)에 시작해 11월 첫 번째 일요일(새벽 2시→1시)에 종료됩니다. 봄엔 한 시간이 사라지고, 가을엔 한 시간이 반복됩니다.
바로 그 '반복되는 한 시간'에 버그가 숨습니다. 로컬 시간 기준 새벽 1:30에 스케줄된 작업은 시계가 뒤로 돌아가는 날 밤 두 번 실행됩니다. UTC 기반 스케줄링을 쓰면 이 문제가 완전히 사라집니다.
자주 하는 실수들
**실수 1: 오프셋이 항상 정수 시간이라고 가정하기.** 인도는 UTC+5:30, 네팔은 UTC+5:45입니다. 수동 산술 대신 제대로 된 라이브러리를 쓰세요.
**실수 2: UTC+0을 UTC로 취급하기.** 영국은 `Europe/London`을 쓰는데, 겨울엔 UTC+0이지만 여름엔 UTC+1입니다. UTC+0으로 하드코딩하면 일 년 중 절반은 틀립니다.
**실수 3: 시간대 정보 없이 로컬 시간 저장하기.** "2026-03-08 02:30:00"은 시간대를 모르면 모호합니다. 이 시각은 일부 시간대에서 아예 존재하지 않을 수 있습니다.
**실수 4: 시간대 약어 파싱하기.** CST는 북미에서 중부 표준시이지만 다른 맥락에서는 중국 표준시입니다. IANA 이름은 모호하지 않습니다.
실무에서 시간대 다루기
**표시:** 브라우저의 `Intl.DateTimeFormat` API나 `date-fns-tz`, `Luxon` 같은 라이브러리로 UTC 타임스탬프를 로컬 시간으로 변환하세요. 시간대 변환을 절대 하드코딩하지 마세요.
**스케줄링:** 미래 시간을 다룰 때는 IANA 시간대 이름을 함께 저장하세요. "매주 월요일 오전 9시, America/Chicago"처럼 하면 서머타임 전환을 자동으로 처리합니다.
**API:** UTC 오프셋 포함 ISO 8601이 표준입니다. `2026-04-16T14:30:00Z` (Z는 UTC)를 우선 사용하세요.
**Unix 타임스탬프:** 1970-01-01T00:00:00Z 이후의 초 수입니다. 항상 UTC이고 항상 명확합니다. 내부 저장·비교에 완벽하며, 사람이 읽는 형태로의 변환은 UI 레이어에서만 하세요.