C.W.K.
Stream
Lesson 03 of 06 · published

datetime — Dates, Times, and Timezones

~22 min · datetime, timezone, isoformat, strftime

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

The four core types

date — calendar date with no time. time — clock time with no date. datetime — both. timedelta — a duration (positive or negative). Most of your code uses datetime and timedelta; the others come up less often.

Naive vs aware — the timezone distinction

A "naive" datetime has no timezone info. An "aware" datetime has a tzinfo attached. Mixing them in arithmetic raises. The 2020s answer: always use aware datetimes. Construct them with datetime.now(timezone.utc) or datetime.fromisoformat with a timezone in the string. The zoneinfo module (3.9+) gives you ZoneInfo("Asia/Seoul") for IANA-named zones.

The ISO 8601 lifeline

datetime.fromisoformat("2026-05-02T15:30:00+09:00") parses, dt.isoformat() emits. Round-trippable, sortable as strings, unambiguous. Modern code defaults to ISO 8601 for serialization. strftime/strptime handle other formats; only reach for them when you must.

timedelta — durations and arithmetic

timedelta(days=7, hours=3) is a duration. Datetimes minus datetimes give a timedelta. Datetimes plus timedelta gives a datetime. Comparisons work. Common idiom: now + timedelta(days=30) for "30 days from now."

Principle: Store and transmit datetimes as ISO 8601 strings with a UTC offset. Convert to local time only at display. Mixing naive and aware datetimes is the most common datetime bug; using aware everywhere makes it impossible.

Code

Construction and formatting·python
from datetime import datetime, date, time, timedelta, timezone
from zoneinfo import ZoneInfo

# Today, now
print(date.today())                # 2026-05-02
print(datetime.now())              # naive — bad! avoid in modern code
print(datetime.now(timezone.utc))  # aware — good
print(datetime.now(ZoneInfo("Asia/Seoul")))   # aware in Seoul time

# Construction
dt = datetime(2026, 5, 2, 15, 30, tzinfo=ZoneInfo("Asia/Seoul"))
print(dt)                          # 2026-05-02 15:30:00+09:00

# ISO format — the modern standard
print(dt.isoformat())              # 2026-05-02T15:30:00+09:00
print(datetime.fromisoformat("2026-05-02T15:30:00+09:00"))   # round-trip
Naive vs aware — and why the distinction matters·python
from datetime import datetime, timezone
from zoneinfo import ZoneInfo

naive = datetime(2026, 5, 2, 15, 30)
aware = datetime(2026, 5, 2, 15, 30, tzinfo=timezone.utc)

# Mixing them in arithmetic raises
try:
    aware - naive
except TypeError as e:
    print("failed:", e)

# Convert naive to aware (assume it's UTC)
aware_from_naive = naive.replace(tzinfo=timezone.utc)
print(aware - aware_from_naive)        # 0 — same instant

# Convert aware to a different zone
seoul = aware.astimezone(ZoneInfo("Asia/Seoul"))
print(seoul)                            # 2026-05-03 00:30:00+09:00
timedelta — arithmetic on dates·python
from datetime import datetime, timedelta, timezone

now = datetime.now(timezone.utc)
in_a_week = now + timedelta(days=7)
yesterday = now - timedelta(days=1)

print(in_a_week - now)                  # 7 days, 0:00:00
print(now - yesterday)                  # 1 day, 0:00:00

# Total seconds
delta = in_a_week - now
print(delta.total_seconds())           # 604800

# Components
print(delta.days)                       # 7
print(timedelta(hours=25).days)         # 1   — hours overflow into days
strftime / strptime — non-ISO formats·python
from datetime import datetime

dt = datetime(2026, 5, 2, 15, 30)

# strftime — datetime to string
print(dt.strftime("%Y-%m-%d"))          # 2026-05-02
print(dt.strftime("%B %d, %Y"))         # May 02, 2026
print(dt.strftime("%I:%M %p"))          # 03:30 PM

# strptime — string to datetime
parsed = datetime.strptime("05/02/2026 15:30", "%m/%d/%Y %H:%M")
print(parsed)

# When you can, prefer fromisoformat. Only use strptime
# for non-ISO formats you can't change.
Common operations — diff in days, day name, weekday·python
from datetime import date, timedelta

birthday = date(2025, 6, 15)
today = date.today()

diff = (today - birthday).days
print(f"{diff} days since birthday")

# Day of week — 0=Monday, 6=Sunday
print(today.weekday())                  # int
print(["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"][today.weekday()])

# Day name via strftime
print(today.strftime("%A"))             # 'Saturday' (or whatever)

# Find next Monday
days_until_monday = (7 - today.weekday()) % 7 or 7
print(today + timedelta(days=days_until_monday))

External links

Exercise

(a) Get the current time in both UTC and Asia/Seoul, and print the difference (should be 9 hours). Use zoneinfo.ZoneInfo. (b) Compute the date that is exactly 100 days from today. Print it as ISO format. (c) Parse the string "2026-12-25T10:00:00+09:00" with fromisoformat and print how many days from now until that datetime.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.