← 블로그 목록
71 웹 개발·유지보수

예약 시간대와 DST 예외를 등록 전에 검증하는 방법

예약 서비스는 현지 시각을 UTC로 바꾸는 것만으로 충분하지 않습니다. IANA 시간대와 DST의 존재하지 않는 시각·두 번 존재하는 시각을 구분하고, 사용자 선택과 재확인 기록까지 남기는 방법을 설명합니다.

예약 시간대와 DST 예외를 등록 전에 검증하는 방법 대표 이미지

예약 시간을 안전하게 저장하려면 사용자가 입력한 현지 날짜·시각, IANA 시간대, 실제 실행할 UTC 순간을 분리해 검증해야 합니다. 특히 서머타임 전환일에는 현지 시각이 존재하지 않거나 두 번 나타날 수 있으므로, 변환 함수가 값을 만들어냈다는 이유만으로 등록을 완료하면 안 됩니다. 애플리케이션이 모호성을 발견하면 새 시각을 요청하거나 두 선택지를 보여주고, 사용자의 결정을 중간 기록으로 남겨야 합니다.

저장할 값은 ‘UTC 한 개’가 아니라 원래의 현지 입력과 시간대, 그리고 그 입력에서 확정된 순간의 조합입니다.

한 예약에 세 가지 시간을 기록한다

UTC 순간은 알림·결제·작업 실행의 기준이 됩니다. 반면 원래 시간대와 현지 입력은 사용자가 어떤 지역의 시계를 기준으로 예약했는지 설명합니다. PostgreSQL의 시간대 포함 타임스탬프는 내부적으로 UTC로 저장되고 출력 시 세션 시간대로 변환되며, 원래 입력 시간대는 보존하지 않습니다. 따라서 DB 컬럼 하나에 모든 의미를 맡기지 말고 애플리케이션 계약에서 다음 값을 분리하세요.PostgreSQL 날짜·시간 자료형 문서도 이 저장·표시 차이를 설명합니다.

  • local_date와 local_time: 사용자가 선택한 현지 달력 날짜와 시각

  • time_zone: America/New_York처럼 규칙을 가진 IANA 이름

  • resolved_at_utc와 chosen_offset: 검증을 마친 실행 순간과 확정 오프셋

  • resolution_status: valid, nonexistent, ambiguous, confirmed 같은 판정 상태

현지 날짜·시각과 IANA 시간대를 DST 판정으로 거쳐 UTC 순간과 선택 기록으로 연결하는 흐름

입력 단계에서 ‘없음’과 ‘두 번’을 먼저 찾는다

시간대 이름만으로는 충분하지 않습니다. 같은 현지 시각이라도 날짜에 따라 오프셋이 바뀌므로 날짜와 시각을 함께 해석해야 합니다. IANA 시간대 데이터베이스는 정치적 결정에 따른 UTC 오프셋과 서머타임 규칙 변경을 주기적으로 반영합니다. 예약 입력을 처리할 때는 서버의 기본 시간대나 브라우저의 약어를 추측하지 말고, 클라이언트가 보낸 IANA 이름을 허용 목록과 실제 시간대 데이터로 확인하세요.IANA Time Zone Database는 이 규칙 데이터가 지역별 현지 시간 계산을 위해 갱신된다고 설명합니다.

  1. 날짜·시각의 문법과 서비스 운영 시간 범위를 검사한다.

  2. IANA 시간대에서 해당 현지 시각의 유효성·가능한 오프셋 수를 조회한다.

  3. 가능한 순간이 하나면 UTC로 확정하고, 없으면 새 시각을 요청한다.

  4. 두 순간이면 오프셋 또는 ‘첫 번째·두 번째’ 선택을 사용자에게 확인받는다.

예시: 2026년 뉴욕 전환일

가상 입력 A는 `2026-03-08 02:30`, `America/New_York`입니다. 이날 시계는 02:00에서 03:00으로 이동하므로 02:30이라는 현지 순간은 존재하지 않습니다. 결정은 값을 03:30으로 조용히 밀어 넣는 것이 아니라 `nonexistent`로 보류하고 사용자가 새 시각을 선택하게 하는 것입니다. PostgreSQL은 이런 DST 전환 중 존재하지 않는 타임스탬프를 오류로 거부하지 않고 오프셋을 추론해 값을 만들 수 있으므로, DB 삽입 성공은 입력 검증 통과의 증거가 아닙니다. 자세한 동작은 PostgreSQL의잘못되거나 모호한 타임스탬프 처리 문서에서 확인할 수 있습니다.

