Django Time-zone issue

장태순
5 min readMar 18, 2021

--

django 프로젝트를 통해 발생했던 timezone 문제들이 발생과 해결 과정에 대해 기술해보고자 한다.
python 3.8 django 3.1 drf 3.11.1 Mysql 8 버전 사용 기준(Asia/Seoul timezone)으로 작성한다.

1. Project basic Setup

settings.py

USE_TZ=True
TIME_ZONE="Asia/Seoul"

이렇게 하면 모든 것이 해결될 줄 알았지만 그게 아니였다.

import datetime

파이선에서 제공하는 기본 datetime 모듈을 사용하면 timezone이 다르게 인식되어서 db에 utc 기준으로 저장이 된다. 이 원인을 찾아보니 datetime객체가 다 같은 datetime 객체가 아니였던 것이다.

2. datetime 객체의 종류(naive vs aware)

datetime 객체는 2가지 종류가 있다. type을 print 해봐도 나오지 않지만 나뉘어져 있다.

  • naive 객체 : tzinfo 속성이 설정되지 않는 datetime 객체(TIME_ZONE 변수로 지정한 시간대 사용)
  • aware 객체 : tzinfo 속성이 설정된 datetime 객체

ex) naive 객체

ex) aware 객체

두 개를 비교하면 tzinfo를 통해 timezone이 정의가 되어 있느냐 아니냐에 따라가 같은 datetime 객체더라도 naive와 aware로 나뉜다고 보면 된다. 기존에 datetime을 쓴다면 django 기본 세팅의 time-zone 변수에 따라 지정되지만 프론트/클라이언트 서버와의 통신, db에 insert 및 update를 할때 에러 혹은 엉뚱한 시간이 저장된다.
그래서 aware datetime 객체를 사용해야 한다. 대표적인것이 django에서 제공하는 timezone이다.

3. django timezone

django에서 기본적으로 제공하는 라이브러리로 손쉽게 사용이 가능하다. aware 객체 사용을 위해 많이 사용하는 것이 localtime(), make_aware()이다.

3–1. pytz

pytz는 Olson tz databse를 Python으로 옮겨온 라이브러리다.
django에서 기본제공은 안되므로 따로 설치를 해줘야 한다.

pip install pytz

pytz의 장점은 시간대를 시간차가 아닌 사람이 알아보기 쉬운 지역 이름으로 비교적 쉽게 설정할 수 있고, 원하는 시간대의 aware datetime으로 변경해주는 localize() 메소드를 제공한다는 점이다. datetime 이슈시에 많이 등장하는 라이브러리로 설치해서 사용하는 것이 좋을 듯 하다.

from datetime import datetime
from django.utils import timezone
import pytz
  • timezone.localtime() : 특정 시간대 기준 Aware 객체 → TIME_ZONE 시간대 기준의 Aware 객체 변환
  • timezone.make_aware() : 시간대 기준이 없는 Naive 객체 → TIME_ZONE 시간대 기준의 Aware 객체 변환

ex) timezone.make_aware(datetime.now()) # aware (utc) → aware(tz)

ex) timezone.localtime(datetime.now(pytz.utc)) # naive(tz) → aware(tz)

실제 예시

프론트에서 UTC string으로 전달시에 KST로 변환하여 활용 하기

프론트에서 date 속성을 가진 data들을 전송시에 UTC 속성으로 전달 받는다. 그래서 전달받은 date_data를 KST로 변환하는 기능이 필요하다.

utc_dt 라는 utc date string을 받으면 datetime 객체로 전환 뒤 pytz 라이브러리를 활용하여 utc.localize()를 통해 UTC aware 객체로 전환 그 다음 설정한 timezone에 맞게 astimezone() 메서드를 실행해주면 KST aware 객체가 완성된다. 이 때 시간대 간 변환을 할 시 normalize() 메소드를 활용해야 서머타임 이슈에서 자유로울 수 있어서 사용하였다.

>> 중요점 : pytz를 이용하여 timezone 을 지정한 aware 객체를 db에 insert 혹은 update할시에 settings 모듈에

USE_TZ = True 로 설정해야 django에서 aware 객체의 tz을 인식하여 처리한다.

아예 query상에서는 datetime 객체가 아니고 date_format을 갖춘 string이여도 상관 없다.

위와 같지만 마지막에 string type으로 변환하는 것만 다르다.

실제 api에서 BaseFilterBackend 에서 적용되는 방식이다.

KST date_format을 갖춘 string 변환 후 django queryset에 날려주는 로직이다.

--

--