2025년 1월 09일·6분 읽기

서머타임(DST) 버그: 타임스탬프와 보고서를 위한 규칙

서머타임(DST) 버그를 피하는 실무 규칙: 타임스탬프는 의미를 잃지 않게 저장하고, 로컬 시간은 사람이 기대하는 방식으로 보여주며, 사용자가 검증할 수 있는 리포트를 만드세요.

서머타임(DST) 버그: 타임스탬프와 보고서를 위한 규칙

일반 제품에서 이런 버그가 발생하는 이유

사람들은 UTC로 살지 않습니다. 사람들은 로컬 시간을 사용하고, 로컬 시간은 앞으로 점프하거나 뒤로 밀리거나 해마다 규칙이 바뀔 수 있습니다. 같은 순간을 두 사용자가 보면 서로 다른 시계를 볼 수 있고, 더 나쁜 경우 같은 로컬 시각이 서로 다른 실제 순간을 가리킬 수 있습니다.

서머타임(DST) 버그는 보통 연중 두 번만 나타나기 때문에 놓치기 쉽습니다. 개발 환경에서는 모든 것이 정상처럼 보이다가 실제 고객이 전환 주말에 약속을 잡거나 근무표를 제출하거나 리포트를 확인하면 뭔가 이상해 보입니다.

팀은 보통 몇 가지 패턴을 먼저 알아차립니다: 일정 항목이 사라지거나 옮겨지는 “사라진 시간”, 로그나 알림이 중복된 것처럼 보이는 “중복된 시간”, 그리고 하루가 23시간 또는 25시간이라서 일일 합계가 밀리는 현상.

이건 개발자 문제만이 아닙니다. 고객지원에는 “앱이 내 회의를 변경했다”라는 티켓이 오고, 재무는 일일 매출이 맞지 않다고 보고하며, 운영팀은 야간 잡이 두 번 실행되거나 건너뛰었다고 놀랍니다. 심지어 “오늘 생성됨” 필터도 지역에 따라 달라질 수 있습니다.

목표는 지루하지만 신뢰할 수 있는 것입니다: 의미를 잃지 않는 방식으로 시간을 저장하고, 사람이 기대하는 방식으로 로컬 시간을 보여주며, 이상한 날에도 진실을 유지하는 리포트를 만드는 것. 그렇게 하면 비즈니스의 모든 부분이 숫자를 신뢰할 수 있습니다.

커스텀 코드로 빌드하든 AppMaster (appmaster.io) 같은 플랫폼을 쓰든 규칙은 같습니다. 원래 순간을 보존하는 타임스탬프와 그 순간이 사용자 시계에 어떻게 보였는지 설명할 수 있는 충분한 컨텍스트(예: 사용자의 시간대)를 원합니다.

시간을 이해하기 위한 간단한 모델

대부분의 DST 버그는 “타임라인상의 순간”과 “벽시계가 표시하는 방식”을 섞어쓰면서 생깁니다. 이 둘을 분리하면 규칙이 훨씬 간단해집니다.

몇 가지 용어(평이한 언어):

  • 타임스탬프: 타임라인상의 정확한 순간(위치와 무관).
  • UTC: 타임스탬프를 일관되게 표현하는 전역 기준 시계.
  • 로컬 시간: 특정 장소의 벽시계가 보여주는 시간(예: 뉴욕의 오전 9시).
  • 오프셋: 특정 순간의 UTC와의 차이, 예: +02:00 또는 -05:00.
  • 시간대: 날짜별로 오프셋을 결정하는 규칙 집합의 이름, 예: America/New_York.

오프셋은 시간대와 같지 않습니다. -05:00은 그 순간의 UTC 차이만 알려줄 뿐, 여름에 -04:00으로 바뀌는지나 다음 해 법률 변경 여부는 알려주지 않습니다. 시간대 이름은 규칙과 역사 정보를 담고 있으므로 그 차이를 설명합니다.

DST는 오프셋을 바꿀 뿐, 실제 타임스탬프를 바꾸지는 않습니다. 사건은 같은 순간에 발생했고, 로컬 시계의 라벨만 바뀝니다.

혼란을 일으키는 주요 두 경우:

  • 봄의 건너뛰기(스프링 스킵): 시계가 앞으로 점프해서 어떤 로컬 시간 범위가 존재하지 않습니다(예: 2:30 AM이 불가능할 수 있음).
  • 가을의 반복(폴 리피트): 시계가 한 시간을 반복해서 같은 로컬 시간이 두 번 발생합니다(예: 1:30 AM이 모호해짐).

