Lesson 6 of 10
Lesson 7.7 introduced datetime. Here's the full picture — including the mistakes that cause
real bugs.
The four types
from datetime import date, time, datetime, timedelta
print(date(2026, 3, 15))
print(time(14, 30))
print(datetime(2026, 3, 15, 14, 30))
print(timedelta(days=7))
date— a calendar daytime— a clock timedatetime— both togethertimedelta— a duration
Now
from datetime import date, datetime
today = date.today()
now = datetime.now()
print(isinstance(today, date))
print(isinstance(now, datetime))
print(now.year > 2000)
Arithmetic
Add or subtract a timedelta; subtracting two dates gives a timedelta:
from datetime import date, timedelta
start = date(2026, 3, 1)
print(start + timedelta(days=30))
print(start - timedelta(weeks=2))
end = date(2026, 12, 25)
gap = end - start
print(gap.days)
print(type(gap).__name__)
timedelta accepts days, hours, minutes, seconds, weeks — but not months or years,
because those have no fixed length. Adding "one month" to 31 January is ambiguous, so Python
refuses to guess. Use dateutil.relativedelta (third-party) if you need calendar-aware maths.
Formatting with strftime
Date to string:
from datetime import datetime
d = datetime(2026, 3, 15, 14, 30, 5)
print(d.strftime("%Y-%m-%d"))
print(d.strftime("%d/%m/%Y %H:%M"))
print(d.strftime("%B %d, %Y"))
print(d.strftime("%A"))
| Code | Means |
| --- | --- |
| %Y / %y | 4-digit / 2-digit year |
| %m / %B / %b | month number / full name / short name |
| %d | day of month |
| %A / %a | weekday full / short |
| %H / %I | hour 24 / 12 |
| %M / %S | minute / second |
| %p | AM or PM |
Parsing with strptime
String to date — same codes, describing the input format:
from datetime import datetime
d = datetime.strptime("2026-03-15", "%Y-%m-%d")
print(d.date())
d2 = datetime.strptime("15/03/2026 14:30", "%d/%m/%Y %H:%M")
print(d2)
A mismatch raises ValueError, so parsing user input needs try/except:
from datetime import datetime
def parse_date(text):
try:
return datetime.strptime(text, "%Y-%m-%d").date()
except ValueError:
return None
print(parse_date("2026-03-15"))
print(parse_date("not a date"))
Remember: strFtime formats, strPtime parses.
ISO format
For storing dates — in JSON, a database, a filename — use ISO 8601 and skip custom formats entirely:
from datetime import date, datetime
d = date(2026, 3, 15)
print(d.isoformat())
print(date.fromisoformat("2026-03-15"))
print(datetime.fromisoformat("2026-03-15T14:30:00"))
ISO sorts correctly as plain text, is unambiguous internationally, and is what json output
should carry. Never store 15/03/2026 — is that March or December? Depends who's reading.
Time zones
A datetime with no timezone is naive — it doesn't know where it is. That's the source of
most date bugs.
from datetime import datetime, timezone, timedelta
naive = datetime(2026, 3, 15, 14, 30)
print(naive.tzinfo)
aware = datetime(2026, 3, 15, 14, 30, tzinfo=timezone.utc)
print(aware.tzinfo)
phnom_penh = timezone(timedelta(hours=7))
local = aware.astimezone(phnom_penh)
print(local.strftime("%H:%M %Z"))
The rule that avoids trouble: store UTC, display local. Convert at the edges only. Comparing
a naive and an aware datetime raises TypeError, which is Python protecting you.
Try it yourself
Checkpoint
date,time,datetime,timedelta— day, clock, both, duration.- Subtracting dates gives a
timedelta;timedeltahas no months or years. strftimeformats out,strptimeparses in; a bad parse raisesValueError.- Store dates in ISO format — sortable, unambiguous, standard.
- Naive datetimes have no timezone; store UTC and convert only for display.
Questions about this lesson
Log in to ask a question.