The milk carton says 'use by Friday', and Friday is not a number of days. Dates need their own arithmetic. What is the difference between a calendar date and a moment in time?
The datetime module separates calendar dates, clock times, and durations. Start with date and timedelta for most beginner tasks. Add time zones only when the data actually represents a moment that different regions observe. You will also separate dates from instants, parse ISO text, and avoid treating a month as 30 days.
Create a date
date(year, month, day) validates its arguments and gives you an immutable date object. isoformat() produces the sortable YYYY-MM-DD form. Access year, month, and day directly when you need the parts.
from datetime import date
start = date(2026, 1, 15)
print(start.isoformat())
print(start.year, start.month, start.day)A calendar date
Add a timedelta
A timedelta represents a duration. Adding it to a date returns a new date, so the original is unchanged. Subtract two dates to get a timedelta and read its days attribute.
from datetime import date, timedelta
start = date(2026, 1, 30)
end = start + timedelta(days=3)
print(end.isoformat())
print((end - start).days)Durations and differences
Format for a reader
strftime builds a string from a format pattern. Use %Y, %m, and %d for numeric dates. Month and weekday names depend on locale, so prefer numeric formats when output must be identical everywhere.
from datetime import date
day = date(2026, 10, 27)
print(day.strftime("%Y/%m/%d"))
print(day.isoformat())Stable numeric formatting
A date is not an instant
date stores a calendar day with no time zone. datetime stores a clock reading; only an aware datetime identifies a moment that can be compared across regions. Do not subtract a naive datetime from an aware one; decide which kind of value the data represents first.
from datetime import date, datetime, timezone
day = date(2026, 10, 28)
moment = datetime(2026, 10, 28, 12, 0, tzinfo=timezone.utc)
print(day.isoformat())
print(moment.isoformat())
print(moment.tzinfo is not None)Calendar day versus aware instant
Parse ISO text before arithmetic
fromisoformat accepts the YYYY-MM-DD shape and rejects ambiguous day/month input. Do not guess the order from a string such as 28/10/2026; ask the source for its format and parse explicitly. Catch ValueError at the boundary and report the bad value.
from datetime import date
for raw in ["2026-10-28", "28/10/2026"]:
try:
parsed = date.fromisoformat(raw)
print(parsed.year, parsed.month, parsed.day)
except ValueError:
print("not ISO:", raw)Validate the shape before using the date
A month is not 30 days
timedelta counts fixed days, not calendar months. Adding 30 days to January 31 lands in March, not February 28. If the rule is a calendar month, use an explicit month calculation or a maintained date library; do not encode it as 30 days.
from datetime import date, timedelta
start = date(2026, 1, 31)
print((start + timedelta(days=30)).isoformat())
print((start + timedelta(days=59)).isoformat())Fixed days versus calendar months
Key points
- date(y, m, d) creates a validated calendar date.
- timedelta(days=n) represents a duration.
- Adding a timedelta returns a new date object.
- Use numeric strftime patterns when output must not depend on locale.
- Choose date or aware datetime before arithmetic; timedelta is fixed duration.
Common mistakes
- Using ambiguous day/month order in stored dates.
- Trying to mutate a date in place; date objects are immutable.
- Adding time zones before deciding whether the value is a date or an instant.
- Subtracting naive and timezone-aware datetimes and expecting a meaningful result.
Create date(2026, 12, 31), add one day, and print both dates in ISO format.
Try it yourself
Change the values, predict the output, then run the code. The Python runtime is downloaded the first time you press Run, so the first run can take a few seconds.
Practice this lesson
12 questions with immediate explanations.