가을 반복 동안 “1:30 AM”에 지원 티켓이 생성되면, 사건을 정확히 정리하려면 시간대와 정확한 순간(UTC 타임스탬프)이 필요합니다.

대부분의 문제를 예방하는 데이터 규칙

대부분의 DST 버그는 형식 문제보다 데이터 문제에서 시작합니다. 저장된 값이 불분명하면 이후의 모든 화면과 리포트가 추측을 해야 하고, 그 추측은 서로 달라집니다.

규칙 1: 실제 사건은 절대 순간(UTC)으로 저장하세요

지불이 처리되었거나 티켓에 답변이 달렸거나 근무가 시작된 등 특정 순간에 일어난 일은 UTC로 저장하세요. UTC는 앞뒤로 점프하지 않으므로 DST 변경에도 안정적입니다.

예: 뉴욕의 지원 담당자가 시계 변경 날 오전 9시 15분에 답변을 했다면, 그 순간을 UTC로 저장하면 런던에 있는 사람이 나중에 스레드를 볼 때도 순서가 올바르게 유지됩니다.

규칙 2: 시간대 컨텍스트는 IANA 시간대 ID로 보관하세요

사람이 이해할 수 있는 방식으로 시간을 보여주려면 사용자의 또는 위치의 시간대를 알아야 합니다. America/New_York 또는 Europe/London 같은 IANA 시간대 ID로 저장하세요. “EST” 같은 모호한 표시나 오프셋만으로는 DST 규칙을 담을 수 없습니다.

간단한 패턴: 이벤트 시각은 UTC로, 사용자나 사무실, 지점, 기기에는 별도로 시간대 ID를 붙입니다.

규칙 3: 날짜만 필요한 값은 날짜로 저장하세요(타임스탬프 아님)

생일, “매달 5일 갱신”, 인보이스 마감일 등은 순간이 아닙니다. 날짜 전용 필드로 저장하세요. 타임스탬프로 저장하면 시간대 변환 때문에 전날이나 다음날로 옮겨질 수 있습니다.

규칙 4: 시간대 컨텍스트 없이 로컬 시간을 문자열로만 저장하지 마세요

“2026-03-08 02:30”이나 “9:00 AM”처럼 시간대 없이 저장하는 것을 피하세요. DST 전환 시 이런 시간은 모호하거나(두 번 발생) 아예 불가능할 수 있습니다.

로컬 입력을 받아야 한다면, 로컬 값과 시간대 ID를 모두 저장하고 경계에서(예: API나 폼 제출 시) UTC로 변환하세요.

각 레코드 유형별로 무엇을 저장할지 결정하기

많은 DST 버그는 한 레코드 유형을 다른 유형처럼 취급하면서 발생합니다. 감사 로그 항목, 캘린더 미팅, 급여 마감 등은 모두 “날짜와 시간처럼 보이지만” 서로 다른 데이터가 필요합니다.

과거 사건(이미 발생한 일)의 경우: 보통 정확한 순간, 즉 UTC 타임스탬프를 저장하세요. 사용자가 그때 어떻게 보았는지를 설명해야 한다면, 사건 당시의 사용자의 시간대(IANA ID)를 함께 저장하세요. 그래야 사용자가 나중에 프로필 시간대를 바꿔도 화면에 무엇이 보였는지 재구성할 수 있습니다.

스케줄링(로컬 벽시계 시간에 일어나야 하는 일)의 경우: 의도한 로컬 날짜와 시간, 그리고 시간대 ID를 저장하세요. 이를 UTC로만 변환해 원본을 버리면 규칙이 바뀔 때 의미를 잃게 됩니다. 예: “Europe/Berlin의 3월 10일 09:00”은 의도입니다. UTC는 파생값입니다.

사람이 이동하거나 사무실이 이전하거나 정책이 바뀌는 건 정상입니다. 과거 기록은 사용자가 프로필 시간대를 바꿀 때 수정하지 마세요. 미래 일정의 경우, 일정이 사용자를 따라가야 하는지(여행 친화적) 아니면 고정 위치를 따라야 하는지(오피스 친화적)를 결정하고 그 위치의 시간대를 저장하세요.