가상 입력 B는 `2026-11-01 01:30`, `America/New_York`입니다. 이번에는 시계가 되돌아가 01:30이 두 번 나타납니다. 사용자가 `-04:00`을 선택하면 결과는 `2026-11-01T05:30:00Z`, `-05:00`을 선택하면 `2026-11-01T06:30:00Z`입니다. 입력에 대한 결정은 `ambiguous` 판정을 표시하고 두 오프셋을 함께 제시하는 것이며, 중간 산출물은 다음과 같은 선택 기록입니다: `{local_date: "2026-11-01", local_time: "01:30", time_zone: "America/New_York", chosen_offset: "-05:00", resolved_at_utc: "2026-11-01T06:30:00Z", resolution_status: "confirmed"}`. 기대 결과는 실행 시각과 사용자가 확인한 현지 표현을 다시 표시할 수 있는 것입니다.

뉴욕 DST 전환일의 존재하지 않는 02시 30분과 두 번 존재하는 01시 30분을 비교한 판정표

약어와 고정 오프셋을 같은 의미로 취급하지 않는다

`EST`나 `PST` 같은 약어를 사용자의 시간대 선택값으로 저장하면 지역 규칙과 과거 의미를 잃을 수 있습니다. PostgreSQL 문서에서 약어는 특정 UTC 오프셋을 뜻하는 입력으로 설명되지만, 같은 약어가 지역과 시기에 따라 다른 오프셋을 가리킨 사례도 있습니다. 그러므로 서비스의 선택 UI에는 IANA 이름을 사용하고, 약어를 받아야 한다면 해석된 오프셋과 입력 원문을 함께 기록한 뒤 정책상 허용할지 결정하세요. ‘약어는 언제나 고정’이라고 일반화할 수 없습니다.

한 번의 예약과 반복 일정은 다른 계약이다

반복 일정을 설명하기 전에 시간의 의미를 하나로 선택해야 합니다. 이 글의 기준은 ‘매주 지점의 현지 벽시계 시각’을 지키는 wall-clock 방식입니다. 예를 들어 매주 월요일 09:00 America/New_York라면 DST 뒤에도 현지 09:00에 실행하고, 발생할 때마다 IANA 규칙으로 UTC를 다시 계산합니다. 고정 UTC 방식은 매주 같은 UTC 순간을 지키고, 고정 오프셋 방식은 -05:00 같은 오프셋을 지키므로 각각 다른 상품 계약입니다. 셋을 섞은 채 시작 시점의 UTC에 7일을 더하면 안 됩니다.

단발 예약은 확정된 UTC 순간과 원래 현지 표현을 보존합니다. 반대로 미래 지점의 현지 시각을 지키기로 했다면 단발 예약도 규칙 변경 시 재확인 대상입니다. 반복 항목에는 선택한 의미(wall_clock, fixed_utc, fixed_offset), 현지 요일·시각, IANA 시간대 또는 오프셋과 다음 발생 시점의 판정 상태를 저장하고, 규칙이 바뀌면 영향받는 발생분을 검토 대상으로 올립니다. 규칙 변경을 감지했다고 전체 미래 예약을 자동으로 전역 이동하지 말고, 영향받는 사용자에게 변경 전·후 시각을 보여준 뒤 재확인이나 취소를 받으세요.

등록 전후의 실패 경로를 명시한다

검증 실패는 모두 같은 오류가 아닙니다. 존재하지 않는 시각은 새 입력을 요구하고, 모호한 시각은 선택을 요구하며, 알 수 없는 시간대는 허용 목록에서 다시 고르게 해야 합니다. 저장 이후 시간대 데이터가 갱신되거나 작업 큐가 재시도되면 원래 선택 기록과 현재 계산 결과를 비교해 재확인 상태로 되돌릴 수 있어야 합니다.

  • 입력 보류: resolution_status와 검증 사유를 저장하고 실행 작업을 만들지 않는다.

  • 사용자 선택: chosen_offset, 선택 시각, 확인 주체를 기록한 뒤에만 예약을 확정한다.

  • 규칙 변경 감지: 영향을 받는 발생분을 재계산하고 사용자 재확인 전에는 자동 실행하지 않는다.

  • 실행 전 불일치: 이전 확정 UTC를 덮지 말고 예약을 일시 중지해 원래 선택과 새 계산을 비교한다.

예약 시간의 재확인 조건은요구사항 정의서에서 먼저 합의하고, 외부 예약 시스템에는 현지 입력과 UTC 값의데이터 계약을 함께 전달하세요.

검증 완료를 판정하는 기록

예약 등록은 UTC 값이 생긴 순간 끝나지 않습니다. local_date·local_time·IANA time_zone·resolved_at_utc·chosen_offset·resolution_status가 한 기록에서 연결되고, `nonexistent` 입력이 실행 큐에 들어가지 않으며, `ambiguous` 입력에 사용자의 선택이 남아 있어야 합니다. 반복 일정이라면 선택한 `wall_clock`·`fixed_utc`·`fixed_offset` 계약과 규칙 데이터 갱신으로 영향을 받은 발생분, 재확인 결과가 함께 식별되어야 합니다. 이 조건을 테스트 데이터와 실제 저장 행으로 읽어낼 수 있을 때 시간대 검증이 완료됐다고 판정하세요.