Python Datetime: Python 날짜와 시간 완전 가이드
Updated on
Python에서 날짜와 시간 작업은 간단해야 하지만, 실제로는 거의 그렇지 않습니다. CSV 파일에서 날짜 문자열을 파싱해야 하는데, "01/02/2026"은 로케일에 따라 1월 2일 또는 2월 1일을 의미할 수 있습니다. 두 타임스탬프의 차이를 계산하고 싶지만, 하나는 문자열이고 다른 하나는 Unix 에포크입니다. 이번 달에 벌써 열 번째로 strftime과 strptime을 혼동합니다. 날짜와 시간 연산은 프로덕션 코드에서 버그, off-by-one 오류, 타임존 두통의 끊임없는 원인입니다.
그 결과는 단순한 불편함을 넘어섭니다. 재무 보고서의 잘못된 날짜 형식은 잘못된 계산을 의미합니다. 타임존을 무시하는 naive datetime 비교는 중복 cron 작업을 유발합니다. 잘못된 timedelta 계산은 고객에게 30일이 아닌 31일을 청구합니다.
Python의 내장 datetime 모듈은 깔끔하고 일관된 API로 이러한 문제를 해결합니다. 날짜, 시간, 타임스탬프, 기간을 위한 클래스를 제공합니다. 패턴을 배우면 -- 특히 strftime과 strptime의 차이를 이해하면 -- 서드파티 라이브러리 없이 모든 날짜 연산을 처리할 수 있습니다.
현재 날짜와 시간 가져오기
가장 일반적인 시작점은 현재 날짜와 시간을 가져오는 것입니다. datetime 모듈은 이를 위한 여러 가지 방법을 제공합니다.
from datetime import datetime, date
# 현재 날짜와 시간
now = datetime.now()
print(now) # 2026-02-10 14:30:45.123456
# 현재 날짜만
today = date.today()
print(today) # 2026-02-10
# datetime.today()는 datetime.now()와 비슷하지만 타임존 지원 없음
now_alt = datetime.today()
print(now_alt) # 2026-02-10 14:30:45.123456datetime.now()와 datetime.today()의 차이는 미묘하지만 중요합니다. datetime.now()는 타임존 인식 datetime을 위한 선택적 tz 매개변수를 받습니다. datetime.today()는 받지 않습니다. 대부분의 코드에서는 datetime.now()를 선호하세요.
from datetime import datetime
from zoneinfo import ZoneInfo
# 타임존 인식 현재 시간
utc_now = datetime.now(tz=ZoneInfo("UTC"))
print(utc_now) # 2026-02-10 14:30:45.123456+00:00
tokyo_now = datetime.now(tz=ZoneInfo("Asia/Tokyo"))
print(tokyo_now) # 2026-02-10 23:30:45.123456+09:00Datetime 객체 생성
개별 구성 요소 또는 기존 데이터에서 datetime 객체를 생성할 수 있습니다.
from datetime import datetime, date, time
# 연, 월, 일, 시, 분, 초에서
dt = datetime(2026, 3, 15, 9, 30, 0)
print(dt) # 2026-03-15 09:30:00
# 날짜만
d = date(2026, 12, 25)
print(d) # 2026-12-25
# 시간만
t = time(14, 30, 0)
print(t) # 14:30:00
# 날짜와 시간 결합
combined = datetime.combine(d, t)
print(combined) # 2026-12-25 14:30:00기존 datetime에서 구성 요소를 추출할 수도 있습니다.
from datetime import datetime
dt = datetime(2026, 3, 15, 9, 30, 45)
print(dt.year) # 2026
print(dt.month) # 3
print(dt.day) # 15
print(dt.hour) # 9
print(dt.minute) # 30
print(dt.second) # 45
print(dt.weekday()) # 6 (일요일, 월요일=0)
print(dt.isoformat()) # 2026-03-15T09:30:45strftime으로 날짜 포맷팅
strftime은 "string format time"(문자열 포맷 시간)의 약자입니다. datetime 객체를 포맷된 문자열로 변환합니다. 날짜 구성 요소로 대체되는 지시자가 포함된 포맷 문자열을 전달합니다.
from datetime import datetime
dt = datetime(2026, 3, 15, 9, 5, 7)
# 일반적인 포맷
print(dt.strftime("%Y-%m-%d")) # 2026-03-15
print(dt.strftime("%d/%m/%Y")) # 15/03/2026
print(dt.strftime("%B %d, %Y")) # March 15, 2026
print(dt.strftime("%Y-%m-%d %H:%M:%S")) # 2026-03-15 09:05:07
print(dt.strftime("%I:%M %p")) # 09:05 AM
print(dt.strftime("%A, %B %d, %Y")) # Sunday, March 15, 2026기억하는 방법: strftime = string from time (datetime에서 문자열로).
strftime 포맷 코드 참조
| 코드 | 의미 | 예시 |
|---|---|---|
%Y | 4자리 연도 | 2026 |
%y | 2자리 연도 | 26 |
%m | 0으로 채운 월 | 03 |
%B | 월 전체 이름 | March |
%b | 월 약어 | Mar |
%d | 0으로 채운 일 | 15 |
%A | 요일 전체 이름 | Sunday |
%a | 요일 약어 | Sun |
%H | 시간 (24시간, 0 채움) | 09 |
%I | 시간 (12시간, 0 채움) | 09 |
%M | 분 (0 채움) | 05 |
%S | 초 (0 채움) | 07 |
%p | AM/PM | AM |
%f | 마이크로초 (6자리 0 채움) | 000000 |
%z | UTC 오프셋 (+HHMM 또는 -HHMM) | +0000 |
%Z | 타임존 이름 | UTC |
%j | 연중 일수 (001-366) | 074 |
%% | 리터럴 % 문자 | % |
가장 많이 사용되는 포맷 패턴
| 패턴 | 포맷 문자열 | 출력 |
|---|---|---|
| ISO 8601 | %Y-%m-%dT%H:%M:%S | 2026-03-15T09:05:07 |
| 미국 날짜 | %m/%d/%Y | 03/15/2026 |
| 유럽 날짜 | %d/%m/%Y | 15/03/2026 |
| 읽기 쉬운 날짜 | %B %d, %Y | March 15, 2026 |
| 로그 타임스탬프 | %Y-%m-%d %H:%M:%S | 2026-03-15 09:05:07 |
| 12시간 시간 | %I:%M:%S %p | 09:05:07 AM |
| 컴팩트 날짜 | %Y%m%d | 20260315 |
| 파일 안전 타임스탬프 | %Y%m%d_%H%M%S | 20260315_090507 |
strptime으로 문자열 파싱
strptime은 "string parse time"(문자열 파싱 시간)의 약자입니다. strftime의 반대로 -- 문자열을 datetime 객체로 변환합니다. 문자열과 해당 포맷을 제공합니다.
from datetime import datetime
# 다양한 날짜 문자열 포맷 파싱
dt1 = datetime.strptime("2026-03-15", "%Y-%m-%d")
print(dt1) # 2026-03-15 00:00:00
dt2 = datetime.strptime("15/03/2026", "%d/%m/%Y")
print(dt2) # 2026-03-15 00:00:00
dt3 = datetime.strptime("March 15, 2026 09:30 AM", "%B %d, %Y %I:%M %p")
print(dt3) # 2026-03-15 09:30:00
dt4 = datetime.strptime("2026-03-15T09:30:00", "%Y-%m-%dT%H:%M:%S")
print(dt4) # 2026-03-15 09:30:00기억하세요: strptime = string parse time (문자열에서 datetime으로).
파싱 오류 처리
문자열이 포맷과 일치하지 않으면 Python은 ValueError를 발생시킵니다. 사용자 입력이나 외부 데이터를 파싱할 때는 항상 strptime 호출을 오류 처리로 감싸세요.
from datetime import datetime
def safe_parse_date(date_string, fmt="%Y-%m-%d"):
"""Parse a date string safely, returning None on failure."""
try:
return datetime.strptime(date_string, fmt)
except ValueError as e:
print(f"Could not parse '{date_string}': {e}")
return None
# Valid input
print(safe_parse_date("2026-03-15")) # 2026-03-15 00:00:00
# Invalid input
print(safe_parse_date("15-03-2026")) # Could not parse '15-03-2026': ...
print(safe_parse_date("not a date")) # Could not parse 'not a date': ...여러 포맷 파싱
예측할 수 없는 포맷의 날짜를 받을 때는 여러 패턴을 시도합니다.
from datetime import datetime
def parse_flexible_date(date_string):
"""Try multiple date formats and return the first match."""
formats = [
"%Y-%m-%d",
"%d/%m/%Y",
"%m/%d/%Y",
"%B %d, %Y",
"%b %d, %Y",
"%Y-%m-%dT%H:%M:%S",
"%Y-%m-%d %H:%M:%S",
]
for fmt in formats:
try:
return datetime.strptime(date_string, fmt)
except ValueError:
continue
raise ValueError(f"No matching format found for '{date_string}'")
print(parse_flexible_date("2026-03-15")) # 2026-03-15 00:00:00
print(parse_flexible_date("March 15, 2026")) # 2026-03-15 00:00:00
print(parse_flexible_date("15/03/2026")) # 2026-03-15 00:00:00timedelta를 사용한 날짜 연산
timedelta 클래스는 기간 -- 두 날짜 또는 시간의 차이를 나타냅니다. timedelta 객체를 더하거나 빼서 날짜를 앞뒤로 이동할 수 있습니다.
from datetime import datetime, timedelta
now = datetime(2026, 2, 10, 12, 0, 0)
# 일수 더하기
tomorrow = now + timedelta(days=1)
print(tomorrow) # 2026-02-11 12:00:00
# 일수 빼기
last_week = now - timedelta(weeks=1)
print(last_week) # 2026-02-03 12:00:00
# 시간과 분 더하기
later = now + timedelta(hours=5, minutes=30)
print(later) # 2026-02-10 17:30:00
# 여러 단위 결합
future = now + timedelta(weeks=2, days=3, hours=6)
print(future) # 2026-02-27 18:00:00시간 차이 계산
하나의 datetime에서 다른 datetime을 빼면 timedelta가 반환됩니다.
from datetime import datetime
start = datetime(2026, 1, 1)
end = datetime(2026, 12, 31)
diff = end - start
print(diff) # 364 days, 0:00:00
print(diff.days) # 364
print(diff.total_seconds()) # 31449600.0timedelta 생성자 매개변수
| 매개변수 | 설명 | 예시 |
|---|---|---|
weeks | 주 수 | timedelta(weeks=2) = 14일 |
days | 일 수 | timedelta(days=30) |
hours | 시간 수 | timedelta(hours=12) |
minutes | 분 수 | timedelta(minutes=45) |
seconds | 초 수 | timedelta(seconds=120) |
milliseconds | 밀리초 수 | timedelta(milliseconds=500) |
microseconds | 마이크로초 수 | timedelta(microseconds=1000) |
모든 매개변수를 결합할 수 있습니다. 내부적으로 timedelta는 days, seconds, microseconds만 저장합니다. 나머지는 모두 변환됩니다.
from datetime import timedelta
delta = timedelta(weeks=1, days=2, hours=3, minutes=30, seconds=45)
print(delta) # 9 days, 3:30:45
print(delta.days) # 9
print(delta.seconds) # 12645 (3*3600 + 30*60 + 45)
print(delta.total_seconds()) # 790245.0날짜 비교
Datetime 객체는 모든 표준 비교 연산자를 지원합니다. 이를 통해 날짜 정렬과 필터링이 간단해집니다.
from datetime import datetime
dt1 = datetime(2026, 1, 1)
dt2 = datetime(2026, 6, 15)
dt3 = datetime(2026, 12, 31)
print(dt1 < dt2) # True
print(dt3 > dt2) # True
print(dt1 == dt2) # False
print(dt1 != dt2) # True날짜 정렬
from datetime import datetime
dates = [
datetime(2026, 12, 25),
datetime(2026, 1, 1),
datetime(2026, 7, 4),
datetime(2026, 2, 14),
]
sorted_dates = sorted(dates)
for d in sorted_dates:
print(d.strftime("%B %d, %Y"))
# January 01, 2026
# February 14, 2026
# July 04, 2026
# December 25, 2026타임스탬프 작업
Unix 타임스탬프는 1970년 1월 1일(Unix 에포크) 이후의 초를 나타냅니다. datetime 모듈은 타임스탬프와 datetime 객체 간의 변환이 가능합니다.
from datetime import datetime, timezone
# Datetime에서 타임스탬프로
dt = datetime(2026, 3, 15, 9, 30, 0)
ts = dt.timestamp()
print(ts) # 1773814200.0 (로컬 타임존에 따라 다름)
# 타임스탬프에서 Datetime으로 (타임존 인식, 권장)
dt_aware = datetime.fromtimestamp(ts, tz=timezone.utc)
print(dt_aware) # 2026-03-15 01:30:00+00:00중요: 타임존 없는 datetime.fromtimestamp()는 로컬 시간을 반환합니다. UTC의 경우 항상 tz=timezone.utc를 전달하세요.
타임존 처리
Naive datetime(타임존 정보 없음)은 버그의 일반적인 원인입니다. Python은 타임존 인식 datetime을 위한 두 가지 내장 접근 방식을 제공합니다.
datetime.timezone 사용 (내장)
timezone 클래스는 고정 UTC 오프셋을 처리합니다.
from datetime import datetime, timezone, timedelta
# UTC
utc_now = datetime.now(timezone.utc)
print(utc_now) # 2026-02-10 14:30:00+00:00
# 고정 오프셋 (예: 인도의 UTC+5:30)
ist = timezone(timedelta(hours=5, minutes=30))
india_time = datetime.now(ist)
print(india_time) # 2026-02-10 20:00:00+05:30
# 타임존 간 변환
utc_time = datetime(2026, 3, 15, 12, 0, 0, tzinfo=timezone.utc)
eastern = timezone(timedelta(hours=-5))
eastern_time = utc_time.astimezone(eastern)
print(eastern_time) # 2026-03-15 07:00:00-05:00zoneinfo 사용 (Python 3.9+)
서머타임을 올바르게 처리하는 명명된 타임존에는 zoneinfo 모듈을 사용합니다.
from datetime import datetime
from zoneinfo import ZoneInfo
# 명명된 타임존
utc = ZoneInfo("UTC")
eastern = ZoneInfo("America/New_York")
tokyo = ZoneInfo("Asia/Tokyo")
# 타임존 인식 datetime 생성
dt = datetime(2026, 7, 15, 12, 0, 0, tzinfo=utc)
print(dt) # 2026-07-15 12:00:00+00:00
# 다른 타임존으로 변환
print(dt.astimezone(eastern)) # 2026-07-15 08:00:00-04:00 (EDT)
print(dt.astimezone(tokyo)) # 2026-07-15 21:00:00+09:00Naive vs Aware Datetime
| 특성 | Naive | Aware |
|---|---|---|
| 타임존 정보 있음 | 아니오 | 예 |
| 존 간 비교에서 안전 | 아니오 | 예 |
datetime.now()로 생성 | 예 | 아니오 (tz 전달 시 제외) |
| 연산에서 혼합 가능 | 다른 naive와만 | 다른 aware와만 |
| 프로덕션에서 권장 | 아니오 | 예 |
naive datetime과 aware datetime을 비교하거나 빼는 것은 불가능합니다. Python은 TypeError를 발생시킵니다.
실용적인 예제
사람의 나이 계산
from datetime import date
def calculate_age(birth_date):
"""Calculate age in years from a birth date."""
today = date.today()
age = today.year - birth_date.year
if (today.month, today.day) < (birth_date.month, birth_date.day):
age -= 1
return age
birthday = date(1995, 8, 20)
print(f"Age: {calculate_age(birthday)} years") # Age: 30 years (as of Feb 2026)날짜 범위 생성
from datetime import date, timedelta
def date_range(start, end, step_days=1):
"""Generate dates from start to end (inclusive)."""
current = start
while current <= end:
yield current
current += timedelta(days=step_days)
start = date(2026, 2, 1)
end = date(2026, 2, 7)
for d in date_range(start, end):
print(d.strftime("%A, %B %d"))두 날짜 사이의 영업일 수 세기
from datetime import date, timedelta
def business_days_between(start, end):
"""Count weekdays (Mon-Fri) between two dates, excluding endpoints."""
count = 0
current = start + timedelta(days=1)
while current < end:
if current.weekday() < 5: # 0=Mon, 4=Fri
count += 1
current += timedelta(days=1)
return count
start = date(2026, 2, 1)
end = date(2026, 2, 28)
print(f"Business days: {business_days_between(start, end)}") # Business days: 19Jupyter에서 Datetime 실험하기
날짜와 시간 연산은 인터랙티브한 실험에서 큰 이점을 얻습니다. CSV에서 일관되지 않은 날짜 형식을 파싱하거나 타임존 변환을 디버깅할 때, 노트북 셀에서 각 단계를 테스트할 수 있으면 상당한 시간을 절약할 수 있습니다.
RunCell (opens in a new tab)은 Jupyter 노트북 내에서 직접 작동하는 AI 에이전트입니다. datetime 객체를 검사하고, 데이터에 적합한 strftime/strptime 포맷 코드를 제안하며, 타임존 변환 문제를 실시간으로 디버깅하는 데 도움을 줍니다.
strftime vs strptime: 빠른 비교
| strftime | strptime | |
|---|---|---|
| 전체 이름 | String Format Time | String Parse Time |
| 방향 | datetime -> 문자열 | 문자열 -> datetime |
| 호출 대상 | datetime 객체 | datetime 클래스 |
| 구문 | dt.strftime("%Y-%m-%d") | datetime.strptime(s, "%Y-%m-%d") |
| 반환 | 포맷된 문자열 | datetime 객체 |
| 예외 | 없음 | 포맷 불일치 시 ValueError |
| 사용 사례 | 표시, 로깅, 파일 이름 | CSV, API 응답, 사용자 입력 파싱 |
from datetime import datetime
# strftime: datetime -> string
dt = datetime(2026, 3, 15, 9, 30)
formatted = dt.strftime("%B %d, %Y at %I:%M %p")
print(formatted) # March 15, 2026 at 09:30 AM
# strptime: string -> datetime
parsed = datetime.strptime("March 15, 2026 at 09:30 AM", "%B %d, %Y at %I:%M %p")
print(parsed) # 2026-03-15 09:30:00FAQ
Python에서 현재 날짜와 시간을 가져오려면?
datetime 모듈의 datetime.now()를 사용하세요. 날짜만 필요하면 date.today()를 사용하세요. 타임존 인식 현재 시간의 경우 타임존을 전달하세요: datetime.now(tz=timezone.utc). 이것들은 표준 접근 방식이며 서드파티 라이브러리가 필요하지 않습니다.
Python에서 strftime과 strptime의 차이는?
strftime은 datetime 객체를 포맷된 문자열로 변환합니다(string from time). strptime은 문자열을 파싱하여 datetime 객체로 변환합니다(string parse time). strftime은 문자열을 출력하고, strptime은 문자열을 입력한다고 기억하세요. 둘 다 %Y, %m, %d와 같은 동일한 포맷 코드를 사용합니다.
Python에서 날짜에 일수를 추가하려면?
timedelta 클래스를 사용하세요. datetime에서 임포트한 후 날짜에 더하세요: new_date = old_date + timedelta(days=7). weeks, hours, minutes, seconds도 매개변수로 사용할 수 있습니다. 빼기도 같은 방식으로 작동합니다: past_date = today - timedelta(days=30).
Python에서 문자열을 datetime으로 변환하려면?
datetime.strptime(string, format)을 사용하세요. 입력과 일치하는 포맷 문자열을 제공해야 합니다. 예를 들어, datetime.strptime("2026-03-15", "%Y-%m-%d")는 ISO 날짜를 파싱합니다. 문자열이 포맷과 일치하지 않으면 Python은 ValueError를 발생시키므로, 외부 데이터의 경우 호출을 try/except 블록으로 감싸세요.
Python datetime에서 타임존을 처리하려면?
Python 3.9 이상에서는 내장 zoneinfo 모듈을 사용하세요: from zoneinfo import ZoneInfo. datetime.now(tz=ZoneInfo("UTC"))로 타임존 인식 datetime을 생성하고 dt.astimezone(ZoneInfo("America/New_York"))으로 존 간 변환을 수행하세요. 고정 UTC 오프셋에는 datetime.timezone(timedelta(hours=N))을 사용하세요. 프로덕션 코드에서는 naive datetime을 피하세요.
결론
Python의 datetime 모듈은 날짜와 시간 연산에 필요한 모든 것을 제공합니다: 날짜 생성, 표시를 위한 포맷팅, 문자열에서 파싱, 연산 수행, 타임존 처리. 핵심 패턴은 한번 배우면 일관되고 예측 가능합니다.
핵심 구분을 기억하세요: strftime은 datetime을 문자열로 포맷하고, strptime은 문자열을 datetime으로 파싱합니다. 날짜 연산에는 timedelta를 사용하세요. 올바른 타임존 처리에는 zoneinfo (Python 3.9+)를 사용하세요. 미묘한 비교 버그를 피하기 위해 프로덕션 코드에서는 datetime을 타임존 인식으로 유지하세요.
대부분의 프로젝트에서는 내장 datetime 모듈로 충분합니다. 현대 Python에서는 pytz가 필요하지 않습니다 -- zoneinfo가 명명된 타임존을 네이티브로 처리합니다. datetime.now(tz=timezone.utc)를 기준점으로 시작하고, 사용자에게 표시할 때만 로컬 타임존으로 변환하세요.