과거 데이터가 로컬 타임스탬프만 가지고 있는 레거시 시스템은 까다롭습니다. 출처 시간대를 알고 있다면 그 시간대를 붙여 로컬로 취급하세요. 모른다면 “플로팅(floating)”으로 표시하고 리포트에서 정직하게(예: 저장된 값을 변환 없이 표시) 알리세요. 또한 이런 값들을 별도 필드로 모델링하면 화면과 리포트가 실수로 섞어 쓰는 걸 방지할 수 있습니다.

타임스탬프를 안전하게 저장하는 단계별 가이드

Make time rules repeatable
Turn your DST checklist into reusable components your team can apply to every feature.
Try AppMaster

DST 버그를 멈추려면 하나의 불분명하지 않은 기록 체계를 선택하고, 사람에게 보여줄 때만 변환하세요.

팀 규칙을 문서화하세요: 데이터베이스의 모든 타임스탬프는 UTC다. 문서와 날짜 처리 코드 근처의 주석에 적어 두세요. 이런 결정은 시간이 지나며 실수로 뒤집히기 쉽습니다.

실용적인 저장 패턴 예시:

  • 시스템 기록 기준으로 UTC를 선택하고 필드 이름을 명확히 하세요(예: created_at_utc).
  • 실제로 필요한 필드를 추가하세요: 이벤트 시간(예: occurred_at_utc)과 로컬 컨텍스트가 중요하면 tz_id(IANA 시간대 ID).
  • 입력을 받을 때는 로컬 날짜와 시간, tz_id를 수집하고 경계에서 한 번만 UTC로 변환하세요(API나 폼 제출 시). 여러 층에서 여러 번 변환하지 마세요.
  • 저장과 쿼리는 UTC로 하세요. 로컬 시간 변환은 UI, 이메일, 내보내기 등 경계에서만 하세요.
  • 결제, 규정 준수, 스케줄링 같은 중요한 행동은 수신한 원값(원래 로컬 문자열, tz_id, 계산된 UTC)도 로깅하세요. 분쟁 시 감사 추적을 제공해 줍니다.

예: 사용자가 America/Los_Angeles에서 “11월 5일 9:00 AM”을 예약하면 occurred_at_utc = 2026-11-05T17:00:00Ztz_id = America/Los_Angeles를 저장하세요. 이후 DST 규칙이 바뀌어도 사용자가 의미한 바와 저장값을 설명할 수 있습니다.

PostgreSQL로 모델링할 경우(시각적 데이터 모델링 도구 포함), 컬럼 타입을 명시적으로 통일하고 애플리케이션이 항상 UTC로 쓰도록 강제하세요.

사용자가 이해할 수 있는 로컬 시간 표시하기

Fix time at the schema
Design clear timestamp and date-only fields in a visual PostgreSQL data model.
Model Data

대부분의 DST 버그는 데이터베이스가 아니라 UI에서 드러납니다. 사용자는 화면에 보이는 것을 읽고 메시지에 복사하고 그에 따라 계획합니다. 화면이 불분명하면 사용자는 잘못 가정합니다.

시간이 중요한 곳(예약, 티켓, 약속, 배송 시간대 등)에서는 영수증처럼: 완전하고, 구체적이며, 라벨을 달아 보여주세요.

표시는 예측 가능하게 유지하세요:

  • 날짜 + 시간 + 시간대 표시(예: “Mar 10, 2026, 9:30 AM America/New_York”).
  • 시간대 라벨은 시간 옆에 두고 설정 속에 숨기지 마세요.
  • 상대 문구(“2시간 후”)를 보여줄 때는 정확한 타임스탬프도 함께 두세요.
  • 공유 항목에서는 뷰어의 로컬 시간과 이벤트의 시간대를 둘 다 보여주는 것을 고려하세요.

DST 엣지케이스는 명시적 동작이 필요합니다. 사용자가 임의로 시간을 입력하게 하면 언젠가는 존재하지 않거나 두 번 발생하는 시간을 받게 됩니다.

  • 스프링-포워드(존재하지 않는 시간): 유효하지 않은 선택을 차단하고 다음 가능한 시간으로 안내하세요.
  • 폴-백(모호한 시간): 오프셋을 함께 보여주거나 명확한 선택(예: “1:30 AM UTC-4” vs “1:30 AM UTC-5”)을 제공하세요.
  • 기존 레코드 편집: 포맷이 바뀌어도 원래 순간(instant)을 보존하세요.

