Historical Context & Motivation
Computers have needed to track dates and times since their earliest days. When you log into a website, send a text message, or schedule a reminder, the software behind the scenes is storing and comparing timestamps. Before standardized libraries existed, programmers had to write their own date-handling code from scratch — accounting for leap years, time zones, and the varying number of days in each month. This led to countless bugs and even famous disasters like the Y2K bug, where storing years as two digits threatened to cause widespread computer failures at the turn of the millennium.
Python's datetime module was introduced as part of the standard library to give every Python programmer a reliable, well-tested toolkit for working with dates and times. Instead of reinventing the wheel, you can import a single module and immediately start creating date objects, calculating time differences, and formatting dates for display.
time module for basic time functions, but it relies on low-level C functions and lacks intuitive date objects.datetime module, providing object-oriented classes for dates, times, and time deltas. It becomes the standard way to handle dates in Python.dateutil and arrow build on top of it for advanced features.The central question the datetime module answers is simple but important: How do we represent, manipulate, and display dates and times in a way that is both accurate and easy for programmers to use? In this lesson, you will learn the core classes in the datetime module and how to put them to work in your own programs.
Core Principles & Key Classes
The datetime module is built around a handful of classes, each designed to represent a different aspect of time. Understanding these classes is the foundation for everything you will do with dates in Python. You always begin by importing the module with from datetime import date, datetime, timedelta (or similar import statements), and then you create objects from these classes.
date
date(2025, 1, 15).time
time(14, 30, 0).datetime
datetime(2025, 1, 15, 14, 30).timedelta
timedelta(days=7) for a one-week span.date class is the calendar page (just the date), the time class is the clock face (just the time), the datetime class is both combined into one, and timedelta is like counting the number of days between two dates you circled on the calendar.Visual Explanation — The datetime Class Hierarchy
datetime class (gold box) combines the attributes of both date (cyan) and time (pink), while timedelta (green) represents durations between two points in time.As you can see in the diagram above, the datetime class sits at the intersection of date and time. This means a single datetime object carries all six attributes — year, month, day, hour, minute, and second — making it the most versatile class for everyday use. The timedelta class stands alongside them as the tool for measuring and calculating differences in time.
How It Works — Creating and Using datetime Objects
Working with the datetime module follows a consistent pattern: you import the classes you need, create objects, and then access their attributes or call their methods. Let's walk through the key operations one by one.
Importing and Creating Objects
The module is named datetime, and it contains a class also called datetime. This can be confusing at first! The most common import style is from datetime import date, datetime, timedelta so you can use the class names directly. To create a specific date, you pass the year, month, and day as arguments: my_date = date(2025, 6, 15). For a full date and time, use my_dt = datetime(2025, 6, 15, 9, 30, 0) where the last three numbers are hour, minute, and second.
Getting the Current Date and Time
One of the most useful features is grabbing the current date and time. The method datetime.now() returns a datetime object representing the exact moment the code runs. Similarly, date.today() returns just today's date without any time information.
Accessing Attributes
Once you have a datetime or date object, you can access its parts using dot notation. For example, if now = datetime.now(), then now.year gives the year, now.month gives the month (1–12), now.day gives the day, and now.hour gives the hour in 24-hour format.
Formatting with strftime()
The strftime() method (short for 'string format time') converts a datetime object into a formatted string. You pass a format string using special codes: %Y for a four-digit year, %m for a two-digit month, %d for the day, %H for the hour (24-hour), and %M for the minute. For example, now.strftime("%B %d, %Y") might produce "January 15, 2025".
Arithmetic with timedelta
You can add or subtract timedelta objects to shift dates forward or backward. Writing date.today() + timedelta(days=30) gives you the date 30 days from now. You can also subtract two date or datetime objects to get a timedelta: difference = date2 - date1 and then access difference.days to see how many days apart they are.
strftime Format Codes — Detailed Breakdown
The strftime() method is incredibly flexible. The table below lists the most commonly used format codes you will encounter. Each code starts with a percent sign and is replaced by the corresponding date/time component when the string is generated.
| Code | Meaning | Example Output |
|---|---|---|
%Y | Four-digit year | 2025 |
%m | Month as zero-padded number (01–12) | 01 |
%d | Day as zero-padded number (01–31) | 15 |
%H | Hour in 24-hour format (00–23) | 14 |
%I | Hour in 12-hour format (01–12) | 02 |
%M | Minute (00–59) | 30 |
%S | Second (00–59) | 00 |
%B | Full month name | January |
%A | Full weekday name | Wednesday |
%p | AM or PM | PM |
strftime() takes a datetime object and a format string, then substitutes each format code (like %B and %Y) with the corresponding value to produce a human-readable string.Notice that literal characters like the comma, spaces, and the word "at" in the format string are passed through unchanged. Only codes starting with % are replaced. This gives you full control over how dates appear to your users — whether you want "01/15/2025" (American format), "15/01/2025" (European format), or something completely custom.
Worked Example — Building a Countdown Timer
Let's write a short program that calculates how many days remain until a specific event — for instance, the last day of school on June 6, 2025. This example uses multiple datetime concepts together.
date for working with calendar dates and timedelta is returned automatically when we subtract dates. Write: from datetime import datefrom datetime import datedate.today() returns the current date from the system clock. Store it in a variable: today = date.today(). If today is January 15, 2025, this creates date(2025, 1, 15).today = date.today() # date(2025, 1, 15)last_day = date(2025, 6, 6).last_day = date(2025, 6, 6)timedelta object. We write: remaining = last_day - today. The result is a timedelta representing 142 days.remaining = last_day - today # timedelta(days=142).days attribute of the timedelta to get an integer. We can use an f-string for a nice display: print(f"Only {remaining.days} days until the last day of school!")from datetime import date
today = date.today()
last_day = date(2025, 6, 6)
remaining = last_day - today
print(f"Only {remaining.days} days until the last day of school!")Strengths, Limitations & Comparisons
The datetime module is powerful, but it is important to understand both what it does well and where it falls short. Knowing its limitations helps you pick the right tool for the job.
| Feature | Strengths | Limitations |
|---|---|---|
| Date Arithmetic | Easy addition and subtraction with timedelta; automatically handles month lengths and leap years | No built-in way to add months or years (only days, seconds, and weeks) |
| Formatting | strftime() supports many format codes and works consistently across platforms | Format codes can be tricky to memorize (%m vs %M); no natural language parsing built in |
| Time Zones | Supports timezone-aware datetimes via the tzinfo interface | Built-in timezone support is minimal; most developers need the third-party pytz or zoneinfo library |
| Built-in | Part of Python's standard library — no installation required | Less user-friendly than third-party alternatives like arrow or pendulum |
| Comparison | Datetime objects support <, >, ==, and other comparison operators directly | Cannot compare naive (no timezone) and aware (has timezone) datetimes; raises TypeError |
zoneinfo module or the dateutil library).Connection to Advanced Topics
The basics you have learned here form the foundation for more advanced date and time handling. As your projects grow in complexity, you will encounter situations where you need features beyond what we have covered today. The table below previews how introductory datetime concepts connect to more advanced topics.
| Intro Concept (This Lesson) | Advanced Concept (Future Learning) |
|---|---|
date.today() and datetime.now() | Timezone-aware datetime.now(tz=ZoneInfo('US/Eastern')) using the zoneinfo module |
strftime() for formatting output | strptime() for parsing date strings into datetime objects (the reverse operation) |
timedelta(days=7) for simple offsets | The dateutil.relativedelta for adding months and years accurately |
Comparing two dates with < and > | Sorting lists of datetime objects and using them as dictionary keys or database timestamps |
Accessing .year, .month, .day | Using .isoformat(), .weekday(), .isocalendar() for ISO standards and calendar operations |
As you move into web development, data science, or app building, you will find that datetime is used almost everywhere. Database records are timestamped with datetime values. APIs send dates as ISO 8601 strings (like "2025-01-15T14:30:00Z") that you will parse using strptime(). Mastering the basics now gives you a strong foundation for all of these future applications.
Practice Problems
date object and a datetime object in Python's datetime module? When would you choose one over the other?date object for March 14, 2025 (Pi Day). Then write another expression that uses strftime() to format it as "03/14/2025".timedelta and prints it in the format "Friday, January 31, 2025"..days attribute, then divide by 365.from datetime import datetime
dt = datetime(2025, 3, 1) - datetime(2025, 2, 1)
print(dt.days)
They expected 30 but got 28. Explain why, and describe how the result would change if the year were 2024 instead of 2025.Lesson Summary
Python's datetime module provides four core classes for handling dates and times. The date class represents a calendar date (year, month, day), the time class represents a time of day, the datetime class combines both into one object, and timedelta represents a duration between two points in time. You create objects by passing numeric arguments (like year, month, and day), and you can retrieve the current date or time using date.today() or datetime.now().
To display dates in a specific format, use the strftime() method with format codes like %Y (year), %m (month), and %d (day). To perform date arithmetic, add or subtract timedelta objects, or subtract two dates to get a timedelta. The module handles tricky details like leap years and varying month lengths automatically, making it an essential part of every Python programmer's toolkit.