파이썬 표준 라이브러리로 시간 다루기: datetime 완전 정복
시리즈 03 – 날짜/시간 연산, 타임존, 포맷 변환까지 한 번에
시간은 단순히 “문자열”이 아닙니다. 날짜가 하루씩 넘어가고, 월이 바뀌고, 서머타임이 끼어들고, 지역마다 기준이 달라집니다. 그래서 날짜/시간을 다룰 때는 “표시용 문자열”과 “계산 가능한 시간 값”을 구분하고, 타임존까지 포함해 다루는 습관이 중요합니다.

이 글에서는 datetime 모듈을 중심으로 다음을 정리합니다.
- 현재 시각과 날짜 만들기
- 기간 계산(
timedelta) - 문자열 포맷/파싱(
strftime,strptime) - 타임존 처리(
timezone,zoneinfo) - 자주 마주치는 주의점과 안정적인 패턴
1. datetime은 무엇을 제공하나?
datetime에는 비슷해 보이지만 역할이 다른 타입들이 있습니다.
date: 연-월-일만 필요할 때time: 시:분:초만 필요할 때datetime: 날짜+시간(가장 많이 씀)timedelta: 시간의 “차이”(기간)timezone: 고정 오프셋 타임존(예: UTC+9처럼 오프셋이 변하지 않는 경우)
그리고 파이썬 3.9+에서는 표준 라이브러리로 zoneinfo가 들어오면서, 지역 타임존(예: Asia/Tokyo)을 다루기 쉬워졌습니다.
2. “지금”을 얻는 방법: naive vs aware부터 정리
2.1 naive / aware란?
datetime 객체는 크게 두 부류가 있습니다.
- naive datetime: 타임존 정보가 없음
- aware datetime: 타임존 정보(
tzinfo)가 있음
둘을 섞어 연산/비교하면 에러가 나기도 하고, 더 조심해야 할 건 “에러 없이 돌아가는데 결과가 의도와 다를 수 있다”는 점입니다.
2.2 추천되는 기본값: UTC aware로 시작하기
저장/계산 기준을 UTC로 통일하면 깔끔해지는 경우가 많습니다.
from datetime import datetime, timezone
utc_now = datetime.now(timezone.utc) # aware (UTC)
print(utc_now)
로컬 시각이 필요하면 “표시 단계”에서 변환합니다.
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
utc_now = datetime.now(timezone.utc)
tokyo_now = utc_now.astimezone(ZoneInfo("Asia/Tokyo"))
print(utc_now)
print(tokyo_now)
3. 날짜/시간 계산: timedelta가 중심
3.1 더하기/빼기
from datetime import datetime, timedelta, timezone
now = datetime.now(timezone.utc)
print(now + timedelta(days=3))
print(now - timedelta(hours=2))
3.2 두 시각의 차이
from datetime import datetime, timezone
a = datetime(2026, 1, 1, tzinfo=timezone.utc)
b = datetime(2026, 1, 10, tzinfo=timezone.utc)
delta = b - a
print(delta.days) # 9
print(delta.total_seconds()) # 777600.0
3.3 timedelta로 “기간”을 표현할 때의 감각
timedelta는 일/초/마이크로초 단위로 동작합니다.
“한 달 뒤”처럼 달 길이가 달라지는 표현은 timedelta(days=30)로 대충 맞출 수는 있어도, 달력 기준의 “다음 달 같은 날짜”는 다른 접근이 필요합니다(이 부분은 표준 라이브러리 범위 밖에서 dateutil 같은 도구가 많이 쓰입니다).
4. 포맷과 파싱: strftime / strptime
4.1 datetime → 문자열(strftime)
from datetime import datetime, timezone
dt = datetime(2026, 1, 30, 14, 5, 0, tzinfo=timezone.utc)
print(dt.strftime("%Y-%m-%d %H:%M:%S %z"))
자주 쓰는 포맷 패턴:
%Y-%m-%d: 2026-01-30%H:%M:%S: 14:05:00%z: +0000 (UTC 오프셋)
4.2 문자열 → datetime(strptime)
from datetime import datetime
s = "2026-01-30 14:05:00"
dt = datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
print(dt)
여기서 생성된 dt는 naive입니다. 타임존을 명확히 하고 싶다면 tzinfo를 붙여줘야 합니다.
from datetime import datetime, timezone
s = "2026-01-30 14:05:00"
dt = datetime.strptime(s, "%Y-%m-%d %H:%M:%S").replace(tzinfo=timezone.utc)
print(dt)
replace(tzinfo=...)는 “시간 값을 변환”하는 게 아니라 그 시각에 타임존 라벨을 붙이는 동작입니다. 변환이 필요하면astimezone()을 사용합니다(다음 섹션 참고).
5. 타임존: timezone과 zoneinfo를 구분해서 쓰기
5.1 고정 오프셋이면 timezone
예: UTC, UTC+9처럼 오프셋이 고정인 경우
from datetime import datetime, timezone, timedelta
kst_fixed = timezone(timedelta(hours=9))
dt = datetime(2026, 1, 30, 12, 0, tzinfo=kst_fixed)
print(dt)
5.2 지역 타임존이면 zoneinfo
서머타임처럼 오프셋이 변할 수 있는 지역은 zoneinfo가 적합합니다.
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
utc_now = datetime.now(timezone.utc)
ny_now = utc_now.astimezone(ZoneInfo("America/New_York"))
print(ny_now)
5.3 replace(tzinfo=...) vs astimezone(...) 차이
replace(tzinfo=...): 시각 값은 그대로, 타임존만 “라벨링”astimezone(...): 같은 순간을 다른 타임존의 시각으로 “변환”
이 차이를 알고 있으면 타임존 관련 버그를 많이 줄일 수 있습니다.
6. 자주 마주치는 주의점 정리
6.1 naive/aware 혼용
- 내부 계산용 시각은 aware(UTC) 로 맞추는 편이 안정적입니다.
- 외부 입력은 들어오는 순간 타임존을 결정하고 통일하는 것이 좋습니다.
6.2 “로컬 시간”은 환경에 따라 달라질 수 있음
datetime.now()는 실행 환경의 로컬 설정을 따릅니다. 컨테이너/서버 환경에서는 로컬이 UTC일 수도, 아닐 수도 있습니다.
환경 차이를 줄이려면 datetime.now(timezone.utc) 같은 방식이 낫습니다.
6.3 문자열 파싱의 포맷 불일치
strptime은 포맷이 1글자라도 다르면 실패합니다. 입력 포맷이 여러 가지면 “전처리” 또는 포맷 후보를 단계적으로 시도하는 코드가 필요합니다(가능하면 입력 포맷을 하나로 제한하는 편이 더 좋습니다).
7. 많이 쓰는 패턴 3가지
7.1 ISO 8601 형태로 저장하기
표준적인 문자열 표현이 필요하다면 isoformat()이 편합니다.
from datetime import datetime, timezone
dt = datetime.now(timezone.utc)
print(dt.isoformat()) # 예: 2026-01-30T05:12:34.567890+00:00
7.2 “오늘 날짜”로 파일명 만들기
from datetime import datetime
stamp = datetime.now().strftime("%Y%m%d")
filename = f"report_{stamp}.json"
print(filename)
7.3 특정 시각까지 남은 시간 계산하기
from datetime import datetime, timezone
target = datetime(2026, 2, 1, 0, 0, tzinfo=timezone.utc)
now = datetime.now(timezone.utc)
remaining = target - now
print(remaining)
print(remaining.total_seconds())
8. 마무리
datetime은 “날짜 문자열 만들기”를 넘어, 시간을 연산 가능한 값으로 다루게 해주는 핵심 도구입니다.
여기에 타임존(timezone, zoneinfo)까지 포함하면, 실행 환경이 달라져도 결과가 흔들리지 않는 코드를 만들기 쉬워집니다.
다음 편에서는 random에 대하여 난수 생성/샘플링/셔플/시드 그리고 보안용 난수(secrets)까지 묶어서 정리해보겠습니다.
관련글 :
이전 시리즈 보기