Dates and times
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:
| Type | Holds | Example |
|---|---|---|
date | a day | date(2026, 3, 14) |
datetime | a day and a time of day | datetime(2026, 3, 14, 18, 30) |
timedelta | a length of time | timedelta(days=7) |
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)
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:
1from datetime import date2print(date(2028, 2, 29))3print(date(2026, 2, 29))
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 month2028 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.
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))
<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:
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.
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())
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
| Code | Means | For March 14, 2026, 18:30 |
|---|---|---|
%Y / %y | year | 2026 / 26 |
%m / %d | month / day, two digits | 03 / 14 |
%B / %b | month name | March / Mar |
%A / %a | weekday name | Saturday / Sat |
%H / %I | hour, 24-hour / 12-hour | 18 / 06 |
%M / %S | minute / second | 30 / 00 |
%p | AM or PM | PM |
%j | day of the year | 073 |
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.
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"))
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:
1from datetime import datetime2try:3 datetime.strptime("14/03/2026", "%Y-%m-%d")4except ValueError as e:5 print("Bad date:", e)
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.
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())
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.
What does this print?
1from datetime import date2gap = date(2026, 3, 1) - date(2026, 2, 1)3print(gap)
Subtracting dates gives a timedelta, and a printed timedelta shows days and then hours:minutes:seconds. February 2026 has 28 days. Use gap.days for just 28.
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)
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.
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")
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.
1import time2start = time.perf_counter()3total = 04for n in range(100000):5 total += n6elapsed = time.perf_counter() - start7print(total)8print(elapsed > 0)
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:
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.
A long date
✓Show d as a weekday, day, month name and year, like the line below.
Saturday, 04 July 2026
1from datetime import date2d = date(2026, 7, 4)3print(d)
Days to go
✓Print how many days are left from today to festival, followed by days to go.
53 days to go
1from datetime import date2today = date(2026, 11, 1)3festival = date(2026, 12, 24)4print(festival)
Due in ten days
✓Read the date in text, add 10 days, and print the result as day.month.year.
07.04.2026
1from datetime import datetime, timedelta2text = "2026-03-28"3print(text)
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 raisesValueError.date.today()anddatetime.now()read the clock, so they change every run.strftimeandf"{d:%d %B}"turn dates into text;strptimereads text into adatetime.- Date plus
timedeltais a date; date minus date is atimedelta, and.dayscounts its days. - Dates compare and sort in time order.
time.perf_counter()before and after some code measures how long it took.