Work with Dates and Times using datetime
Learn how to use Python's datetime module to work with dates and times effectively in this step-by-step tutorial. Understand how to create, format, and manipulate dates and times, handle time zones, and deal with common edge cases. Includes a hands-on exercise and practical troubleshooting tips.
Focus: work with dates and times using datetime
Dates and times are everywhere in software — log timestamps, scheduled tasks, data analysis, and user-facing calendars. Yet few things trip up Python developers more consistently than working with dates and times using datetime. The datetime module is Python's built-in, batteries-included answer to this universal need. This lesson demystifies it so you can stop guessing and start building reliable time-aware code.
The problem this lesson solves
Without a clear mental model, trying to represent a simple date — like "tomorrow at 3 PM" — can lead to a tangle of string parsing, manual arithmetic, and off-by-one errors. You might reach for third-party libraries too soon, or write brittle code that breaks across time zones. The core problem: dates and times are not simple strings or numbers; they have their own semantics (leap years, months with different lengths, daylight saving time). Python's datetime module wraps these complexities into intuitive objects, so you can focus on what you want to do with a date instead of how to compute it.
Core concept / mental model
Think of datetime as a toolbox of four main object types:
date: A year, month, and day (no time). Useful for birthdays, due dates.time: Hours, minutes, seconds, microseconds — no date attached. Like a daily alarm.datetime: A date and a time together — the most common use case. Think of a flight departure or a log entry.timedelta: A duration (difference between two points in time). Think “3 hours later” or “5 days ago.”
These objects work together: you add a timedelta to a datetime to get a new datetime, or subtract two datetime objects to get a timedelta.
Pro tip: Always work with
datetimeobjects, never with raw strings. Strings are for display; objects are for logic.
How it works step by step
1. Getting the current moment
Use datetime.datetime.now() or datetime.date.today() to capture now.
2. Creating specific dates and times
You construct datetime(year, month, day, hour, minute, second) objects directly. The arguments map 1-to-1 to calendar components.
3. Formatting and parsing (the strftime / strptime dance)
strftime(string from time) — converts adatetimeobject to a formatted string for display.strptime(string parse time) — converts a string into adatetimeobject, using a format directive.
Format codes like %Y (4-digit year), %m (month), %d (day), %H (hour), %M (minute) are your friends. A full list is in the Python docs.
4. Date arithmetic with timedelta
Add or subtract a timedelta to shift a datetime forward or backward in time. timedelta(days=1) means "tomorrow," timedelta(hours=-12) means "12 hours ago."
5. Comparing dates
All datetime objects support <, >, ==, etc. The comparisons are intuitive: datetime(2024, 3, 15) > datetime(2024, 3, 14) is True.
Hands-on walkthrough
Let's start with a complete example that answers: "What was the date 7 days from today?" and prints it in a readable format.
from datetime import datetime, timedelta
# Get today's date and time
now = datetime.now()
print(f"Now: {now}")
# Add 7 days
future = now + timedelta(days=7)
print(f"7 days later: {future}")
# Format output nicely
formatted = future.strftime("%A, %B %d, %Y")
print(f"Formatted: {formatted}")
Expected output (actual values depend on when you run it):
Now: 2025-03-15 14:32:10.123456
7 days later: 2025-03-22 14:32:10.123456
Formatted: Saturday, March 22, 2025
Now let's parse a common string format (ISO 8601) into a datetime:
from datetime import datetime
date_string = "2025-12-25"
parsed_date = datetime.strptime(date_string, "%Y-%m-%d")
print(f"Parsed: {parsed_date}")
print(f"Day of week: {parsed_date.strftime('%A')}")
Expected output:
Parsed: 2025-12-25 00:00:00
Day of week: Thursday
Calculate the time until New Year:
from datetime import datetime
today = datetime.now()
new_year = datetime(today.year + 1, 1, 1)
difference = new_year - today
print(f"Days until New Year: {difference.days}")
print(f"Seconds until New Year: {difference.total_seconds()}")
Expected output:
Days until New Year: 291
Seconds until New Year: 25171200.0
Compare options / when to choose what
Once you're comfortable with datetime, you might wonder: should I use datetime or third-party libraries?
| Library/Module | Best for | Consideration |
|---|---|---|
datetime (built-in) |
Simple date manipulation, formatting, basic arithmetic | No timezone database built-in (use zoneinfo in Python 3.9+ for time zones) |
dateutil (third-party) |
Relative dates (e.g., "next Monday"), fuzzy parsing | Heavier dependency; great for complex human-style dates |
arrow (third-party) |
Human-friendly API, timezone handling | Overkill for basic needs; adds extra dependency |
pandas (third-party) |
Time series analysis, large datasets | Only when you already use pandas; heavy for standalone date work |
Rule of thumb: Start with datetime for 90% of tasks. Reach for dateutil only when you need natural language parsing like "last Friday". Avoid third-party modules for basic date math — they add complexity without benefit.
Troubleshooting & edge cases
Here are the most common pitfalls when working with dates and times using datetime:
Mistake: Confusing month and day in parser formats
# Wrong: %m is month, %d is day
datetime.strptime("2025-03-04", "%Y-%d-%m") # Would interpret 03 as day, 04 as month — silently wrong
Fix: Always double-check your format string matches the input exactly.
Mistake: Forgetting that datetime objects are immutable
You cannot change a datetime inline. today.replace(day=15) returns a new object — you must assign it.
start = datetime(2025, 1, 10)
start.day = 15 # AttributeError: attribute is read-only
Mistake: Assuming strftime uses 12-hour clock by default
%H is 24-hour, %I is 12-hour. Use %I with %p (AM/PM) for 12-hour display.
from datetime import datetime
dt = datetime(2025, 3, 15, 15, 30)
print(dt.strftime("%I:%M %p")) # "03:30 PM"
print(dt.strftime("%H:%M")) # "15:30"
Edge case: timedelta with months and years
timedelta does not support months or years because they vary in length. For month-level arithmetic, you need dateutil.relativedelta or manual logic.
# This does NOT exist in datetime:
# today + timedelta(months=1) # TypeError
Pro tip: For "3 months from now," increment the month number and wrap around. Use
dateutil.relativedeltafor a clean solution:from dateutil.relativedelta import relativedelta.
Edge case: Daylight saving time transitions
When an hour is skipped (spring forward) or repeated (fall back), naive datetime objects ignore the issue. Use time-zone-aware objects from zoneinfo to get correct arithmetic across DST boundaries.
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
eastern = ZoneInfo("America/New_York")
dt_aware = datetime(2025, 3, 9, 1, 30, tzinfo=eastern) # DST spring-forward at 2 AM
next_hour = dt_aware + timedelta(hours=1)
print(next_hour) # Correctly jumps to 3:30 AM EDT
What you learned & what's next
You now understand how to:
- Create
date,time,datetime, andtimedeltaobjects - Get the current date/time
- Format dates as strings (
strftime) and parse strings into dates (strptime) - Perform date arithmetic (
timedelta) and comparisons - Avoid common mistakes like misusing format codes or expecting month arithmetic in
timedelta - Recognize when to reach for third-party tools like
dateutil
Next step: Build on this foundation by exploring the time module for measuring performance (time.time()) or scheduling tasks (time.sleep()). Then dive into the calendar module for month calendars and weekday calculations. These skills will make you confident when handling time in any Python project.
Practice recap
Try this mini-exercise: Write a script that reads a date string like "2025-08-15 14:30:00", parses it into a datetime, adds 2 days and 5 hours, then prints the result in the format "Friday, August 15, 2025 at 07:30 PM". Use strptime, timedelta, and strftime — no third-party modules.
Common mistakes
- Using
%m(month) and%d(day) in the wrong order when parsing or formatting strings, leading to silent data corruption. - Trying to modify a datetime object in-place (e.g.,
start.day = 15) — datetime objects are immutable; always assign the result of.replace()or.shift(). - Assuming
timedeltasupportsmonthsoryears— it only works with days, seconds, and microseconds. For months/years, usedateutil.relativedelta. - Ignoring time zones (naive datetimes) when working with UTC offsets or DST transitions, causing off-by-one-hour errors in logs and scheduling.
- Using
strftimewith%H(24-hour) when a 12-hour format with AM/PM is expected — always pair%Iwith%pfor 12-hour display.
Variations
- Use
datetime.utcnow()(deprecated in 3.12) ordatetime.now(tz=timezone.utc)for UTC-aware datetimes in modern Python. - For parsing natural language dates like "next Tuesday" or relative expressions like "3 weeks ago", the third-party
dateutillibrary providesparser.parse()andrelativedelta. - When working exclusively with date ranges (no times),
datetime.dateobjects suffice; usedate_range = [start + timedelta(days=i) for i in range((end-start).days)].
Real-world use cases
- Automatically generating a timestamp for every log entry in a web application, formatted as ISO 8601 strings.
- Calculating subscription expiration dates: add 30 or 365 days from the purchase datetime to generate a renewal date.
- Scheduling a daily report email: capture the current time, compute the next 9 AM occurrence (adjusting for DST), and send the report at that moment.
Key takeaways
- Python's
datetimemodule providesdate,time,datetime, andtimedeltaobjects for all basic date/time needs. - Use
strftimeto format a datetime as a string andstrptimeto parse a string back into a datetime — always match the format specifier carefully. - Date arithmetic is done with
timedelta(days, seconds, microseconds); for month/year shifts, drop back to manual logic ordateutil.relativedelta. - Always prefer time-zone-aware datetimes (using
zoneinfo) in any system that spans multiple time zones or observes DST. - Avoid third-party libraries for 90% of date tasks; the built-in
datetimeis robust, well-documented, and keeps dependencies minimal.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.