Cheondi
개발 · 알고리즘·CS

시간대는 문자열 포맷 문제가 아니었다

서버 시간과 기기 시간이 어긋나는 문제를 따라가며 시간대와 기준 시각을 배운 기록입니다.

  • #datetime
  • #timezone
  • #unity

거래 내역의 시간이 몇 시간씩 어긋나 보이는 문제가 있었다. 처음에는 화면에 yyyy-MM-dd HH:mm:ss 포맷을 잘못 적용한 줄 알았다. 문자열 모양은 맞았고 분과 초도 정확했는데 시간만 일정하게 차이 났다. 이상한 점은 개발 PC에서는 괜찮다가 다른 지역 설정의 기기에서 다시 틀어졌다는 것이다.

코드를 따라가 보니 서버에서 받은 문자열을 DateTime으로 바꾸고 다시 로컬 시간으로 바꾸는 과정이 있었다. 문제는 원본 문자열이 이미 로컬 시간인지 UTC인지 코드만 보고 알 수 없었다는 데 있었다.

같은 숫자라도 기준이 다를 수 있었다

2023-11-04 09:00:00이라는 값만 보면 어느 지역의 아홉 시인지 알 수 없다. UTC 기준인지, 서버가 있는 지역인지, 사용자의 지역인지가 함께 있어야 하나의 시점을 가리킬 수 있었다.

표시 문자열  : 2023-11-04 09:00:00
기준 시각    : UTC 2023-11-04 00:00:00
사용자 시간대: UTC+09:00

화면은 같은 값을 보여주지만 저장과 계산은 기준 시각으로 하는 편이 안전했다.

변환을 한 곳에 모았다

각 화면에서 AddHours(9)를 호출하는 코드가 생기면 지역이 바뀌거나 서버 규칙이 바뀔 때 전부 찾아야 한다. 응답을 모델로 바꾸는 경계에서 UTC로 해석하고, 화면 직전에 로컬 시간으로 변환하도록 흐름을 정리했다.

DateTime utc = DateTime.SpecifyKind(parsed, DateTimeKind.Utc);
DateTime local = TimeZoneInfo.ConvertTimeFromUtc(utc, userTimeZone);
label.text = local.ToString("yyyy-MM-dd HH:mm:ss");

실제 응답에는 오프셋이 포함된 경우와 그렇지 않은 경우가 섞일 수 있어 파싱 규칙도 응답 계약에 맞춰야 했다. 임의로 DateTimeKind를 붙이는 건 예시처럼 입력 기준이 확실할 때만 가능했다.

정렬과 표시가 같은 기준을 써야 했다

화면에는 로컬 시간으로 보이는데 정렬은 원본 문자열로 하면 날짜 경계를 넘을 때 순서가 어색해질 수 있었다. 날짜 범위 검색도 사용자가 선택한 하루를 그대로 서버에 보내면 기준 시간대에 따라 시작과 끝이 달라졌다.

그래서 비교와 정렬에는 하나의 기준 시각을 사용하고, 입력과 출력 경계에서만 시간대를 바꾸는 게 중요했다. 보기 좋은 문자열은 마지막 결과일 뿐이었다.

시간은 값과 문맥의 묶음이었다

이 문제 전에는 시간 버그를 포맷 버그로 생각하는 경우가 많았다. 이제는 로그에 시각을 찍을 때도 기준이 무엇인지 함께 확인한다. 서버 시각, 클라이언트 시각, 사용자가 보는 시각을 구분하지 않으면 모두 맞는 숫자인데 결과는 틀릴 수 있다.

시간대 문제를 한 번 겪고 나니 날짜가 들어간 기능은 단순 UI가 아니었다. 데이터가 어느 기준으로 저장되고 어느 경계에서 변환되는지 먼저 정해야 했다. 작은 표시 오류가 데이터의 의미를 생각하게 만든 작업이었다.