Python datetime 의 localize 는 라벨링이고 astimezone 이 진짜 변환이다
·수정 1회
요약
pytz.localize(dt)는 시계 숫자를 안 건드리고 tzinfo 만 attach 하는 "라벨링" 이다. 진짜 변환은astimezone().- 헷갈리면 함수 이름이
to_kst라도 실제로는 KST 시각이 보장 안 되는 함정에 빠진다. - 라벨링 단독의 영향 범위는 의외로 좁다 —
%Z/%z포맷, aware/naive 비교, 변환 호출 가능 여부 정도. 시계 숫자만 쓰는 코드에서는 라벨 무영향.
본문
naive vs aware datetime
tzinfo 가 들어있느냐 없느냐로 갈린다.
| 정의 | 예 | |
|---|---|---|
| naive | tzinfo is None |
datetime.now() |
| aware | tzinfo 있음 |
datetime.now(tz=pytz.UTC) |
naive 는 "시계 숫자만 적힌 자막" — 어느 나라 시간인지 모름. aware 는 "한국 시간 14:00" 처럼 출처가 명시된 시각.
라벨링 (localize) vs 변환 (astimezone)
서버가 UTC 환경이라고 치자. datetime.now() 는 2026-01-01 00:00 (naive) 을 준다.
라벨링 — 시계 숫자 안 건드림:
pytz.timezone('Asia/Seoul').localize(naive)
# → 2026-01-01 00:00 KST
# ↑ 그대로 ↑ KST 라벨만 새로 붙음
naive 에 "이건 사실 KST 야" 라고 우기는 것. 시계 숫자 그대로.
변환 — 시계 숫자 바뀜:
naive.replace(tzinfo=pytz.UTC).astimezone(pytz.timezone('Asia/Seoul'))
# → 2026-01-01 09:00 KST
# ↑ 숫자 바뀜 ↑ KST 라벨
"UTC 00:00 == KST 09:00 이라고 정확히 재계산" — 같은 순간을 다른 zone 표기로.
비유: 자막 "오후 3시" 에 "이건 한국 시간이야" 라고 주석 다는 게 라벨링. 미국 시청자용 자막으로 "오전 1시" 로 다시 쓰는 게 변환.
to_kst(datetime.now()) 의 함정
def to_kst(dt):
return pytz.timezone('Asia/Seoul').localize(dt)
함수 이름은 "KST 로 변환" 인 척하지만 실제로는 라벨링만 한다. 서버 환경에 따라:
| 서버 TZ | datetime.now() |
to_kst(...) |
실제 KST | 어긋남 |
|---|---|---|---|---|
| UTC (Docker 기본) | 00:00 naive |
00:00 KST 라벨 |
09:00 KST |
−9h |
| KST | 09:00 naive |
09:00 KST 라벨 |
09:00 KST |
OK |
중요: 이 9시간 어긋남은 라벨링의 죄가 아니라 datetime.now() 의 서버 로컬 TZ 의존성 에서 옴. 라벨링은 그냥 잘못된 라벨을 붙여서 거짓말을 완성할 뿐.
라벨링이 실제로 영향 주는 곳
라벨링은 시계 숫자를 안 건드리니, 시계 숫자만 쓰는 코드에서는 무영향. 영향 주는 경우:
%Z,%z포맷 출력 — 라벨이 글자로 나옴naive.strftime('%Y-%m-%d %H:%M:%S.%Z') # "...00:00." (빈 문자열) labeled.strftime('%Y-%m-%d %H:%M:%S.%Z') # "...00:00.KST" (라벨 글자)- naive vs aware 비교 — TypeError 로 즉시 폭발
astimezone()호출 가능 여부 — aware 만 의미 있는 변환 가능- aware 끼리 시간 빼기 — 라벨 다르면 실제 시간차로 계산됨
strftime('%Y-%m-%d %H:%M:%S') 같이 시계 숫자만 쓰는 코드에서는 라벨링 결과나 naive 나 동일하다.
올바른 fix
def to_kst(dt):
if dt.tzinfo is None:
dt = dt.replace(tzinfo=pytz.UTC) # 출처를 진짜 라벨링
return dt.astimezone(pytz.timezone('Asia/Seoul')) # 변환
또는 호출부에서 처음부터 aware 로:
datetime.now(tz=pytz.timezone('Asia/Seoul'))
참고
Network Time Protocol은 다양한 방식으로 클럭 동기화를 한다.— NTP 가 노드 간 UTC 시각을 동기화한다면, 이 zettel 은 그 시각을 애플리케이션에서 어떤 zone 으로 표기·변환할지를 다룬다.