파이썬 타임존 오류로 예약이 한 시간씩 밀리는 이유

파이썬 타임존

이 글을 읽으면 예약 시스템에서 시간이 한 시간씩 어긋나는 문제의 정확한 원인과, aware·naive 두 객체의 경계를 코드로 확인하는 방법을 알 수 있습니다. 결론부터 말씀드리면, 파이썬 타임존 문제로 인한 1시간 오차는 대부분 naive datetime과 aware datetime을 섞어 쓰거나, 서머타임(DST)이 있는 지역에 고정 오프셋을 하드코딩했을 때 생깁니다. 아래에서 실제 코드로 재현하고, 어디를 고치면 되는지 짚어 드리겠습니다.

예약 시간이 한 시간씩 밀리는 진짜 원인

많은 개발자가 이 문제를 “타임존 계산 실수”라고 뭉뚱그리지만, 실제로는 두 가지 패턴 중 하나입니다. 하나는 datetime 비교 연산에 naive 객체와 aware 객체가 섞여 들어가는 경우고, 다른 하나는 DST가 있는 지역의 시간을 고정된 UTC 오프셋으로 저장해 버리는 경우입니다.

문제는 첫 번째 패턴이 에러 없이 조용히 통과한다는 점입니다. 아래 코드를 보시면, <> 비교는 예외를 던지지만 == 비교는 그냥 False를 반환합니다.

from datetime import datetime, timezone

naive = datetime(2026, 9, 2, 10, 0)
aware = datetime(2026, 9, 2, 10, 0, tzinfo=timezone.utc)

print(naive == aware)   # False (에러 없음)
print(naive < aware)    # TypeError: can't compare offset-naive and offset-aware datetimes

예약 중복 체크 로직에서 두 datetime을 ==로 비교하도록 짜여 있으면, 실제로는 같은 시각인데도 다른 예약으로 인식되거나 그 반대로 처리될 수 있습니다. 에러가 나지 않으니 배포 전 테스트에서도 잘 안 걸립니다.

aware datetime과 naive datetime, 경계는 어디서 갈리나요?

파이썬 코드 에디터 화면에 시계와 타임존 관련 코드가 표시된 모습

파이썬 공식 문서는 datetime 객체 문서에서 이 구분을 명확히 정의합니다. datetime 객체의 tzinfo 속성이 None이 아니고, utcoffset()None을 반환하지 않으면 aware, 그렇지 않으면 naive입니다.

from datetime import datetime, timezone

now_naive = datetime.now()
now_aware = datetime.now(timezone.utc)

print(now_naive.tzinfo)   # None
print(now_aware.tzinfo)   # datetime.timezone.utc

datetime.now()는 인자 없이 호출하면 시스템 로컬 시각을 naive로 돌려줍니다. 어느 지역 기준인지 정보가 없다는 뜻입니다. 반대로 datetime.now(timezone.utc)처럼 tzinfo를 넘기면 그 시각이 어느 기준인지 스스로 알고 있는 aware 객체가 됩니다. 예약 시스템처럼 서로 다른 서버·클라이언트가 시각을 주고받는 구조라면, DB에 저장하고 API로 응답하는 모든 지점에서 aware로 통일하는 편이 안전합니다.

DST가 있는 타임존에서 고정 오프셋이 깨지는 지점

한국은 1988년 이후 서머타임을 시행하지 않아서 KST는 연중 UTC+9로 고정입니다. 그래서 국내 예약만 다루는 시스템에서는 이 문제가 잘 안 보입니다. 하지만 예약 대상이 미국·유럽처럼 DST를 운영하는 지역이면 이야기가 달라집니다.

아래는 뉴욕 시각을 EST(UTC-5)로 고정해서 저장했다고 가정한 코드입니다. 실제로 7월은 서머타임 기간이라 뉴욕이 EDT(UTC-4)를 쓰는데, 코드가 그 사실을 모릅니다.

from datetime import datetime, timedelta, timezone
from zoneinfo import ZoneInfo

KST = timezone(timedelta(hours=9))
FIXED_NY = timezone(timedelta(hours=-5))  # EST로 고정해버린 잘못된 코드

# 잘못된 방식: 오프셋을 하드코딩
wrong = datetime(2026, 7, 10, 14, 0, tzinfo=FIXED_NY)
print(wrong.astimezone(KST))  # 2026-07-11 04:00:00+09:00