예: 베를린의 지원 담당자가 뉴욕 고객과 “11월 3일 1:30 AM”으로 통화를 잡는다면, 뉴욕은 폴-백에서 이 시간이 두 번 발생할 수 있습니다. UI가 “Nov 3, 1:30 AM (UTC-4)”처럼 보여주면 혼란이 사라집니다.

거짓말하지 않는 리포트 만들기

리포트는 같은 데이터를 놓고 보는 사람에 따라 합계가 달라지면 신뢰를 잃습니다. DST 버그를 피하려면 리포트가 실제로 무엇을 기준으로 묶는지 결정하고 그것을 지키세요.

먼저 각 리포트에서 “하루”가 무슨 의미인지 정하세요. 고객지원팀은 종종 고객의 로컬 날짜를 기준으로 생각하고, 재무는 계정의 법적 시간대를 필요로 하며, 기술 리포트는 UTC 날짜가 가장 안전할 수 있습니다.

로컬 날짜로 그룹하면 DST 주변에서 합계가 바뀔 수 있습니다. 스프링-포워드 날짜에는 한 시간이 사라지고, 폴-백 날짜에는 한 시간이 반복됩니다. 로컬 날짜로 묶되 명확한 규칙이 없다면 바쁜 시간이 사라지거나 중복되어 잘못된 날로 보일 수 있습니다.

실용적 규칙: 모든 리포트는 하나의 리포팅 시간대를 가지고 있으며 그 시간대를 헤더에 표시하세요(예: “모든 날짜는 America/New_York 기준”). 이렇게 하면 계산이 예측 가능해지고 지원팀이 지적할 근거가 생깁니다.

다지역 팀의 경우 사용자가 리포트 시간대를 바꿀 수 있게 해도 됩니다. 다만 그것은 같은 진실의 다른 보기로 취급해야 합니다. 전환 시점 근처의 일일 버킷은 서로 다르게 보일 수 있습니다. 그건 선택한 시간대를 명확히 표시하면 정상입니다.

몇 가지 선택으로 대부분의 놀라움을 막을 수 있습니다:

  • 리포트의 일 경계(사용자 존, 계정 존, 또는 UTC)를 정의하고 문서화하세요.
  • 리포트 실행마다 하나의 시간대를 사용하고, 날짜 범위 옆에 표시하세요.
  • 일일 합계는 선택한 존의 로컬 날짜로 그룹하세요(UTC 날짜로 그룹하지 말 것).
  • 시간별 차트는 폴-백 날의 반복 시간을 레이블링하세요.
  • 기간(듀레이션)은 경과 초를 저장하고 합산한 뒤 표시하세요.

기간 계산은 특별히 주의가 필요합니다. 폴-백을 가로지르는 “2시간 교대”는 벽시계로는 3시간처럼 보일 수 있지만 실제로 일한 시간은 2시간일 수 있습니다. 사용자들이 어떤 의미를 기대하는지 결정한 뒤(예: 급여는 경과 시간) 일관된 반올림 규칙을 적용하세요(예: 합산 후 반올림).

흔한 함정과 회피 방법

Make time readable
Add UI labels that show date, time, and zone so users never have to guess.
Prototype Now

DST 버그는 복잡한 수학 문제가 아닙니다. 시간이 지나며 스며드는 작은 가정들이 원인입니다.

고전적 실패 사례는 로컬 타임스탬프를 저장해놓고 그것을 UTC로 라벨링하는 것입니다. 그때까지는 모든 것이 괜찮아 보이다가 다른 시간대 사람이 기록을 열면 조용히 한 시간 이동합니다. 안전한 규칙은 간단합니다: 인스턴트(UTC) 필드와 로컬 해석이 필요한 경우의 시간대 컨텍스트를 함께 저장하세요.

또 다른 자주 발생하는 원인은 -05:00 같은 고정 오프셋을 사용하는 것입니다. 오프셋은 DST 변경이나 과거 규칙을 알지 못합니다. 실제 IANA 시간대 ID(America/New_York 등)를 사용하면 시스템이 날짜별로 올바른 규칙을 적용할 수 있습니다.

몇 가지 습관이 많은 '이중 근무' 놀라움을 예방합니다:

  • 경계에서만 변환하세요: 입력 한 번 파싱, 저장 한 번, 표시 한 번.
  • “인스턴트” 필드(UTC)와 “벽시계” 필드(로컬 날짜/시간)를 명확히 분리하세요.
  • 로컬 해석이 필요한 레코드에는 시간대 ID를 함께 저장하세요.
  • 서버 시간대를 무시하고 항상 UTC로 읽고 쓰게 하세요.
  • 리포트에는 리포트 시간대를 정의하고 UI에 표시하세요.

