Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes

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

Sponsored

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.datetime over 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 datetime and date directly — always convert to the same type with .date() or datetime.combine().
  • Using datetime.now() without time zone — always prefer datetime.now(timezone.utc) for logs and comparisons.
  • Parsing strings with strptime but using wrong directive letters — %m vs %M (month vs minute) or %Y vs %y (4-digit vs 2-digit year).
  • Assuming timedelta(days=30) equals one month — physical months vary; use dateutil.relativedelta for month/year arithmetic.

Variations

  1. Use date.today() when you only need a date, not time — e.g., checking if a deadline has passed.
  2. Third-party arrow library provides a friendlier API with chaining: arrow.get('2025-01-15').shift(days=+3).
  3. For high-performance logging timestamps, datetime.utcnow() (deprecated in 3.12) → use datetime.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.txt for archival.
  • Calculating a user's age from their birthdate stored as a date object — using timedelta or (today - birthday).days // 365.

Key takeaways

  • Import datetime, date, time, and timedelta from the datetime module to handle all date/time needs.
  • Use strptime(format) to parse strings and strftime(format) to format datetimes — always match the directive letters exactly.
  • timedelta supports 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 datetime or both to date before comparison.
  • Validate user-supplied date strings (especially Feb 29) with try/except ValueError.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.