# 올바른 방식: zoneinfo로 지역 이름 자체를 지정
correct = datetime(2026, 7, 10, 14, 0, tzinfo=ZoneInfo("America/New_York"))
print(correct.astimezone(KST))  # 2026-07-11 03:00:00+09:00

두 결과가 정확히 1시간 차이 납니다. 고정 오프셋 코드는 그 지역이 지금 서머타임인지 아닌지 판단할 수 없기 때문에, DST 적용 기간에만 조용히 1시간이 밀립니다. 예약 목록에서 "가끔씩만" 시간이 틀리다는 문의가 들어온다면 이 패턴을 의심해 볼 만합니다.

세계 여러 도시의 시간대를 나타내는 지도와 아날로그 시계들

여기에 더해, .astimezone()을 인자 없이 호출하면 파이썬이 시스템의 로컬 타임존을 기준으로 삼는다는 점도 실무에서 자주 걸리는 지점입니다. 개발자 PC는 KST인데 배포된 컨테이너의 시스템 타임존이 UTC로 설정돼 있으면, 같은 코드가 로컬과 운영 환경에서 다른 결과를 냅니다. .astimezone(ZoneInfo("Asia/Seoul"))처럼 대상 타임존을 항상 명시하면 이 차이를 없앨 수 있습니다.

zoneinfo와 pytz 차이를 코드로 직접 확인해 보세요

파이썬 3.9부터는 표준 라이브러리에 zoneinfo 모듈이 포함돼 있어서, 별도 패키지 설치 없이 IANA 타임존 이름으로 aware datetime을 만들 수 있습니다. 그 이전에는 서드파티 pytz를 많이 썼는데, 여기서 또 다른 함정이 있습니다. pytz는 tzinfo=pytz.timezone(...)처럼 생성자에 바로 넣으면 안 되고, 반드시 localize()를 거쳐야 DST가 올바르게 반영됩니다.

import pytz
from datetime import datetime

ny = pytz.timezone("America/New_York")

# 잘못된 방식: tzinfo에 직접 대입 (DST 미반영, LMT 오프셋 사용)
wrong = datetime(2026, 7, 10, 14, 0, tzinfo=ny)
print(wrong)  # 2026-07-10 14:00:00-04:56 (역사적 LMT 오프셋)

# 올바른 방식: localize() 사용
correct = ny.localize(datetime(2026, 7, 10, 14, 0))
print(correct)  # 2026-07-10 14:00:00-04:00 (EDT 정상 반영)

새 프로젝트라면 pytz보다 표준 라이브러리인 zoneinfo를 쓰는 편이 이런 함정 자체를 없앨 수 있어 유리합니다. 다만 한 가지 트레이드오프가 있습니다. 윈도우에는 리눅스·macOS와 달리 시스템에 IANA 타임존 데이터베이스가 내장돼 있지 않아서, zoneinfo가 타임존 이름을 못 찾고 ZoneInfoNotFoundError를 던질 수 있습니다. 이 경우 pip install tzdataIANA 시간대 데이터베이스를 파이썬 패키지 형태로 추가 설치해야 정상 동작합니다. 리눅스 서버 배포 환경에서는 보통 시스템 tzdata가 이미 있어 문제가 없지만, 로컬 윈도우 개발 환경에서 막힌다면 이 부분부터 확인해 보시기 바랍니다.

예약 시스템 타임존 점검, 이 순서로 하면 됩니다

정리하면 세 지점만 확인하면 됩니다. DB와 API 응답 어디서든 naive datetime이 섞여 들어가는 곳이 없는지, 비교 연산에서 ==가 조용히 False를 내며 통과하고 있지 않은지, 그리고 DST가 있는 지역을 다룬다면 고정 오프셋 대신 zoneinfo.ZoneInfo("지역/도시") 형태의 IANA 이름을 쓰고 있는지입니다.

바로 실행할 수 있는 다음 단계로는, 기존 코드에서 datetime(으로 검색해 tzinfo 인자 없이 객체를 만드는 곳을 찾아보시는 걸 권해 드립니다. 그 지점들이 대부분 이번에 다룬 1시간 오차의 출발점입니다.

파이썬 GIL 때문에 안 빨라질 때, 스레드·프로세스·async 고르는 기준

Leave a Comment