숨겨진 변환에도 주의하세요. 흔한 패턴은 사용자의 로컬 시간을 UTC로 파싱해서 저장한 뒤 UI 라이브러리가 그 값을 로컬로 가정하고 다시 변환하는 경우입니다. 결과는 일부 사용자와 일부 날짜에서만 나타나는 한 시간의 불일치입니다.

마지막으로, 청구나 규정 준수 같은 중요한 용도에 클라이언트 장치의 시간대를 사용하지 마세요. 여행 중인 사용자의 폰은 중간에 시간대가 바뀔 수 있습니다. 대신 명확한 비즈니스 규칙(예: 고객 계정 시간대나 사이트 위치)을 기준으로 삼으세요.

테스트: 대부분의 버그를 잡는 몇 가지 사례

대부분의 시간 버그는 1년에 몇 번만 나타나므로 QA를 통과하기 어렵습니다. 해결책은 올바른 순간을 테스트하고 그 테스트를 반복 가능하게 만드는 것입니다.

DST를 관찰하는 한 시간대(예: America/New_York 또는 Europe/Berlin)를 골라 전환일 두 날을 테스트하고, DST를 쓰지 않는 시간대(예: Asia/Singapore 또는 Africa/Nairobi)도 골라 대비 테스트하세요.

영원히 보관할 가치가 있는 5가지 테스트

  • 스프링-포워드 날: 존재하지 않는 시간을 예약할 수 없도록 하고 변환이 존재하지 않는 시간을 만들어내지 않는지 확인하세요.
  • 폴-백 날: 중복된 시간을 테스트하여 두 개의 서로 다른 UTC 순간이 같은 로컬 시간으로 표시될 때 구분이 가능한지 확인하세요(로그와 내보내기에서 구별 가능해야 함).
  • 자정 경계 넘김: 로컬 시간으로 자정을 넘는 이벤트를 만들고 UTC로 보았을 때 정렬과 그룹화가 올바른지 확인하세요.
  • 비-DST 대비: 비-DST 존에서 같은 날짜에 변환을 반복해 결과가 안정적인지 확인하세요.
  • 리포트 스냅샷: 월말 및 DST 주말 주변의 예상 총합을 저장해 두고 변경 후마다 출력 비교하세요.

구체적 시나리오

지원 팀이 폴-백 밤에 “01:30” 팔로업을 예약한다고 합시다. UI가 표시된 로컬 시간만 저장하면 어떤 “01:30”을 의도했는지 알 수 없습니다. 좋은 테스트는 로컬 01:30에 매핑되는 두 개의 UTC 타임스탬프를 생성하고 앱이 둘을 구별해서 보관하는지 확인합니다.

이 테스트들은 시스템이 올바른 사실을 저장하는지(UTC 인스턴트, 시간대 ID, 때로는 원래 로컬 시간)와 클록이 바뀔 때 리포트가 정직한지 빠르게 드러냅니다.

배포 전 빠른 체크리스트

Multi-time-zone workflows
Build internal tools like ticketing or timesheets that work across offices and regions.
Start a Project

서머타임 버그는 대부분의 날에는 앱이 정상처럼 보이기 때문에 놓칩니다. 시간을 표시하거나 날짜로 필터링하거나 리포트를 내보내는 모든 기능을 출시하기 전에 이 체크리스트를 사용하세요.

  • 각 리포트에 하나의 리포팅 시간대를 정하고(예: “비즈니스 본부 시간” 또는 “사용자 시간”) 헤더에 표시하며 표와 합계, 차트 전반에 일관되게 적용하세요.
  • 모든 “순간”은 UTC로 저장하세요(created_at, paid_at, message_sent_at). 컨텍스트가 필요하면 IANA 시간대 ID를 저장하세요.
  • DST가 적용될 수 있는 경우 “UTC-5” 같은 고정 오프셋으로 계산하지 마세요. 해당 날짜의 시간대 규칙으로 변환하세요.
  • UI, 이메일, 내보내기 등에서 시간을 명확히 라벨링하세요. 날짜, 시간, 시간대를 포함해 스크린샷이나 CSV가 오해받지 않게 하세요.
  • 작은 DST 테스트 세트 유지: 스프링 점프 직전의 타임스탬프, 직후의 타임스탬프, 그리고 폴-백 반복 시간대의 타임스탬프.

