Kodewick
Lesson 25 of 31Contents

Dates and times

Lesson 25 of 31 · Part 4 · Code and data · about 20 minutes

Dates look simple until you add 30 days to January 15 or count the days to a deadline. Python's datetime module knows about month lengths, leap years and weekdays, so you never have to. This lesson covers its three main types, turning dates into text and back, date arithmetic, and timing your own code.

Making a date

The datetime module has three types you will use all the time. Import them by name:

TypeHoldsExample
datea daydate(2026, 3, 14)
datetimea day and a time of daydatetime(2026, 3, 14, 18, 30)
timedeltaa length of timetimedelta(days=7)
main.py
1from datetime import date, datetime2d = date(2026, 3, 14)3print(d)4print(d.year, d.month, d.day)5print(d.weekday())6t = datetime(2026, 3, 14, 18, 30)7print(t)8print(t.hour, t.minute)
Output
2026-03-14
2026 3 14
5
2026-03-14 18:30:00
18 30

Arguments go from biggest to smallest: year, month, day, then hour, minute, second. Printing shows the standard ISO order, year first. weekday() counts from Monday as 0, so 5 is Saturday.

A date that does not exist is an error, not a guess:

main.py
1from datetime import date2print(date(2028, 2, 29))3print(date(2026, 2, 29))
Output
2028-02-29
Traceback (most recent call last):
  File "main.py", line 3, in <module>
    print(date(2026, 2, 29))
ValueError: day is out of range for month

2028 is a leap year, 2026 is not, so the second line raises a ValueError.

Today and now

date.today() gives today's date and datetime.now() the current date and time, read from your computer's clock.

main.py
1from datetime import date, datetime2today = date.today()3now = datetime.now()4print(type(today))5print(type(now))6print(today > date(2020, 1, 1))7print(now > datetime(2020, 1, 1))
Output
<class 'datetime.date'>
<class 'datetime.datetime'>
True
True

This page does not print the values themselves: the output here was made when the site was built, so it would show the wrong day. On your own computer, print(today) and print(now) show something like this:

Output
2026-11-01
2026-11-01 09:41:27.318204

The part after the seconds is microseconds. now.date() keeps just the day, like today. The rest of the lesson uses fixed dates, so every result stays the same.

Dates as text: strftime

strftime ("string format time") turns a date into text in any layout you like. Each % code is replaced by one part of the date; everything else is copied as it is.

main.py
1from datetime import datetime2t = datetime(2026, 3, 14, 18, 30)3print(t.strftime("%d/%m/%Y"))4print(t.strftime("%A, %B %d, %Y"))5print(t.strftime("%a %d %b, %I:%M %p"))6print(t.strftime("Day %j of %Y"))7print(f"{t:%H:%M on %d %B}")8print(t.isoformat())
Output
14/03/2026
Saturday, March 14, 2026
Sat 14 Mar, 06:30 PM
Day 073 of 2026
18:30 on 14 March
2026-03-14T18:30:00
CodeMeansFor March 14, 2026, 18:30
%Y / %yyear2026 / 26
%m / %dmonth / day, two digits03 / 14
%B / %bmonth nameMarch / Mar
%A / %aweekday nameSaturday / Sat
%H / %Ihour, 24-hour / 12-hour18 / 06
%M / %Sminute / second30 / 00
%pAM or PMPM
%jday of the year073

The same codes work after the colon in an f-string, as in lesson 24's format specifiers. isoformat() gives the standard form computers swap with each other.

Text to dates: strptime

datetime.strptime ("string parse time") goes the other way: give it the text and the layout it is written in, and it builds a datetime.

main.py
1from datetime import datetime2t = datetime.strptime("14/03/2026 18:30", "%d/%m/%Y %H:%M")3print(t)4d = datetime.strptime("2026-03-14", "%Y-%m-%d").date()5print(d)6print(d.strftime("%B %d"))
Output
2026-03-14 18:30:00
2026-03-14
March 14

The layout must match the text exactly, separators included. If it does not, you get a ValueError, which you can catch as in lesson 21:

