Learning Goals
3 min- Make dates & datetimes with
date(...),datetime(...),date.today()anddatetime.now(). - Parse strings into datetimes with
strptime. - Format datetimes into strings with
strftime. - Do date arithmetic with
timedelta— days, hours, "difference between two dates".
Warm-Up · Today
5 minfrom datetime import date, datetime, timedelta print(date.today()) # → 2026-05-28 print(datetime.now()) # → 2026-05-28 14:33:21.456789 print(datetime.now().year) # → 2026
Three modules' worth of functionality lives in datetime:
date— just a calendar date.datetime— date + time.timedelta— a length of time (days, hours, seconds).
Treat date strings as enemies. Convert them to datetime objects as soon as they enter your program. Do all your maths on objects. Convert back to a string only at the last moment, just before printing or saving.
New Concept · strptime, strftime, timedelta
14 minThe format codes
Both strptime and strftime use the same format codes. The five you'll use most:
%Y → 4-digit year 2026 %m → 2-digit month 05 %d → 2-digit day 28 %H → 2-digit hour (24) 14 %M → 2-digit minute 33
String → datetime (strptime, "p" for parse)
from datetime import datetime text = "2026-05-28" d = datetime.strptime(text, "%Y-%m-%d") print(d.year, d.month, d.day) # → 2026 5 28
datetime → string (strftime, "f" for format)
from datetime import datetime now = datetime.now() print(now.strftime("%d/%m/%Y")) # → 28/05/2026 print(now.strftime("%A, %d %B %Y")) # → Thursday, 28 May 2026
You can mix any text you want with format codes; everything that isn't a %X is taken literally.
Arithmetic — timedelta
from datetime import date, timedelta today = date.today() in_one_week = today + timedelta(days=7) last_friday = today - timedelta(days=6) diff = in_one_week - today print(diff) # → 7 days, 0:00:00 print(diff.days) # → 7
Three rules:
date ± timedelta→datedate − date→timedeltatimedelta(days=, hours=, minutes=, seconds=)— pick any combination.
The full pipeline
# A real CSV row → a calculation → back to a friendly string from datetime import datetime row = {"date": "2026-05-01", "qty": "3"} when = datetime.strptime(row["date"], "%Y-%m-%d") days_ago = (datetime.now() - when).days print(f"That sale was {days_ago} days ago.") print("Nice format:", when.strftime("%d %b %Y")) # → 01 May 2026
Worked Example · Sales-Last-7-Days
12 minUse the sales.csv from Lesson 3. Filter rows from the last 7 days, sum the revenue, print a friendly date range.
# last_7.py — date filtering + formatting import csv from datetime import datetime, date, timedelta today = date.today() seven_ago = today - timedelta(days=7) with open("sales.csv", newline="") as f: rows = list(csv.DictReader(f)) # Parse dates into real objects for r in rows: r["date_obj"] = datetime.strptime(r["date"], "%Y-%m-%d").date() r["revenue"] = int(r["quantity"]) * float(r["price"]) # Filter recent = [r for r in rows if seven_ago <= r["date_obj"] <= today] # Report total = sum(r["revenue"] for r in recent) print(f"📅 {seven_ago.strftime('%d %b')} — {today.strftime('%d %b %Y')}") print(f" {len(recent)} transactions, total RM {total:.2f}") print() for r in recent: print(f" {r['date_obj'].strftime('%a %d %b')} " f"{r['product']:<8} RM {r['revenue']:.2f}")
Sample output (run on 2026-05-08)
📅 01 May — 08 May 2026 6 transactions, total RM 41.50 Fri 01 May roti RM 4.50 Fri 01 May milo RM 6.00 Sat 02 May roti RM 7.50 Sat 02 May nasi RM 8.00 Sun 03 May milo RM 12.00 Sun 03 May nasi RM 16.00
Read the diff
The parse step (strptime → date()) happens once when data loads. After that, every comparison is between real date objects — clean, fast, no string parsing inside the loop. That's the "treat strings as enemies" rule in action.
Try It Yourself
13 minHard-code your next birthday as a date. Print how many days from today.
Hint
from datetime import date bday = date(2026, 9, 14) print(f"{(bday - date.today()).days} days to go")
Read sales.csv and count transactions per weekday. Print the breakdown.
Hint
from collections import Counter counts = Counter() for r in rows: d = datetime.strptime(r["date"], "%Y-%m-%d") counts[d.strftime("%A")] += 1 for day, n in counts.most_common(): print(f" {day:<10} {n}")
Given any date, return the first day of the following month. Don't hard-code 30/31 day rules — let timedelta walk you there.
Hint
from datetime import date, timedelta def first_of_next_month(d): cur = d.replace(day=1) # Jump 32 days forward → guaranteed to be next month nxt = cur + timedelta(days=32) return nxt.replace(day=1) print(first_of_next_month(date(2026, 1, 15))) # → 2026-02-01 print(first_of_next_month(date(2026, 12, 31))) # → 2027-01-01
The trick: jump too far then snap back to day 1. Avoids every "is it 28, 29, 30 or 31?" edge case.
Mini-Challenge · Project Burndown
8 minBuild burndown.py. A project has a start date and an end date. Today, the project is X% done. Print:
- Days elapsed.
- Days remaining.
- Whether the team is on track (% complete ≥ % time elapsed).
Show one possible solution
# burndown.py from datetime import date start = date(2026, 5, 1) end = date(2026, 6, 15) percent = 35 today = date.today() elapsed = (today - start).days total = (end - start).days remain = (end - today).days time_pct = elapsed / total * 100 print(f"Start: {start} End: {end}") print(f"Elapsed: {elapsed} days ({time_pct:.0f}% of schedule)") print(f"Remaining: {remain} days") print(f"Progress: {percent}%") if percent >= time_pct: print("✅ on track") else: print(f"⚠️ behind by {time_pct - percent:.0f} points")
Non-negotiables: real arithmetic on date, an integer comparison to decide on-track, no string parsing of the day numbers.
Recap
3 minThree classes: date, datetime, timedelta. The mantra: parse strings to objects on the way in (strptime), do every comparison and calculation on objects, format back to a string on the way out (strftime). timedelta handles all the "how long between" and "X days later" arithmetic without any maths from you.
Vocabulary Card
- strptime
- "String parse time" — parse a string into a datetime.
- strftime
- "String format time" — format a datetime as a string.
- timedelta
- A length of time; the result of date − date and the input to date ± delta.
- %Y %m %d %H %M %S
- Format codes for year, month, day, hour, minute, second.
Homework
4 minBuild age_calc.py. Ask the user for their birthday in DD/MM/YYYY format. Print:
- Their age in years.
- Their age in days.
- Days until their next birthday.
- Which weekday they were born on.
Sample · age_calc.py
# age_calc.py — interactive age calculator from datetime import datetime, date raw = input("Birthday (DD/MM/YYYY): ").strip() bday = datetime.strptime(raw, "%d/%m/%Y").date() today = date.today() # Age in years years = today.year - bday.year - ((today.month, today.day) < (bday.month, bday.day)) # Age in days days = (today - bday).days # Next birthday this year — or next next_bday = bday.replace(year=today.year) if next_bday < today: next_bday = bday.replace(year=today.year + 1) print(f"Age: {years} years ({days} days)") print(f"Next birthday in {(next_bday - today).days} days") print(f"You were born on a {bday.strftime('%A')}")
Non-negotiables: input parsed with strptime, year-edge case handled with the boolean trick, next-birthday handles "already passed this year".