현실 점검: 뉴욕의 지원 매니저가 “일요일에 생성된 티켓”을 내보내고 런던의 동료가 파일을 열었을 때, 타임스탬프가 어떤 시간대를 나타내는지 추측하지 않아도 알아야 합니다.

예시: 시간대가 다른 환경에서 실제 지원 워크플로

Stop DST bugs early
Build a time-safe data model with UTC timestamps and IANA zones from day one.
Try AppMaster

미국이 서머타임으로 전환했지만 영국은 아직 전환하지 않은 주에 뉴욕 고객이 지원 티켓을 열고, 지원 팀은 런던에 있는 시나리오를 상상해 보세요.

3월 12일에 고객이 뉴욕 로컬 시간으로 09:30에 티켓을 제출했습니다. 그 순간은 뉴욕이 이제 UTC-4이므로 13:30 UTC입니다. 런던 상담원은 런던 시간으로 14:10에 답장했는데, 그 주 런던은 여전히 UTC+0이므로 14:10 UTC입니다. 답장은 티켓 생성 후 40분 만에 왔습니다.

만약 로컬 시간만 저장하고 시간대 ID가 없다면 이렇게 틀어질 수 있습니다:

  • “09:30”과 “14:10”을 일반 타임스탬프로 저장합니다.
  • 리포트 작업이 나중에 “뉴욕은 항상 UTC-5”라고 가정(또는 서버 시간대를 사용)합니다.
  • 09:30을 13:30 UTC가 아니라 14:30 UTC로 변환합니다.
  • SLA 시계가 한 시간 어긋나고 2시간 SLA를 만족한 티켓이 늦은 것으로 표시될 수 있습니다.

안전한 모델은 UI와 리포팅을 일관되게 유지합니다. 이벤트 시간을 UTC 타임스탬프로 저장하고, 관련 IANA 시간대 ID(예: 고객은 America/New_York, 상담원은 Europe/London)를 보관하세요. UI에서는 저장된 날짜의 규칙을 사용해 같은 UTC 순간을 뷰어의 시간대로 표시합니다.

주간 리포트는 “고객 로컬 날짜로 그룹” 같은 명확한 규칙을 선택하세요. America/New_York에서 자정-자정 경계를 계산해 그 경계를 UTC로 변환한 뒤 그 안의 티켓을 센다면, DST 주간에도 숫자는 일관되게 유지됩니다.

다음 단계: 앱 전반에 걸쳐 시간 처리 일관화하기

제품이 DST 버그에 시달렸다면, 가장 빠른 해결책은 몇 가지 규칙을 문서화하고 전면적으로 적용하는 것입니다. ‘대체로 일관됨’ 상태가 시간 문제의 온상입니다.

규칙을 짧고 구체적으로 유지하세요:

  • 저장 포맷: 무엇을 저장하는지(대개 UTC 인스턴트)와 절대 저장하지 않을 것(예: 존 컨텍스트 없는 애매한 로컬 시간).
  • 리포트 시간대: 기본으로 사용할 시간대와 사용자가 변경할 수 있는 방법.
  • UI 라벨링: 시간 옆에 무엇을 보여줄지(예: “Mar 10, 09:00 (America/New_York)” vs 단순히 “09:00”).
  • 반올림 규칙: 시간(시, 일, 주)을 어떻게 버킷팅할지와 그 버킷이 따르는 시간대.
  • 감사 필드: 어떤 타임스탬프가 “사건 발생”을, 어떤 것이 “레코드 생성/업데이트”를 의미하는지.

저위험 방식으로 배포하세요. 먼저 새 레코드부터 고쳐 문제의 확장을 멈추고, 그 다음에 기록을 배치로 마이그레이션하세요. 마이그레이션하는 동안에는 원래 값(가능하면)과 정규화된 값을 모두 유지해 리포트 차이가 나는지 확인하세요.

AppMaster (appmaster.io)를 사용하는 경우 한 가지 실용적 이점은 데이터 모델과 공통 비즈니스 로직에 이러한 규칙을 중앙화할 수 있다는 점입니다: UTC 타임스탬프를 일관되게 저장하고, 로컬 의미가 필요한 레코드 옆에 IANA 시간대 ID를 보관하며, 입력과 표시 경계에서 변환을 적용하세요.

