Zettelkasten

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 의존성 에서 옴. 라벨링은 그냥 잘못된 라벨을 붙여서 거짓말을 완성할 뿐.

라벨링이 실제로 영향 주는 곳

라벨링은 시계 숫자를 안 건드리니, 시계 숫자만 쓰는 코드에서는 무영향. 영향 주는 경우:

  1. %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"     (라벨 글자)
    
  2. naive vs aware 비교 — TypeError 로 즉시 폭발
  3. astimezone() 호출 가능 여부 — aware 만 의미 있는 변환 가능
  4. 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 으로 표기·변환할지를 다룬다.