Handle Dates & Times with Datetime
Learn to handle dates and times with Python's datetime module in this hands-on tutorial. Understand core concepts, step-by-step examples, troubleshooting, and what to study next.
Focus: handle dates and times with the datetime module
Dates and times seem simple—until they're not. You hardcode a file timestamp, run a script at midnight, or compare two logged events from different time zones, and suddenly you're knee-deep in off-by-one errors, time zone confusion, or sneaky formatting bugs. Python's datetime module is the standard way to handle all of this: creating, parsing, formatting, and calculating with dates and times in a reliable, cross-platform way. In this tutorial, you'll move from raw timestamps to expert-level scheduling logic in minutes.
The problem this lesson solves
Bare-bones time handling—like storing "2025-01-15" as a plain string—breaks the moment you need to:
- Add days to a date (e.g., "15 days from now")
- Compare two events ("Did this happen before that?")
- Output in different formats ("January 15, 2025" vs "2025-01-15")
- Respect time zones ("Is it still yesterday in UTC?")
With raw integers or strings you have to write your own arithmetic, formatting, and validation logic—error-prone and non-standard. Python's datetime module wraps the complexity into objects that just work.
Core concept / mental model
Think of datetime as a toolkit of four main classes, each solving one piece of the problem:
datetime.date– A date (year, month, day) without time. Used for birthdays, deadlines, or holiday calcs.datetime.time– A time of day (hour, minute, second, microsecond) without a date. Think "alarm clock" or "scheduled job at 14:30."datetime.datetime– A date and time together. The most common class. Think "a meeting on 2025-01-15 at 09:00."datetime.timedelta– A duration of time. Not a moment, but difference between two moments. Used for arithmetic (add 3 days, subtract 2 weeks, etc.)
Pro tip: Always prefer
datetime.datetimeover separate date and time objects unless you're certain you never need the other half.
How it works step by step
1. Import the module
You'll almost always start with:
from datetime import datetime, date, time, timedelta
No external installation—datetime is part of Python's standard library.
2. Create a datetime object
There are three common ways:
# Current moment (local time)
now = datetime.now()
# Specific date and time
dt = datetime(2025, 1, 15, 9, 30, 0) # Jan 15, 2025, 09:30:00
# From a string (parsing)
date_string = "2025-01-15 09:30:00"
dt_parsed = datetime.strptime(date_string, "%Y-%m-%d %H:%M:%S")
3. Format a datetime to string
Use strftime (string format time) with directives:
dt = datetime(2025, 1, 15, 14, 30, 0)
formatted = dt.strftime("%A, %B %d, %Y at %I:%M %p")
print(formatted) # Wednesday, January 15, 2025 at 02:30 PM
Common directives:
- %Y – 4-digit year
- %m – 2-digit month (01-12)
- %d – 2-digit day (01-31)
- %H – 24-hour hour (00-23)
- %I – 12-hour hour (01-12)
- %M – minute (00-59)
- %S – second (00-59)
- %p – AM/PM
- %A – full weekday name (e.g., "Wednesday")
4. Arithmetic with timedelta
Add or subtract time spans:
now = datetime.now()
tomorrow = now + timedelta(days=1)
last_week = now - timedelta(weeks=1)
next_hour = now + timedelta(hours=1)
You can also compute the difference between two datetimes (gives a timedelta):
date1 = datetime(2025, 1, 15, 9, 0, 0)
date2 = datetime(2025, 1, 10, 18, 30, 0)
diff = date1 - date2
print(diff.days) # 4
print(diff.total_seconds()) # 376200.0
Hands-on walkthrough
Example 1: Current date and time
from datetime import datetime
now = datetime.now()
print(f"Current date & time: {now}")
print(f"Year: {now.year}")
print(f"Month: {now.month}")
print(f"Day: {now.day}")
Output (will vary):
Current date & time: 2025-01-15 14:35:21.123456
Year: 2025
Month: 1
Day: 15
Example 2: Parsing a string log entry
from datetime import datetime
log_entry = "[2025-01-15 10:22:35] INFO User logged in"
# Extract the datetime portion and parse it
date_part = log_entry[1:20] # "2025-01-15 10:22:35"
dt = datetime.strptime(date_part, "%Y-%m-%d %H:%M:%S")
print(f"Parsed datetime: {dt}")
print(f"Weekday: {dt.strftime('%A')}") # Wednesday
Example 3: Add business days (skip weekends)
from datetime import datetime, timedelta
def add_business_days(start, days):
current = start
added = 0
while added < days:
current += timedelta(days=1)
if current.weekday() < 5: # Monday=0, Sunday=6
added += 1
return current
start = datetime(2025, 1, 15) # Wednesday
result = add_business_days(start, 3)
print(f"3 business days from {start.date()} is {result.date()}")
# Output: 3 business days from 2025-01-15 is 2025-01-20 (Monday, skipping Sat/Sun)
Example 4: Countdown timer
from datetime import datetime, timedelta
# Event in 7 days
event_date = datetime(2025, 1, 22, 15, 0, 0)
now = datetime.now()
remaining = event_date - now
if remaining.total_seconds() > 0:
print(f"Event starts in {remaining.days} days and {remaining.seconds // 3600} hours.")
else:
print("Event has already started!")
Compare options / when to choose what
| Approach | Best for | Pitfall |
|---|---|---|
datetime.now() |
Current moment, simple timestamps | Local time only; no time zone info |
datetime(year, month, day, ...) |
Fixed, known dates (e.g., known event) | Naïve about time zones; doesn't handle DST |
strptime() / strftime() |
Parsing/formating custom string formats | Can be slow on huge datasets; must match letters exactly |
date.today() |
Date-only needs (birthdays, holidays) | No time component, can't compare with datetime directly |
timedelta |
Date arithmetic (add days, find difference) | Doesn't handle month/year boundaries well (e.g., 1 month ≠ 30 days exactly) |
calendar module |
Finding weekday counts, month ranges | Separate import, less intuitive for simple needs |
Pro tip: If you need months or years arithmetic, prefer
dateutil.relativedelta(third-party) for precision:pip install python-dateutil.
Troubleshooting & edge cases
1. Comparing date and datetime
d = date(2025, 1, 15)
dt = datetime(2025, 1, 15, 0, 0, 0)
print(d == dt) # TypeError: can't compare datetime.datetime to datetime.date
Always convert to the same type: dt.date() == d or datetime.combine(d, time.min).
2. Midnight confusion
midnight = datetime(2025, 1, 15, 0, 0, 0)
print(midnight.time()) # 00:00:00 — correct, but be careful when formatting: `%I:%M %p` gives "12:00 AM"
3. February 29 on non-leap years
import datetime
try:
dt = datetime(2023, 2, 29) # 2023 is not a leap year
except ValueError as e:
print(e) # day is out of range for month
Always validate or handle with try/except if input is user-supplied.
4. Time zones (naïve vs aware)
Datetimes without tzinfo are "naïve" — they don't know their time zone. This leads to off-by-hours errors when comparing with UTC times. Use pytz or zoneinfo (Python 3.9+) for aware objects:
from datetime import datetime, timezone
utc_now = datetime.now(timezone.utc) # aware now
What you learned & what's next
You now know how to handle dates and times with the datetime module — creating objects, parsing strings, formatting output, doing arithmetic with timedelta, and avoiding common pitfalls like type mismatches and naïve time zones. You've written practical code for business days, countdowns, and log parsing.
Next up: [Working with JSON data] — parse and generate JSON to exchange data between systems, building on the structured thinking you've practiced here.
Practice recap
Try this mini exercise: write a function days_until_birthday(birthday_str) that accepts a date string in "YYYY-MM-DD" format, parses it with strptime, finds the next occurrence (adjusting year if this year's birthday already passed), and prints a countdown in days. Use datetime.now().date() for comparison.
Common mistakes
- Comparing
datetimeanddatedirectly — always convert to the same type with.date()ordatetime.combine(). - Using
datetime.now()without time zone — always preferdatetime.now(timezone.utc)for logs and comparisons. - Parsing strings with
strptimebut using wrong directive letters —%mvs%M(month vs minute) or%Yvs%y(4-digit vs 2-digit year). - Assuming
timedelta(days=30)equals one month — physical months vary; usedateutil.relativedeltafor month/year arithmetic.
Variations
- Use
date.today()when you only need a date, not time — e.g., checking if a deadline has passed. - Third-party
arrowlibrary provides a friendlier API with chaining:arrow.get('2025-01-15').shift(days=+3). - For high-performance logging timestamps,
datetime.utcnow()(deprecated in 3.12) → usedatetime.now(timezone.utc)instead.
Real-world use cases
- Parsing ISO 8601 timestamps from an API response to filter events within a date range.
- Generating unique filenames with date stamps like
log_2025-01-15_14-30-00.txtfor archival. - Calculating a user's age from their birthdate stored as a date object — using
timedeltaor(today - birthday).days // 365.
Key takeaways
- Import
datetime,date,time, andtimedeltafrom thedatetimemodule to handle all date/time needs. - Use
strptime(format)to parse strings andstrftime(format)to format datetimes — always match the directive letters exactly. timedeltasupports addition/subtraction for days, seconds, microseconds — but not months or years reliably.- Always use time zone aware datetimes (
timezone.utc) for logging and cross-system communication. - Avoid comparing mixed types — convert both to
datetimeor both todatebefore comparison. - Validate user-supplied date strings (especially Feb 29) with
try/except ValueError.
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.