실용적인 다음 단계는 하나의 시간대 안전 리포트(예: “일별 해결된 티켓”)를 만들어 위 테스트 케이스로 검증하는 것입니다. 두 개의 다른 시간대에서 DST 전환 주간을 통과해도 올바르게 동작하면 준비가 된 것입니다.

자주 묻는 질문

Why do DST bugs happen even when the code looks correct?

일광절약시간(DST)은 지역 시계의 오프셋을 바꿀 뿐, 사건이 실제로 발생한 순간을 바꾸지는 않습니다. 지역 시계 표시를 실제 순간과 같다고 다루면 봄에는 “사라진” 시간이, 가을에는 “중복된” 시간이 생깁니다.

What’s the safest way to store timestamps in a database?

실제 사건은 항상 절대 순간으로, UTC로 저장하세요. 그러면 오프셋이 바뀌어도 값이 흔들리지 않습니다. 화면에는 변환해서 보여주고, 변환 시에는 진짜 시간대 ID를 사용하세요.

Why can’t I store just a UTC offset instead of a time zone name?

오프셋 -05:00 같은 값은 특정 순간의 UTC 차이만 알려줄 뿐, DST 규칙이나 과거 이력을 포함하지 않습니다. America/New_York 같은 IANA 시간대는 날짜별 규칙을 포함하므로 정확한 변환을 보장합니다.

When should I store a value as a date instead of a timestamp?

생일, 청구서 마감일, ‘매달 5일 갱신’ 같은 값은 특정 순간이 아니라 날짜 단위 의미이므로 날짜형으로 저장하세요. 타임스탬프로 저장하면 시간대 변환 때문에 전날이나 다음날로 이동할 수 있습니다.

How should my app handle times that are skipped or repeated during DST switches?

봄 전진(스프링 포워드)은 특정 로컬 시간이 존재하지 않게 만듭니다. 이런 시간 선택은 차단하고 다음 가능한 시간으로 안내하세요. 가을 후퇴(폴 백)는 같은 로컬 시간이 두 번 발생하므로 사용자가 어떤 인스턴스를 의미하는지(예: 오프셋 표기)를 선택할 수 있게 하세요.

Should I convert scheduled meetings to UTC when saving them?

예약의 경우 사용자가 의도한 로컬 날짜와 시간, 그리고 시간대 ID를 함께 저장하세요. 실행을 위해 파생된 UTC 값을 같이 저장할 수는 있지만, 원래의 로컬 의도를 버리지 마세요—규칙이 바뀌면 의미가 사라집니다.

How do I stop reports from showing different daily totals for different users?

리포트마다 ‘하루’의 의미를 정하고 고수하세요. 리포트 헤더에 어떤 시간대 기준인지 명시하면 서로 다른 결과가 왜 발생하는지 명확해집니다. 지역별로 보기를 바꿀 수는 있지만, 그건 같은 데이터의 다른 관점으로 처리해야 합니다.

What’s the most common mistake that causes the “one-hour shift” bug?

가장 흔한 실수는 입력을 한 번 변환한 뒤, 어느 계층에서는 로컬로, 다른 계층에서는 UTC로 가정하며 다시 변환하는 ‘이중 변환’입니다. 입력은 한 번 파싱하고, 저장은 한 번(UTC로), 표시도 한 번만 하세요.

How should I calculate durations across DST changes?

경과 시간(예: 초)을 저장해서 합산한 뒤 표시할 때 형식화하세요. 급여나 SLA처럼 의미가 중요한 곳은 ‘경과 시간’인지 ‘벽시계 시간(월별/교대)’인지 먼저 결정하고 일관되게 반영하세요. DST 밤은 벽시계 시간이 길어지거나 짧아질 수 있습니다.

What tests catch most DST bugs before customers do?

적어도 한 개의 DST 관찰 시간대(예: America/New_York 또는 Europe/Berlin)에서 전환일 두 날을 테스트하고, DST를 쓰지 않는 시간대(예: Asia/Singapore 또는 Africa/Nairobi)와 대비 테스트하세요. 누락된 시간, 중복된 시간, 자정 경계, 리포트 버킷팅을 포함하면 대부분의 버그를 잡습니다.

쉬운 시작
멋진만들기

무료 요금제로 AppMaster를 사용해 보세요.
준비가 되면 적절한 구독을 선택할 수 있습니다.

시작하다