본문 바로가기
AI 웹개발반/Python, Django

[Django] datetime compare 오류, naive와 aware

by 째깍단 2023. 7. 6.

datetime에는 naive한 객체와 aware한 객체가 있다

 

naive는 '순진한' 객체로 시간 정보만을 가지고 있는 객체,

aware는 '인식'하고있는 객체로 시간과 UTC time +00:00을 알고 있는 객체다

 

 

 


 

 

 

이전에 작성했던 시간 비교 로직을 프론트엔드에 연결한 후 오류가 발생했다.

 

아래는 시간비교 로직의 일부

 

def get_grouppurchase_status(self, obj):
        """공구 게시글 상태 check"""
        
        now = datetime.now() #naive
        is_ended = obj.is_ended
        open_at = datetime.strptime(open, "%Y-%m-%d %H:%M:%S")
        close_at = datetime.strptime(close, "%Y-%m-%d %H:%M:%S")
        if is_ended:
            return "종료"
        elif close_at < now:
            return "종료"
        elif not is_ended and open_at > now:
            ...

 

 

오류메세지 :

File "/Users/diane073/Documents/Projects/sparta/BFFs/BFFs_backend/feed/serializers.py", 
line 337, in get_grouppurchase_status
    elif close_at < now:
         ^^^^^^^^^^^^^^
TypeError: can't compare offset-naive and offset-aware datetimes

 

 

 

이유는 프론트의 datetime을 입력해주는 필드에서 순수 string이나 컴퓨터 시간 데이터를 넣어준 것이 아니라

UTC시간이 추가되어 반환되었기 때문이다!

 

 

그래서 datetime.now와  datetime field로 저장되어있는 객체 close_at을 비교할 수 없는 오류가 발생했다

 

 

 

 

 

- 시도 1 -

 

찾아보니 pytz로 timezone을 replace() 혹은 astimezone()을 사용하여

비교할 두 시간대를 맞추어주는 2가지 방법이 있어 실행해보았으나 잘 적용되지 않았다.

[참고] : https://itsourcecode.com/typeerror/typeerror-cant-compare-offset-naive-and-offset-aware-datetimes/

 

 

 

 

 

 

- 시도 2 -

 

그래서 다른 방법을 찾아보다 저장해온 시간을 범위연산자로 잘라내거나

UTC시간에 들어있는 '+'를 spilt으로 합쳐보는 방법을 적용해보았다.

[참고] : https://stackoverflow.com/questions/6707398/valueerror-when-using-strptime-to-get-a-datetime-object-with-timezone-offset

 

 

 

둘 모두 잘 작동한다

 

obj.close_at = '2011-07-15 13:00:00+00:00'
close = obj.close_at[:19] # 19번째, 그러니까 13시 00분 00초까지만 잘라 가져온다
close_at = datetime.strptime(close, '%Y-%m-%d %H:%M:%S')

 

obj.close_at = '2011-07-15 13:00:00+00:00'
close = str(obj.close_at).split('+')[0] 
# 오류를 발생시키는 +를 빼고 UTC시간을 naive datetime의 마이크로초처럼 만든다
close_at = datetime.strptime(close, "%Y-%m-%d %H:%M:%S")

 

 

 

 

 

open_at도 마찬가지의 처리를 해주었다.

 

def get_grouppurchase_status(self, obj):
    """공구 게시글 상태 check"""
    
    now = datetime.now() #naive
    is_ended = obj.is_ended
    open = str(obj.open_at).split('+')[0]
    open_at = datetime.strptime(open, "%Y-%m-%d %H:%M:%S")
    if not obj.close_at:
    	...
    else:
        close = str(obj.close_at).split('+')[0]
        close_at = datetime.strptime(close, "%Y-%m-%d %H:%M:%S")
        if is_ended:
            ...

 

 

 

 

문제점 ) 

 

현재 프로젝트는 한국에서만 서비스하고 한국에서 사용자를 받고 있어 범위연산자나 split을 사용하였지만,

만약 해외서비스라면 UTC시간에 따라 시간자체가 달라질 수 있다

 

안전한 코딩을 위해 replace혹은 astimezone 함수를 사용하거나

현재시간을 비교할때 timezone.now를 불러와 비교하는 방식으로 하는 것이 더 적절한 방법인 것 같다

 

 

 

 

 

 

 

- 시도 3 -

 

문제점이 떠오른 후 조금 더 찾아보고 새로운 방법을 찾았다. pytz 모듈

pytz의 timezone function으로 naive한 datetime에 시간대를 부여해 aware로 만들 수 있다고 한다.

 

필요에 의해 한국 시간을 적용하기만 하였지만,

이후 부여한 시간대를 datetimefield의 시간대와 맞도록 조정할 수 있을듯!

 

 

 

 

 

datetime.now만 사용하면 순수한 시간대가 출력된다.

 

print(datetime.now(), "🐛") #naive

2023-07-04 17:19:43.412812 🐛

 

 

 

naive한 datetime.now에 timezone을 부여하면 시간대가 함께 출력된다

 

now = datetime.now(timezone('Asia/Seoul')) #aware
print(now, "🐛")

2023-07-04 17:20:51.113976+09:00 🐛

 

 

 

 

 

> 최종적으로 수정된 형태!

 

now = datetime.now(timezone('Asia/Seoul')) #aware

#close_at과 open_at은 원래의 형태로 되돌리고 %z를 추가하였다.
open_at = datetime.strptime(str(obj.open_at),"%Y-%m-%d %H:%M:%S%z")
close_at = datetime.strptime(str(obj.close_at),"%Y-%m-%d %H:%M:%S%z")

 

 

 

 

+++

해당 글을 생성하거나 수정하는데에서도 시간 유효성 체크를 하고 있지만 오류는 나지 않는데,

아마 create, update의 로직에서 save된 데이터를 활용하는 것이 아니고

새롭게 들어온 데이터를 점검하므로 datetime값이 마이크로초까지만 들어와서(=UTC값이 없어서) 오류가 나지 않는 것 같다.

 

이후 오류가 날 경우 수정 필요!