목차
한국은 서머타임을 안 쓴다. 그래서 더 위험하다. 겪어 본 적이 없으니 코드에도 대비가 없다.
해를 더 쓰려고 시계를 옮긴다
서머타임은 해가 긴 계절에 시계를 한 시간 앞당겨 놓는 제도다. 봄에 앞당기고 가을에 되돌린다.
목적은 해가 떠 있는 동안을 더 쓰자는 것이다. 시계를 앞당기면 사람들이 한 시간 일찍 움직이게 되고, 저녁에 밝은 시간이 한 시간 늘어난다. 전기를 아끼자는 명분으로 널리 퍼졌다.
효과가 있는지는 지금도 논쟁 중이다. 절약 효과가 미미하다는 연구가 쌓이면서 폐지 논의가 여러 나라에서 진행 중이고, 실제로 없앤 나라도 있다. 그래서 이 제도는 앞으로도 계속 바뀔 데이터다. 앞 글에서 규칙 목록을 계속 받아 와야 한다고 한 이유의 상당 부분이 여기서 나온다.
프로그램 입장에서 중요한 건 명분이 아니다. 1년에 두 번, 달력이 매끄럽지 않게 이어진다는 사실 하나다.
앞당기는 날과 되돌리는 날
공항 비유로 말하면 팻말의 숫자가 계절마다 바뀌는 것이다. 뉴욕 팻말은 겨울에 -05:00, 여름에 -04:00이다. 큰 시계는 그대로 흐르는데 팻말만 바뀐다.
문제는 바뀌는 그 순간이다. 여기서 달력에 구멍과 겹침이 생긴다.
큰 시계는 똑같이 여섯 시간을 갔다. 팻말만 바뀌어서 한 시간이 사라지거나 두 번 온다. 그날 하루는 23시간이거나 25시간이다.
존재하지 않는 시각이 생긴다
봄에 시계를 앞당기는 날, 새벽 2시가 되는 순간 시계를 3시로 옮긴다.
그러면 그날 2시 30분은 존재하지 않는다. 달력에서 한 시간이 통째로 잘려 나갔다. 그날 그 지역 어디에도 2시 30분을 겪은 사람은 없다.
코드가 이걸 만나는 자리는 뻔하다.
- 사용자가 알림 시각으로 새벽 2시 30분을 골랐다
- 매일 새벽 2시 30분에 도는 배치가 있다
- 옛 데이터를 옮기는데 그 시각이 들어 있다
이때 언어와 라이브러리마다 하는 짓이 다르다. 예외를 던지는 쪽도 있고, 3시 30분으로 밀어 주는 쪽도 있고, 1시 30분으로 당기는 쪽도 있다. 아무 말 없이 하나를 골라 주는 쪽이 가장 위험하다. 틀렸다는 신호가 없기 때문이다.
같은 시각이 두 번 온다
가을은 반대다. 새벽 2시가 되는 순간 시계를 1시로 되돌린다.
그러면 그날 1시 30분이 두 번 온다. 한 번은 서머타임이 적용된 상태로, 한 번은 해제된 상태로. 두 번은 실제로 한 시간 떨어진 서로 다른 순간이다.
여기서 나오는 사고가 봄보다 지독하다.
- 한 시간짜리 작업이 두 번 돈다. 1시 30분에 도는 배치가 그날 두 번 실행된다. 정산이 두 번 돌면 돈이 두 번 나간다
- 정렬이 뒤집힌다. 지역 시각으로 정렬하면 나중에 일어난 일이 앞에 온다
- 값을 되돌릴 수 없다.
2025-11-02 01:30이라고만 적힌 값은 두 순간 중 어느 쪽인지 알 방법이 없다
마지막이 핵심이다. 팻말 없이 저장한 값은 1년에 한 번, 한 시간 동안 복원이 불가능해진다.
그날은 23시간이거나 25시간이다
당연해 보이는 전제가 하나 깨진다. 하루가 24시간이 아니다.
봄에 앞당기는 날 → 23시간
가을에 되돌리는 날 → 25시간그래서 이런 코드가 그날만 틀린다.
// 위험: 하루가 늘 24시간이라고 가정했다
long tomorrow = today + 60 * 60 * 24;앞 글에서 “초 덧셈으로 풀리는 요구인지 먼저 확인하라”고 한 이유가 이것이다. “24시간 뒤”와 “내일 같은 시각”은 그날 한 시간 어긋난다. 둘 중 무엇을 원하는지 정해야 한다.
- 24시간 뒤가 맞는 경우: 세션 만료, 쿠폰 유효기간처럼 경과 시간이 뜻인 것
- 내일 같은 시각이 맞는 경우: 알림, 리마인더처럼 사람의 생활 리듬이 뜻인 것
대부분의 나라는 안 쓴다
세계 인구의 다수는 서머타임을 쓰지 않는다. 한국은 1988년 이후로 안 쓴다.
그리고 쓰는 곳도 날짜가 제각각이다. 미국과 유럽은 전환 날짜가 다르다. 그래서 1년에 몇 주 동안은 평소와 시차가 한 시간 다른 기간이 생긴다. “뉴욕은 서울보다 13시간 늦다”가 그 기간에는 14시간이 된다.
남반구는 계절이 반대라 시기도 반대다. 호주 일부 지역은 우리 기준 겨울에 서머타임을 한다.
여기서 비유가 깨진다. 공항 팻말은 모든 도시가 같은 날 같이 바뀔 것 같지만 실제로는 도시마다 바뀌는 날이 다르다. 그래서 “지금 시차”를 상수로 적어 둔 표는 1년에 몇 주 동안 틀린다.
실무에서: 스케줄과 집계가 먼저 깨진다
서머타임 버그는 1년에 두 번, 새벽에만 나타난다. 재현이 거의 안 되고 그래서 오래 살아남는다.
스케줄러에서 확인할 것.
- 반복 작업의 시각을 지역 시각으로 정의했는지 UTC로 정의했는지 정한다. 정산·리포트처럼 사람의 하루에 붙은 작업은 지역 시각이 맞고, 헬스체크처럼 주기가 뜻인 작업은 UTC가 맞다
- 새벽 2~3시대에 도는 작업을 옮긴다. 그 시간대만 피하면 두 사고 다 안 만난다. 가장 싸고 확실한 방어다
- 두 번 돌아도 괜찮게 만든다. 같은 입력으로 여러 번 실행해도 결과가 같으면 반복 실행이 사고가 아니다
집계에서 확인할 것.
- 일별 집계의 경계를 지역 시각으로 자를 때, 그날의 길이가 23시간이나 25시간일 수 있다
- 전년 대비 같은 날을 비교하면 길이가 다른 날끼리 비교하게 되는 경우가 있다
저장에서 확인할 것.
- 지역 시각만 저장하지 않는다. 오프셋이나 순간을 같이 남겨야 가을 겹침 구간이 복원된다
- 사용자가 고른 시각을 저장할 때, 그 시각이 존재하지 않는 시각인지 검사한다. 조용히 옮겨 주는 라이브러리를 쓰고 있다면 그 동작을 알고 쓰는 것과 모르고 쓰는 것은 다르다
정리
- 서머타임은 팻말의 숫자가 계절마다 바뀌는 것이다. 큰 시계는 그대로다.
- 봄에는 존재하지 않는 시각이 생기고, 가을에는 같은 시각이 두 번 온다.
- 가을 겹침 구간의 값은 팻말 없이는 복원할 수 없다. 1년에 한 시간, 정보가 사라진다.
- 하루가 24시간이 아닌 날이 있다. “24시간 뒤”와 “내일 같은 시각”은 다른 요구다.
- 나라마다 쓰는지도 다르고 바뀌는 날짜도 다르다. 시차를 상수로 적으면 틀린다.
- 가장 싼 방어는 새벽 2~3시대에 배치를 두지 않는 것이다.
다음 글은 저장하는 자리로 간다. 팻말을 뗀 채 저장된 값이 무엇을 잃는지, DB 컬럼을 고르는 순간 무엇이 결정되는지를 본다.