날짜·시간 오류는 대부분 “문자열 형식”보다 시간대가 있는 값과 없는 값을 섞는 것에서 발생합니다. 서버와 API에서는 UTC 기준의 시간대 포함 datetime을 사용하고, 화면에 표시할 때 사용자의 지역 시간대로 변환하는 방식이 안전합니다.
[목차]
aware와 naive datetime
- naive: 시간대 정보가 없어 어느 지역의 시각인지 스스로 설명하지 못합니다.
- aware:
tzinfo를 포함해 UTC상의 한 시점을 식별할 수 있습니다.
from datetime import UTC, datetime
now_utc = datetime.now(UTC)
print(now_utc.isoformat())
datetime.utcnow()은 naive 값을 반환하므로 새 코드에서는 datetime.now(UTC)처럼 명시적인 방식을 사용합니다.
한국 시간으로 변환하기
from datetime import UTC, datetime
from zoneinfo import ZoneInfo
seoul = ZoneInfo("Asia/Seoul")
created_at = datetime.now(UTC)
local_time = created_at.astimezone(seoul)
print(local_time.strftime("%Y-%m-%d %H:%M:%S %Z"))
ZoneInfo는 IANA 시간대 규칙을 사용하므로 일광절약시간이 있는 지역도 날짜에 맞는 오프셋을 적용합니다. Windows 등 시스템에 시간대 데이터가 없는 환경을 함께 지원한다면 프로젝트 의존성에 tzdata를 명시하는 방안을 검토합니다.
ISO 8601 문자열 파싱과 출력
from datetime import datetime
value = "2026-08-27T10:30:00+09:00"
dt = datetime.fromisoformat(value)
assert dt.tzinfo is not None
print(dt.isoformat(timespec="seconds"))
외부 입력은 형식과 시간대 포함 여부를 검증해야 합니다. 날짜만 필요한 값과 실제 시점을 나타내는 값을 같은 타입으로 저장하지 않는 것이 좋습니다.
기간 계산
from datetime import UTC, datetime, timedelta
started_at = datetime.now(UTC)
expires_at = started_at + timedelta(days=7)
remaining = expires_at - started_at
print(remaining.total_seconds())
timedelta.seconds는 전체 기간의 초가 아니라 하루를 제외한 나머지 초이므로 전체 초가 필요하면 total_seconds()를 사용합니다. 프로그램 실행 시간처럼 시스템 시각 변경의 영향을 받으면 안 되는 측정에는 time.monotonic()을 사용하세요.
실무 저장 원칙
- DB에는 UTC와 명확한 타입으로 저장합니다.
- API는 오프셋이 포함된 ISO 8601 문자열을 사용합니다.
- 사용자 입력의 지역 시간대는 별도 값으로 받습니다.
- 표시는 마지막 단계에서만 지역 시간대로 변환합니다.
- 일광절약시간 전환일과 월말·윤년을 테스트합니다.
공식 참고자료
관련 글
내용 검토 및 업데이트: 2026년 8월 27일