Python datetime Module Explained — Dates, Times, and Formatting
Every useful program eventually needs to deal with time. A banking app needs to timestamp every transaction. A fitness tracker needs to know how many days you've been on a streak. An e-commerce site needs to calculate whether a coupon has expired. Time is everywhere in software, and Python gives you a clean, built-in way to handle it — the datetime module.
Before the datetime module existed, developers had to manually juggle numbers for days, months, and years, writing messy arithmetic to figure out things like 'what date is 90 days from now?' That's surprisingly error-prone — months have different lengths, leap years exist, and time zones are a nightmare. The datetime module solves all of this by giving you dedicated objects that already understand how calendars work.
By the end of this article you'll know how to get today's date and current time, create specific date and time objects from scratch, calculate the difference between two dates, format dates into human-readable strings, and parse date strings back into date objects. These are skills you'll use in almost every real-world Python project.
The Four Core Classes Inside the datetime Module
The datetime module isn't just one thing — it's a toolbox with four main tools inside it. Understanding which tool to reach for is the first step.
date — Handles only calendar dates: year, month, day. Use this when you don't care about the time of day. Example: a user's birthday, a product release date.
time — Handles only the time of day: hour, minute, second, microsecond. Use this when you don't care about the date. Example: a store's opening hours.
datetime — The combination of both. It holds a year, month, day, hour, minute, second, and microsecond all together. This is the one you'll use most often. Example: the exact moment a user logged in.
timedelta — Represents a duration or difference between two points in time. Think of it as a stopwatch result. Example: 'this event lasts 3 days and 4 hours'.
A common beginner confusion is that the module is called datetime and the class inside it is also called datetime. So when you write datetime.datetime.now() — the first datetime is the module, and the second is the class inside that module. We'll show you a cleaner way to import to avoid this.
# First, let's import the specific classes we need from the datetime module. # This cleaner import style avoids writing 'datetime.datetime' everywhere. from datetime import date, time, datetime, timedelta # --- date object: only year, month, day --- release_date = date(2024, 11, 15) # November 15, 2024 print("Release date:", release_date) # Prints the date print("Year:", release_date.year) # Access individual parts print("Month:", release_date.month) print("Day:", release_date.day) # --- time object: only hour, minute, second --- store_opens = time(9, 30, 0) # 9:30:00 AM print("\nStore opens at:", store_opens) # --- datetime object: date AND time together --- user_login = datetime(2024, 11, 15, 14, 35, 22) # Nov 15, 2024 at 2:35:22 PM print("\nUser logged in at:", user_login) # --- timedelta object: a duration (not a specific moment) --- trial_period = timedelta(days=14) # A 14-day free trial print("\nTrial lasts:", trial_period) print("Trial days:", trial_period.days)
Year: 2024
Month: 11
Day: 15
Store opens at: 09:30:00
User logged in at: 2024-11-15 14:35:22
Trial lasts: 14 days, 0:00:00
Trial days: 14
Getting Today's Date and the Current Date-Time
The most common thing you'll do with the datetime module is ask Python 'what time is it right now?' There are two methods for this depending on whether you need just the date or the full timestamp.
date.today() returns a date object with today's year, month, and day. No hours, no minutes. Perfect for things like 'show me tasks due today'.
datetime.now() returns a datetime object with the full current timestamp — right down to the microsecond. Use this for logging, timestamps on records, or anything where the exact moment matters.
You can also extract the date portion from a datetime object using the .date() method. This is useful when you receive a full datetime but only need to compare calendar dates — for example, checking if an event happened today regardless of what time it happened.
One thing to note: both today() and now() return your local machine's time. They have no awareness of time zones by default. For time-zone-aware work you'd use datetime.now(timezone.utc), but that's an intermediate topic — let's master the basics first.
from datetime import date, datetime # Get today's date only (no time component) today = date.today() print("Today's date:", today) # e.g. 2024-11-15 print("Day of week (0=Mon, 6=Sun):", today.weekday()) # Monday=0, Sunday=6 print("Is today a weekday?", today.weekday() < 5) # True if Mon-Fri # Get the current full timestamp (date + time) right_now = datetime.now() print("\nFull timestamp:", right_now) # e.g. 2024-11-15 14:35:22.123456 print("Current hour:", right_now.hour) # Just the hour portion print("Current minute:", right_now.minute) # Just the minute portion # Extract just the date part from a datetime object login_timestamp = datetime(2024, 11, 15, 8, 45, 0) # Simulated login time login_date_only = login_timestamp.date() # Strip away the time part print("\nLogin timestamp:", login_timestamp) print("Login date only:", login_date_only) # Check if the login happened today if login_date_only == date.today(): print("User logged in today!") else: print("Login was on a different day.")
Day of week (0=Mon, 6=Sun): 4
Is today a weekday? True
Full timestamp: 2024-11-15 14:35:22.847312
Current hour: 14
Current minute: 35
Login timestamp: 2024-11-15 08:45:00
Login date only: 2024-11-15
User logged in today!
Calculating Durations with timedelta — The Date Arithmetic Tool
Here's where datetime really earns its keep. Imagine you're building a subscription service. You need to know when a 30-day trial ends, or whether a user's password hasn't been changed in over 90 days. This is date arithmetic, and timedelta is how you do it.
A timedelta object represents a span of time — not a specific moment, but a duration. You can create one with days, seconds, microseconds, milliseconds, minutes, hours, or weeks. Then you can add or subtract it from a date or datetime object.
You can also subtract two date or datetime objects from each other — the result is automatically a timedelta. This is how you calculate 'how many days between these two events'.
The .days property gives you the total number of whole days in the delta. For anything under a day, use .seconds. If you want the total duration as a single float in seconds, use .total_seconds() — this is especially useful for performance measurements and countdowns.
from datetime import date, datetime, timedelta # --- Scenario 1: Calculate when a free trial expires --- trial_start = date(2024, 11, 1) # Trial started November 1st trial_duration = timedelta(days=30) # Free trial lasts 30 days trial_end = trial_start + trial_duration # Add the duration to the start date print("Trial started:", trial_start) print("Trial ends:", trial_end) # Should be December 1st # --- Scenario 2: How many days until an event? --- today = date.today() new_year = date(2025, 1, 1) # Next New Year's Day days_until_new_year = new_year - today # Subtracting two dates gives a timedelta print("\nDays until New Year:", days_until_new_year.days, "days") # --- Scenario 3: Check if a password is too old (security feature) --- last_password_change = datetime(2024, 7, 20, 10, 0, 0) # Changed July 20th current_time = datetime.now() password_age = current_time - last_password_change # timedelta result max_age = timedelta(days=90) # Force reset after 90 days print("\nPassword age:", password_age.days, "days") if password_age > max_age: print("WARNING: Password is too old! Please reset it.") else: days_left = max_age - password_age print(f"Password OK. Must be changed in {days_left.days} days.") # --- Scenario 4: total_seconds() for precise measurement --- downtime_start = datetime(2024, 11, 15, 2, 0, 0) # Server went down at 2:00 AM downtime_end = datetime(2024, 11, 15, 2, 47, 33) # Back up at 2:47:33 AM downtime_duration = downtime_end - downtime_start print(f"\nServer was down for {downtime_duration.total_seconds()} seconds") print(f"That's {downtime_duration.seconds // 60} minutes and {downtime_duration.seconds % 60} seconds")
Trial ends: 2024-12-01
Days until New Year: 47 days
Password age: 118 days
WARNING: Password is too old! Please reset it.
Server was down for 2853.0 seconds
That's 47 minutes and 33 seconds
Formatting and Parsing Dates — strftime and strptime
Right now your date objects print in a format like 2024-11-15. That's clean for a computer, but your users expect to see something like 'Friday, 15 November 2024' or '11/15/2024'. This is where strftime and strptime come in — two methods with confusing names that do opposite things.
strftime means 'string format time' — it takes a datetime object and converts it INTO a formatted string for display. Think of it as 'formatting for output'.
strptime means 'string parse time' — it takes a date string and converts it INTO a datetime object. Think of it as 'reading a date from user input or a file'.
Both use format codes — special placeholders that start with %. For example, %Y means the 4-digit year, %m means the 2-digit month, %d means the day. You combine these like a template to describe how the date is written.
This is one of the most practically useful parts of datetime — nearly every real app needs to display dates nicely and read dates from form inputs or CSV files.
from datetime import datetime right_now = datetime(2024, 11, 15, 14, 35, 22) # A fixed datetime for consistent output # --- strftime: datetime object --> formatted string --- # Format codes: %Y=4-digit year, %m=month number, %d=day, %B=full month name, # %A=full weekday name, %H=hour(24h), %M=minute, %S=second # Format for a European audience europe_format = right_now.strftime("%d/%m/%Y") # 15/11/2024 print("European format:", europe_format) # Format for an American audience usa_format = right_now.strftime("%m/%d/%Y") # 11/15/2024 print("US format:", usa_format) # A long, human-readable format friendly_format = right_now.strftime("%A, %d %B %Y") # Friday, 15 November 2024 print("Friendly format:", friendly_format) # A format good for filenames (no slashes or spaces) file_safe_format = right_now.strftime("%Y%m%d_%H%M%S") # 20241115_143522 print("Filename-safe format:", file_safe_format) # Including the time full_readable = right_now.strftime("%d %B %Y at %H:%M") print("Full readable:", full_readable) # --- strptime: formatted string --> datetime object --- # Imagine reading a date from a web form or CSV file: user_input_date = "25/12/2024" # Christmas Day typed by a user # Tell strptime exactly what format the string is in parsed_date = datetime.strptime(user_input_date, "%d/%m/%Y") print("\nParsed date object:", parsed_date) # Now it's a real datetime print("Type:", type(parsed_date)) # Confirm it's a datetime object print("Month number:", parsed_date.month) # Access the month as a number print("Day of week:", parsed_date.strftime("%A")) # 'Wednesday' # Real-world example: reading dates from a CSV log file log_entry = "2024-11-15 08:45:00" # Common log file timestamp format parsed_log_time = datetime.strptime(log_entry, "%Y-%m-%d %H:%M:%S") print("\nLog timestamp parsed:", parsed_log_time) print("Log entry hour:", parsed_log_time.hour)
US format: 11/15/2024
Friendly format: Friday, 15 November 2024
Filename-safe format: 20241115_143522
Full readable: 15 November 2024 at 14:35
Parsed date object: 2024-12-25 00:00:00
Type: <class 'datetime.datetime'>
Month number: 12
Day of week: Wednesday
Log timestamp parsed: 2024-11-15 08:45:00
Log entry hour: 8
| Feature | strftime | strptime |
|---|---|---|
| What it does | Converts datetime → string | Converts string → datetime |
| Direction | Output / Display | Input / Parsing |
| Called on | A datetime object instance | The datetime class directly |
| Example call | my_date.strftime('%d/%m/%Y') | datetime.strptime('15/11/2024', '%d/%m/%Y') |
| Returns | A formatted string | A datetime object |
| Common use case | Displaying dates to users | Reading dates from forms or files |
| Raises error if format wrong? | No — silently uses wrong codes | Yes — ValueError if format doesn't match string |
🎯 Key Takeaways
- The datetime module has four core classes:
date(calendar day only),time(clock time only),datetime(both combined), andtimedelta(a duration). Picking the right class matters. - Subtracting two datetime objects gives you a
timedelta. Adding atimedeltato a date gives you a new date. This is how all date arithmetic works — no manual day-counting needed. strftimeformats a datetime INTO a string for display;strptimeparses a string INTO a datetime object. The format codes (like %Y, %m, %d) must exactly match the string structure or you get a ValueError.- Use
date.today()for calendar-day comparisons anddatetime.now()for full timestamps. Never compare adateobject directly with adatetimeobject — they're different types and Python will raise a TypeError.
⚠ Common Mistakes to Avoid
- ✕Mistake 1: Confusing the module name with the class name — Writing
import datetimeand then trying to calldatetime.now()raises AttributeError becausenow()belongs to thedatetimeCLASS inside thedatetimeMODULE, not the module itself. Fix it by usingfrom datetime import datetimesodatetime.now()works, OR keep the full path and writedatetime.datetime.now(). - ✕Mistake 2: Mismatching the strptime format string with the actual date string — If your string is '15-11-2024' but your format is '%d/%m/%Y' (using slashes), Python raises a ValueError: time data does not match format. Always make sure every separator, space, and code in your format string exactly mirrors the structure of the date string you're parsing.
- ✕Mistake 3: Subtracting a date from a datetime (or vice versa) — Writing
datetime.now() - date.today()raises a TypeError: unsupported operand type(s) because one is adatetimeobject and the other is adateobject. Fix it by converting thedateto adatetimefirst:datetime.now() - datetime.combine(date.today(), time.min), or compare only dates usingdatetime.now().date() - date.today().
Interview Questions on This Topic
- QWhat's the difference between the `date`, `time`, and `datetime` classes in Python's datetime module, and when would you use each one?
- QIf you receive a date as a string in the format 'DD-MM-YYYY' from an API and need to check whether it's more than 30 days in the past, how would you do that in Python?
- QWhat does `timedelta.total_seconds()` return that `.days` and `.seconds` don't — and can you give a scenario where using `.days` alone would give you a wrong answer?
Frequently Asked Questions
How do I get today's date in Python?
Use from datetime import date then call date.today(). This returns a date object with the current year, month, and day based on your system clock. If you also need the current time, use datetime.now() from the datetime class instead.
What is the difference between datetime.now() and datetime.today() in Python?
For most practical purposes they return the same result — the current local date and time. The technical difference is that datetime.now() accepts an optional tz (timezone) argument to return timezone-aware datetimes, while datetime.today() always returns a naive (timezone-unaware) datetime. Prefer datetime.now() for new code since it's more flexible.
Why do I get 'TypeError: unsupported operand type' when subtracting dates?
This happens when you try to subtract a date object from a datetime object or vice versa. Python won't automatically convert between them. The fix is to ensure both values are the same type — either extract the .date() from your datetime first, or convert your date to a datetime using datetime.combine(my_date, time.min) before doing the arithmetic.
Written and reviewed by senior developers with real-world experience across enterprise, startup and open-source projects. Every article on TheCodeForge is written to be clear, accurate and genuinely useful — not just SEO filler.