main.py
1from datetime import datetime2try:3    datetime.strptime("14/03/2026", "%Y-%m-%d")4except ValueError as e:5    print("Bad date:", e)
Output
Bad date: time data '14/03/2026' does not match format '%Y-%m-%d'

Adding and subtracting

A timedelta is a length of time. Add one to a date to move forward, subtract to go back. Python handles month ends and leap years.

main.py
1from datetime import date, datetime, timedelta2d = date(2026, 1, 15)3print(d + timedelta(days=30))4print(d - timedelta(weeks=3))5t = datetime(2026, 3, 14, 22, 0)6print(t + timedelta(hours=5))7print(timedelta(hours=36))8print(timedelta(hours=36).total_seconds())
Output
2026-02-14
2025-12-25
2026-03-15 03:00:00
1 day, 12:00:00
129600.0

Subtract one date from another and you get a timedelta. Its .days is the plain number of days.

Guess first

What does this print?

1from datetime import date2gap = date(2026, 3, 1) - date(2026, 2, 1)3print(gap)
main.py
1from datetime import date, datetime2gap = date(2026, 12, 24) - date(2026, 11, 1)3print(gap.days)4shift = datetime(2026, 3, 14, 18, 30) - datetime(2026, 3, 14, 9, 15)5print(shift)6print(shift.total_seconds() / 3600)
Output
53
9:15:00
9.25

replace() is the other way to change a date: it gives a copy with some parts swapped, such as d.replace(year=2027). There is no timedelta(months=1), because months have different lengths; use replace(month=...) when you mean "same day, another month". If that day doesn't exist in the new month, such as 31 in April, it raises a ValueError.

Comparing dates

Dates compare the way you would expect: earlier is smaller. That means <, ==, sorted, min and max all work.

main.py
1from datetime import date2deliveries = [date(2026, 5, 2), date(2026, 3, 14), date(2026, 4, 1)]3for d in sorted(deliveries):4    print(d)5print(min(deliveries))6today = date(2026, 4, 10)7late = [d for d in deliveries if d < today]8print(len(late), "already passed")
Output
2026-03-14
2026-04-01
2026-05-02
2026-03-14
2 already passed

Here today is a fixed date so the result never changes. In a real program you would write today = date.today().

Timing your code

To measure how long some code takes, read time.perf_counter() before and after it and subtract. The number itself means nothing; only the difference between two readings matters.

main.py
1import time2start = time.perf_counter()3total = 04for n in range(100000):5    total += n6elapsed = time.perf_counter() - start7print(total)8print(elapsed > 0)
Output
4999950000
True

The time is different on every run and every computer, so this page only checks that some time passed. On your computer, print(f"Took {elapsed:.4f} s") shows something like:

Output
Took 0.0062 s

Use perf_counter for timing and datetime.now() for "what time is it": perf_counter is more precise and never jumps when the clock is changed.

Practice

Write the code, then press Check. A hint costs nothing; the answer is there when you need it.

Exercise 1

A long date

Show d as a weekday, day, month name and year, like the line below.

It should print
Saturday, 04 July 2026
exercise1.py
1from datetime import date2d = date(2026, 7, 4)3print(d)
Output
Press Run or Ctrl+Enter
Exercise 2

Days to go

Print how many days are left from today to festival, followed by days to go.

It should print
53 days to go
exercise2.py
1from datetime import date2today = date(2026, 11, 1)3festival = date(2026, 12, 24)4print(festival)
Output
Press Run or Ctrl+Enter
Exercise 3

Due in ten days

Read the date in text, add 10 days, and print the result as day.month.year.

It should print
07.04.2026
exercise3.py
1from datetime import datetime, timedelta2text = "2026-03-28"3print(text)
Output
Press Run or Ctrl+Enter

What you learned

  • from datetime import date, datetime, timedelta: a day, a day with a time, a length of time.
  • date(2026, 3, 14) makes a date; an impossible date raises ValueError.
  • date.today() and datetime.now() read the clock, so they change every run.
  • strftime and f"{d:%d %B}" turn dates into text; strptime reads text into a datetime.
  • Date plus timedelta is a date; date minus date is a timedelta, and .days counts its days.
  • Dates compare and sort in time order.
  • time.perf_counter() before and after some code measures how long it took.