자본시장과 결제 · 거래일·세션 시간·시간대 · 이론
오늘이 언제인지가 시스템마다 다르다
한 줄 요약
거래일은 달력일이 아니고, 세션은 시장마다 다르며, 서머타임이 있는 시장에서는 같은 지역시가 두 번 나오거나 아예 없다. 이 셋을 고정 오프셋으로 뭉개 놓으면 한 해에 두 번, 전환 주말마다 조용히 틀린다.
왜 이게 필요했나
시각을 다루는 코드는 대개 두 번 쓰인다. 처음 쓸 때는 잘 돌고, 전환 주말에 한 번 고쳐진다. 그 사이의 몇 달 동안 아무도 문제를 모르는 이유는, 틀린 값이 예외를 던지지 않고 그냥 한 시간 어긋난 채로 저장되기 때문이다.
자본시장에서 이 어긋남은 곧바로 돈과 보고에 닿는다. 마감 배치가 한 시간 일찍 돌면 그 사이의 체결이 다음 날로 밀리고, 대사는 어긋난 채로 맞춰진다. 그리고 시장마다 전환 날짜가 다르다. 미국 시장이 이미 여름 시각으로 옮겨 간 뒤에도 유럽 시장은 아직 겨울 시각인 몇 주가 해마다 있고, 그 몇 주 동안 두 시장의 마감 간격은 평소와 다르다. 이 사실을 시스템이 몰라도 되는 이유는 없다.
어떻게 동작하나
첫째, 오프셋이 아니라 규칙으로 바꾼다. 지역시를 UTC 로 바꾸는 일은 "아홉 시간을 뺀다" 가 아니다. 그 지역이 그 날짜에 어떤 규칙 아래 있었는지를 봐야 한다. 그 규칙의 원본은 [IANA 시간대 데이터베이스](https://www.iana.org/time-zones)이고, 파이썬에서는 [zoneinfo](https://docs.python.org/3/library/zoneinfo.html)가 그것을 읽는다. 이 모듈이 표준에 들어온 배경은 [PEP 615](https://peps.python.org/pep-0615/)에 적혀 있다. 고정 오프셋을 코드에 박으면 전환일 이후의 모든 기록이 한 시간씩 어긋난다.
둘째, 전환일에는 벽시계가 성립하지 않는 순간이 있다. 봄에 시계를 앞당기는 시장에서는 건너뛴 한 시간이 통째로 존재하지 않는다. 그 시각이 적힌 기록은 지역시로 읽을 수 없고, "일단 변환해서 저장" 하면 조용히 한 시간 뒤의 값이 된다. 가을에 되돌리는 시장에서는 같은 벽시계 값이 두 번 지나가므로, 그 시각만으로는 두 순간 중 어느 쪽인지 정할 수 없다. 파이썬은 이 두 번째 경우를 fold 속성으로 구분하지만, 어느 쪽을 쓸지는 업무가 정해야 하는 규칙이지 라이브러리가 정해 주는 것이 아니다.
셋째, 세션과 달력은 별도의 자료다. 정규장 앞뒤에 개장 전과 개장 후 구간이 있고, 반일장이 있고, 휴장일이 있다. 이것을 코드에 상수로 박으면 해가 바뀔 때마다 배포가 필요해진다. 달력을 자료로 두면 그 자료 자체를 검증할 수 있다는 이점도 생긴다. 반일장 마감이 정규 마감보다 늦게 적혀 있거나, 같은 날이 휴장일이면서 반일장으로도 적혀 있는 일은 실제로 일어난다.
넷째, 거래일은 라벨이다. 자정을 넘는 야간장에서 새벽 한 시의 체결은 달력으로는 오늘이지만 거래일로는 어제다. 이 라벨을 정하지 않으면 같은 체결이 어느 날의 집계에 들어갈지 시스템마다 달라지고, 대사는 영원히 맞지 않는다. 표기 자체의 규칙은 [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339)가 정하지만, 거래일을 무엇으로 볼지는 시장의 규칙이다.
현장에서 만나는 모습
가장 흔하게 보는 사고는 전환 주말 직후의 마감 배치다. 어떤 시장의 마감을 UTC 로 고정해 두면 전환 뒤에는 그 시장이 아직 열려 있는 시각에 배치가 돈다. 반대로 지역시로만 잡아 두면 다른 시장의 배치와 같은 순간에 겹쳐 두 작업이 같은 자원을 두고 부딪힌다. 실제로 두 시장의 마감이 여름 시각 동안 정확히 같은 UTC 시각에 놓이는 구간이 있고, 그 몇 주 동안만 배치가 밀린다.
두 번째로 흔한 것은 오프셋 없는 시각 문자열이다. 로그와 전문에 2025-03-30 01:30:00 만 적혀 있으면, 그것이 어느 시간대인지는 문서에서 가져와야 한다. 그리고 그 시각이 그 시간대에 존재하지 않을 수도 있다는 것까지 다뤄야 한다.
세 번째는 달력이 코드 안에 있는 경우다. 휴장일과 반일장을 상수로 박아 두면 해가 바뀔 때마다 배포가 필요하고, 급하게 고치다 보면 그 값이 어디에서 왔는지 아무도 모르게 된다. 달력을 자료로 빼 두면 그때부터 달력 자체를 검증할 수 있다. 같은 날이 휴장일이면서 반일장으로도 적혀 있거나, 반일장 마감이 정규 마감보다 늦게 적혀 있는 일은 실제로 일어나고, 그런 줄 하나가 그 날 하루의 집계를 통째로 바꾼다. 이 검증은 몇 줄이면 되는데, 달력이 코드 안에 있으면 그 몇 줄을 쓸 자리가 없다.
그리고 이 모든 처리에는 공통의 원칙이 하나 있다. 시각은 받는 즉시 한 번만 해석하고, 그 뒤로는 절대 시각으로만 다닌다. 지역시 문자열을 그대로 들고 다니며 필요할 때마다 해석하면, 같은 값이 시스템의 층마다 다르게 읽힌다. 화면에 보여 줄 때만 다시 지역시로 바꾸면 되고, 그때는 어느 시장의 지역시인지가 그 화면의 맥락에서 정해진다.
다음 실습에서 할 것
네 시장의 세션 정의와 달력, 서른 건의 체결을 만들고 시간대 규칙으로 UTC 로 바꿉니다. 고정 오프셋으로 바꾼 결과와 견주어 어긋난 날을 찾고, 전환일의 없는 시각과 두 번 있는 시각을 가른 뒤, 세션 구간과 거래일 라벨을 매깁니다. 마지막에 시장별 마감을 UTC 로 모아 배치 창이 겹치는 날을 찾고, 달력 자체의 모순 두 가지를 찾아냅니다.