docs: Add Calendar CRUD timezone issue resolution
Section 9 추가: - Calendar READ 실패 원인: naive datetime vs timezone-aware - 해결: parse_time_range, handle_calendar_query에 Asia/Seoul timezone 추가 - 테스트 결과: CRUD 67% → 100% 통과 - 교훈: Microservice 간 datetime 전송 시 timezone 필수 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
e41eb70335
commit
ff15e1d18f
@ -285,7 +285,122 @@ IntentType.CALENDAR_EVENT: [
|
|||||||
- 문서화 미완료: 위 보완 사항을 정리했으나, Slack/Frontend 사용자 가이드는 아직 업데이트되지 않음 (향후 DOCS/guide 추가 필요)
|
- 문서화 미완료: 위 보완 사항을 정리했으나, Slack/Frontend 사용자 가이드는 아직 업데이트되지 않음 (향후 DOCS/guide 추가 필요)
|
||||||
|
|
||||||
### 후속 TODO
|
### 후속 TODO
|
||||||
1. approval 완료 후에는 LLM을 재호출하지 않고 “등록 완료” 응답을 직접 반환
|
1. approval 완료 후에는 LLM을 재호출하지 않고 "등록 완료" 응답을 직접 반환
|
||||||
2. 일정 중복 감지(최근 N분 내 같은 날짜/시간이면 안내만) 기능 추가
|
2. 일정 중복 감지(최근 N분 내 같은 날짜/시간이면 안내만) 기능 추가
|
||||||
3. 사용자용 가이드 문서(DOCS/guide)에 승인 흐름 및 실패 메시지 내용을 반영
|
3. 사용자용 가이드 문서(DOCS/guide)에 승인 흐름 및 실패 메시지 내용을 반영
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Calendar CRUD Timezone 이슈 해결 (2025-11-16)
|
||||||
|
|
||||||
|
### 문제 상황
|
||||||
|
- **CREATE**: 일정 등록 성공 (event_id 반환, Google Calendar 확인됨)
|
||||||
|
- **READ**: 방금 등록한 일정 조회 실패 ("11월 17일에 등록된 일정이 없습니다")
|
||||||
|
- **DELETE**: 삭제는 성공하지만 잘못된 날짜 표시 ("2025-11-18 00:00" 대신 "2025-11-17 15:00"이어야 함)
|
||||||
|
|
||||||
|
### 근본 원인
|
||||||
|
```python
|
||||||
|
# 문제: naive datetime (timezone 없음) 사용
|
||||||
|
start_iso = f"{date}T{start_hour}:{start_min}:00" # "2025-11-17T15:00:00"
|
||||||
|
end_iso = f"{date}T{end_hour}:{end_min}:00" # "2025-11-17T16:00:00"
|
||||||
|
|
||||||
|
# Google Calendar API는 timezone 없는 datetime을 어떻게 해석할지 불확실
|
||||||
|
# skill-calendar 서비스가 Asia/Seoul로 해석하지만,
|
||||||
|
# rb8001에서 보내는 query datetime은 naive여서 매칭 실패
|
||||||
|
```
|
||||||
|
|
||||||
|
### 증상
|
||||||
|
1. **CREATE API 호출**:
|
||||||
|
- 전송: `{"start": "2025-11-17T15:00:00", "end": "2025-11-17T16:00:00"}`
|
||||||
|
- Google Calendar: Asia/Seoul timezone으로 저장됨
|
||||||
|
|
||||||
|
2. **READ API 호출**:
|
||||||
|
- 전송: `{"start_date": "2025-11-17T00:00:00", "end_date": "2025-11-17T23:59:59"}`
|
||||||
|
- skill-calendar: 이 datetime을 어떤 timezone으로 해석?
|
||||||
|
- 결과: 조회 실패 (timezone 불일치)
|
||||||
|
|
||||||
|
### 해결 방법
|
||||||
|
|
||||||
|
#### 수정 파일: `rb8001/app/router/calendar_handler.py`
|
||||||
|
|
||||||
|
**1. parse_time_range() 함수 (Line 397-432)**
|
||||||
|
```python
|
||||||
|
# Before
|
||||||
|
start_iso = f"{date}T{start_hour.zfill(2)}:{start_min}:00"
|
||||||
|
end_iso = f"{date}T{end_hour.zfill(2)}:{end_min}:00"
|
||||||
|
|
||||||
|
# After
|
||||||
|
from datetime import datetime
|
||||||
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
|
year, month, day = map(int, date.split('-'))
|
||||||
|
tz = ZoneInfo("Asia/Seoul")
|
||||||
|
|
||||||
|
start_dt = datetime(year, month, day, int(start_hour), int(start_min), 0, tzinfo=tz)
|
||||||
|
end_dt = datetime(year, month, day, int(end_hour), int(end_min), 0, tzinfo=tz)
|
||||||
|
|
||||||
|
return start_dt.isoformat(), end_dt.isoformat()
|
||||||
|
# Result: "2025-11-17T15:00:00+09:00", "2025-11-17T16:00:00+09:00"
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. handle_calendar_query() 함수 (Line 228-312)**
|
||||||
|
```python
|
||||||
|
# Before
|
||||||
|
today = datetime.now()
|
||||||
|
target_date = datetime(year, month, day)
|
||||||
|
|
||||||
|
# After
|
||||||
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
|
today = datetime.now(ZoneInfo("Asia/Seoul"))
|
||||||
|
target_date = datetime(year, month, day, tzinfo=ZoneInfo("Asia/Seoul"))
|
||||||
|
|
||||||
|
start_time = target_date.replace(hour=0, minute=0, second=0)
|
||||||
|
end_time = target_date.replace(hour=23, minute=59, second=59)
|
||||||
|
# Result: start_time.isoformat() = "2025-11-17T00:00:00+09:00"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 테스트 결과
|
||||||
|
|
||||||
|
**CRUD 테스트 (test_calendar_crud_proper.py)**:
|
||||||
|
```bash
|
||||||
|
docker exec rb8001 python3 /code/test_calendar_crud_proper.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**Before (67% 통과)**:
|
||||||
|
```
|
||||||
|
✅ PASS CREATE
|
||||||
|
❌ FAIL READ - "11월 17일에 등록된 일정이 없습니다"
|
||||||
|
✅ PASS DELETE
|
||||||
|
통과율: 2/3 (67%)
|
||||||
|
```
|
||||||
|
|
||||||
|
**After (100% 통과)**:
|
||||||
|
```
|
||||||
|
✅ PASS CREATE
|
||||||
|
✅ PASS READ - "11월 17일 일정: • 15:00 TDD 테스트 일정"
|
||||||
|
✅ PASS DELETE - "TDD 테스트 일정 (2025-11-17 15:00)"
|
||||||
|
통과율: 3/3 (100%)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 교훈
|
||||||
|
|
||||||
|
#### 1. Datetime은 항상 timezone-aware로
|
||||||
|
- Python에서 `datetime.now()` 대신 `datetime.now(ZoneInfo("Asia/Seoul"))` 사용
|
||||||
|
- ISO 8601 전송 시 반드시 timezone offset 포함 (`+09:00`)
|
||||||
|
- Google Calendar API는 timezone 정보 없으면 예측 불가능하게 동작
|
||||||
|
|
||||||
|
#### 2. Microservice 간 datetime 전송 규칙
|
||||||
|
- **절대 금지**: naive datetime (timezone 없음)
|
||||||
|
- **권장**: ISO 8601 with timezone (`2025-11-17T15:00:00+09:00`)
|
||||||
|
- **대안**: UTC로 통일 후 각 서비스에서 로컬 변환
|
||||||
|
|
||||||
|
#### 3. 테스트 시 timezone 검증 필수
|
||||||
|
- CREATE/READ/DELETE 모두 같은 timezone 기준 사용 확인
|
||||||
|
- 컨테이너 시스템 timezone과 코드 timezone 일치 여부 확인
|
||||||
|
- `docker exec rb8001 date` → "KST" 확인
|
||||||
|
|
||||||
|
### 관련 커밋
|
||||||
|
- `4cd0990`: fix: Add timezone awareness to calendar operations
|
||||||
|
- `fd7ec1c`: Revert README.md documentation changes
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user