Dates and Deltas
Date and Time in Practice ๐
Chapter 1 ยท Dates and Deltas
Dates and times look simple until a program has to compare, add, store or display them. Months have different lengths, years sometimes have an extra day, and the same moment is written differently in different countries. This course is practical: each chapter takes one idea, runs it, and ends with a rule you can reuse. All examples use Python 3 and its built-in datetime module, so there is nothing to install.
Four Types, Four Jobs
The module gives you separate types for separate ideas. Choosing the right one up front avoids a lot of later conversion.
| Type | Holds | Example |
|---|---|---|
| date | a calendar day, no time of day | date(2026, 11, 2) |
| time | a time of day, no calendar day | time(14, 30, 5) |
| datetime | a day and a time together | datetime(2026, 11, 2, 14, 30, 5) |
| timedelta | a length of time, not a point in time | timedelta(hours=1, minutes=30) |
Invalid values are rejected immediately rather than silently rolling over, which is a useful safety net:
Subtracting Gives a timedelta
Two points in time subtract to give a length of time. That length is a timedelta, and you can add it to or subtract it from a date or datetime to move forward or back.
Two details catch people out. First, a timedelta stores only days, seconds and microseconds, so there is no "months" or "years" argument: a month is not a fixed length. Second, a negative delta prints oddly (-1 day, 23:58:30 is minus 90 seconds), because the days part is negative and the rest is positive. Use total_seconds() when you want one plain number.
.seconds attribute of a timedelta is only the seconds left over after whole days are removed. For a span of a day or more it is the wrong thing to read. Use .total_seconds().
Turning Dates into Text and Back
strftime ("string from time") formats a date as text using percent codes. strptime ("string parsed to time") does the reverse: you give it the text and the format that describes it. The two use the same codes.
| Code | Meaning | Example |
|---|---|---|
%Y %m %d | 4-digit year; month 01-12; day 01-31 | 2026 11 02 |
%H %M %S | hour (24-hour); minute; second | 14 30 05 |
%A %B | full weekday name; full month name | Monday November |
The format must describe the text exactly, and the same text means different dates under different formats. Both lines below parse successfully and give a different day:
So the text 02/11/2026 and the text 11/02/2026 both mean 2 November, but only because each was given the matching format. Handed the wrong format, the first would be read as 11 February. This is why you should store and exchange dates in the unambiguous ISO form (2026-11-02) and keep the day-first or month-first forms for display only.
date when there is no time of day, timedelta for lengths. Subtract to measure, add a timedelta to move. Prefer ISO text when storing, and catch ValueError when parsing anything a person typed.
Challenges
Given born = date(1990, 5, 17) and today = date(2026, 11, 2), work out the person's age in whole years and the total number of days they have lived. Take care that the age is right when the birthday has not yet happened this year. Hint: tuples compare element by element, so (today.month, today.day) < (born.month, born.day) is a neat test.
A shift starts at 09:15 and ends at 17:50 on the same day (use datetime objects for 2026-11-02). Print the length of the shift as a timedelta, as a decimal number of hours, and as whole hours plus remaining minutes. Predict each output before you run it.
Write next_weekday(d, target) that returns the next date strictly after d that falls on weekday number target (Monday is 0, Sunday is 6). If d is already that weekday, return the date a week later. Test it with date(2026, 11, 2), which is a Monday, for a Monday, a Friday and a Sunday, and print the Friday's name with strftime.
Quick Reference
- Types:
date,time,datetimeare points;timedeltais a length - Subtract two points to get a timedelta; add a timedelta to move a point
- Read a delta with
.daysfor whole days,.total_seconds()for everything - Format:
strftime(fmt)to text,strptime(text, fmt)from text - ISO:
isoformat()andfromisoformat()for unambiguous storage - Bad input raises
ValueError; catch it when parsing user text