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으로 합쳐보는 방법을 적용해보았다.
둘 모두 잘 작동한다
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값이 없어서) 오류가 나지 않는 것 같다.
이후 오류가 날 경우 수정 필요!
'AI 웹개발반 > Python, Django' 카테고리의 다른 글
[DRF] 다중(bulk) create, delete 구현 (0) | 2024.01.11 |
---|---|
[TIL] 코드 리팩토링 Code Refactoring (0) | 2023.07.07 |
[Django] JSONfield (0) | 2023.07.04 |
[Django] raise serializers.ValidationError 사용 시 assertEqual로 에러 메세지 비교하기 (0) | 2023.07.01 |
[Django + Vue] Profile에 새로운 내용 가져와 추가하기 (0) | 2023.06.30 |