장 날짜는 제공자가 이미 들고 있는 사실이다. 그걸 유도하려는 시도는 전부 남의 달력을 다시 구현하는 짓이다.
날짜를 얻는 세 가지 방법, 순위대로
기계 달력이 시장 달력이 아니라는 걸 받아들이고 나면 후보 수선책이 셋인데, 세 번째만 버텨.
고정 오프셋을 뺀다. "뉴욕은 UTC-5 니까 날짜 뽑기 전에 다섯 시간 빼자." 일 년에 두 번, 양방향으로 틀려. 그리고 그 실패가 딱 아무도 안 보는 자리에 몰려. 서머타임 전환 다음 주, 그것도 한 시장에서만.
거래소 달력을 조회한다. 낫지. 그리고 이제 세 나라 거래소 개장 시간이랑 휴장일 테이블을 소유하게 됐고, 그건 흘러가는 두 번째 진실 원본이야. 흘러갔다는 건 어느 시장이 예정에 없던 반장을 하는 날 알게 되고.
제공자한테 묻는다. 시세가 이미 찍힌 순간이랑 거래소 시간대 이름을 달고 있어. 하나를 다른 하나로 통과시키면 거래소 자신이 쓸 장 날짜가 나와. 테이블도, 오프셋도, 표류도 없어. 답이 그게 설명하는 값이랑 같은 데서 오니까.
구현이 실제로 하는 일
함수는 짧고, 재밌는 건 가장자리야. 필드 둘을 읽어 — 마지막 정규장 순간의 에포크 초 타임스탬프랑 거래소 시간대 이름 — UTC 로 aware datetime 을 만들고, 거래소 존으로 변환하고, 날짜를 뽑아.
서버 존으로 변환하지 않는다는 데 주목해. 중간 표현이 UTC 인 건 모호하지 않아서고, 목적지는 거래소야. 이게 무슨 장인지 정의하는 게 거래소니까.
정직함이 사는 자리는 폴백이야
제공자가 조용하면 함수는 None 을 돌려줘. 타임스탬프가 없거나, 존 이름이 없거나, 파싱이 안 되는 경우. 추측을 안 해. 그리고 독스트링은 그러면 호출자가 UTC 날짜로 폴백하되 그렇다고 말한다고 적어놨어. 달력을 지어내는 대신.
그 마지막 절이 이 규율 전체의 축소판이야. 진짜랑 구분이 안 되는 폴백은 폴백이 아니라 조용한 하향이고, 흔적을 안 남기는 하향이 바로 시스템 정확도가 언제 그랬는지 아무도 못 짚는 채로 닳아가는 방식이야.
이 레슨을 처음 쓸 때, 그 절은 구현이 안 돼 있었어. 호출자가 폴백 날짜를 집어넣고 제공자 원래 출처 문자열을 그대로 저장했거든. 그래서 폴백으로 날짜 붙은 행이 제공자가 날짜를 준 행이랑 출처가 바이트 단위로 똑같았어. 추측 대신 None 을 돌려주는 정직한 절반은 진짜였고 하중도 받고 있었는데, 공개하는 절반은 독스트링에만 살았지. 코드가 뭘 하는지 설명하는 주석은 증거가 아니라 주장이다. 그 주석은 인용되고 믿기고 되풀이돼 왔어. 이 레슨의 이전 판까지 포함해서.
그리고 2026-08-31 에 닫혔어
닫는 게 어떻게 생겼는지가 틈 자체보다 배울 게 많아. 고친 사람이 폴백 날짜 를 건드린 게 아니야. 그건 원래 맞았거든. 고친 건 그 옆에 나가는 출처 문자열 이야. 두 호출 경로가 각자 들고 있던 폴백 헬퍼를 없애고, 갈림길 하나를 함수로 만들어서 둘 다 그걸 통과하게 했어. 제공자가 날짜를 말했으면 출처는 손 안 대고 그대로. 안 말했으면 UTC 날짜에 (date:utc-fallback) 를 붙여서 나가. 그게 전부야.
세 가지가 눈에 띄어야 해. 첫째, 고침이 저장 계층까지 내려갔어. 로그 줄이나 메트릭이 아니라 행 자체가 달라졌어. 이 창고의 불변식이 저장하는 모든 값에 출처 인데, 날짜가 둘 중 어느 쪽이냐는 게 바로 출처거든. 둘째, 갈림길이 하나로 모였어. 두 군데서 각자 폴백하고 있었으니 같은 결함이 두 벌 있었던 거고, 한 군데만 고쳤으면 나머지 하나가 조용히 남았을 거야. 셋째, 회귀 테스트가 양쪽 분기를 다 못박았어. 제공자가 날짜를 준 경우엔 출처가 안 붙는다는 것까지. 태그가 붙는지만 검사하는 테스트는 전부 태그를 붙여버리는 구현도 통과시키니까.