언어 Language/파이썬 Python

Q. 파이썬 타임존 유명 pytz 팩키지의 서울 타임존이 LMT+08:28:00으로 나오는 이유는?

Tap to restart 2022. 11. 5. 15:00

아래처럼 코드를 실행해보면

from pytz import timezone
seoul = timezone('Asia/Seoul')
seoul

엉뚱한 결과가 나온다. LMT+8:28:00 STD

아니 서울은 시차가 +9시간인데 왜 +8:30도 아니고 +8:28일까?

A. 대한제국 이전을 기준으로 한 것이다.

엄청 많이 쓰는 pytz. 공식 파이썬 팩키지 같지만 아니다. 그냥 개인 프로젝트다.
pytz 깃허브 들어가보면 생각보다 별 숫자도 적어서 놀라게 된다. 바로 별을 눌렀다!

pytz는 Time Zone Database를 사용하고 있다.
여기 사이트 가서 데이터를 받아서 압축을 풀어서 asia를 열어보자.

tzdb-2022f.tar
0.49MB
asia
0.17MB

아래 같은 정보를 확인할 수 있다.

# Zone	NAME		STDOFF	RULES	FORMAT	[UNTIL]
Zone	Asia/Seoul	8:27:52	-	LMT	1908 Apr  1
			8:30	-	KST	1912 Jan  1
			9:00	-	JST	1945 Sep  8
			9:00	ROK	K%sT	1954 Mar 21
			8:30	ROK	K%sT	1961 Aug 10
			9:00	ROK	K%sT
Zone	Asia/Pyongyang	8:23:00 -	LMT	1908 Apr  1
			8:30	-	KST	1912 Jan  1
			9:00	-	JST	1945 Aug 24
			9:00	-	KST	2015 Aug 15 00:00
			8:30	-	KST	2018 May  4 23:30
			9:00	-	KST

저 8:27:52가 1908년 4월 1일까지 사용한 것을 확인할 수 있다.
이 데이터를 갖고 와서 타임존 설정을 해주니 +8:28이 되는 것이다.

관련 코드도 볼 수 있다. 플러스 마이너스 30초 하고 있다고 적혀 있다.
출처: tzfile.py

            # Round utcoffset and dst to the nearest minute or the
            # datetime library will complain. Conversions to these timezones
            # might be up to plus or minus 30 seconds out, but it is
            # the best we can do.
            utcoffset = int((utcoffset + 30) // 60) * 60
            dst = int((dst + 30) // 60) * 60
            transition_info.append(memorized_ttinfo(utcoffset, dst, tzname))


잠깐 그렇다면 이것은 오류일까? 나도 처음에는 오류라고 생각했다.
아니다. pytz 팩키지를 제대로 사용하지 않아서 그런 것이다.
오류가 아니다. 아주 정확한 시간을 표현해주려고 이렇게 만든 것이다.

아래처럼 실행해보자.

import pytz
from datetime import datetime

print(pytz.all_timezones)
seoul = pytz.timezone('Asia/Seoul')
fmt = '%Y-%m-%d %H:%M:%S %Z%z'
loc_dt = seoul.localize(datetime(1908, 3, 31, 0, 0, 0))
print(loc_dt)
loc_dt = seoul.localize(datetime(1908, 4, 2, 0, 0, 0))
print(loc_dt)
loc_dt = seoul.localize(datetime(1911, 12, 31, 0, 0, 0))
print(loc_dt)
loc_dt = seoul.localize(datetime(1912, 1, 2, 0, 0, 0))
print(loc_dt)
loc_dt = seoul.localize(datetime(1945, 9, 7, 0, 0, 0))
print(loc_dt)
loc_dt = seoul.localize(datetime(1945, 9, 9, 0, 0, 0))
print(loc_dt)
loc_dt = seoul.localize(datetime(1954, 3, 20, 0, 0, 0))
print(loc_dt)
loc_dt = seoul.localize(datetime(1954, 3, 21, 0, 0, 0))
print(loc_dt)
loc_dt = seoul.localize(datetime(1961, 8, 9, 0, 0, 0))
print(loc_dt)
loc_dt = seoul.localize(datetime(1961, 8, 11, 0, 0, 0))
print(loc_dt)

실행해보면 아래 같은 결과가 나온다.


맞다. 역사적 타임존 시간에 맞춰서 처리해주고 있었던 것이다.
특별한 시간대로 localize 하지 않는다면? 역사적 타임존의 가장 맨 위에 있는 시간대로 처리하게 된 거였다. 하지만 pytz 개발자의 이 선택으로 많은 사람들이 오류를 경험한다.

오류 발생 코드 예

보통 아래처럼 사용한다.

import pytz
import datetime

seoul = pytz.timezone('Asia/Seoul')
dt = datetime.datetime(2022, 1, 1, tzinfo=seoul)
print(dt)

실행결과로 +08:28이 나온다.
2022-01-01 00:00:00+08:28

파이썬 기본 팩키지인 datetime을 활용해서 datetime을 만들게 된다. 이때 타임존 정보만 'Asia/Seoul' 식으로 지역명으로 처리하고 싶어서 pytz의 타임존 정보를 갖고 온다. pytz에 대한 이해도 없이 당연히 가장 최근 타임존 정보가 올 거라고 추정하고 실행을 하고, 엉뚱한 결과를 얻고 당황하게 된다. 바쁘니 일단 대충 검색해서 해결책을 찾고, pytz 문서까지는 읽어보지 않는다. 나도 그랬다.

오류 해결 방법

해결책은 pytz를 타임존 정보를 갖고 오는데까지만 쓰지 말고, 날짜시간 값을 갖고 오는데까지 사용하는 것이다.
아래 코드를 실행 해보자.

import pytz
import datetime

seoul = pytz.timezone('Asia/Seoul')
dt = seoul.localize(datetime.datetime(2022, 1, 1))
print(dt)

2022-01-01 00:00:00+09:00
로 원하는 값이 정확하게 나온다.

공식 문서에 나온 사용 예가 바로 위 예다.
출처: pytz/README

왜 다른 나라는 제대로 나오는 것처럼 보일까?

미국을 보자. 처음과 끝이 똑같다. 그들은 상관 없다.
유럽 나라들도 많은 경우 똑같다.

Asia/Gaza를 보자. 2:17:52다.

그래서 팔레스타인 가자도 이상하게 나온다.

1900년 10월 이전 타임존으로 나오는 것이다! 이 팩키지를 쓰고 있는 대한민국 개발자만 고통받고 있었던 것은 아니었다.

기타 정보. Python3.9와 zoneinfo

최근 Python3.9부터는 zoneinfo가 기본으로 추가되었다.
이 zoneinfo는 현재를 기준으로 나와서 +9시간으로 나온다.

from zoneinfo import ZoneInfo
from datetime import datetime

dt = datetime(2012, 10, 28, 2, 0, tzinfo=ZoneInfo('Asia/Seoul'))
print(dt)

실행해보면 아래처럼 나온다.