Lesson 23 · Intermediate · 19 min

Python Dates and Times with datetime

Work with calendar dates and simple durations accurately.

datetimedate arithmetictimedeltaISO dates

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.
Your turn

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.

In-browser PythonRuns locally · no account
First run downloads Pyodide from a CDN. Use Run code after editing.

Practice this lesson

12 questions with immediate explanations.

See all 12 answersStart practice
← pathlibassert →