Manipulating dates and times is one of the most common tasks in software development. Whether it's for logging events, calculating time intervals, formatting output for users, or handling time zones, Python's datetime module is the essential tool every developer needs to master.

In this complete guide, you'll learn everything from basic concepts to advanced techniques for handling temporal data in Python, with practical examples you can apply immediately in your projects.

📦 Introduction to the datetime Module

Python comes with the datetime module built into its standard library, which means you don't need to install anything extra to start using it. This module provides classes for working with dates, times, and combinations of both.

According to the official Python documentation, the datetime module offers several fundamental classes that make handling temporal data simple and intuitive.

import datetime

# Check module version
print(datetime.__version__)

🕐 The Main Classes of datetime

1. datetime.date — Date Only

The date class represents a date in year-month-day format, without time information. It's ideal when you need to work only with calendars, anniversaries, deadlines, etc.

import datetime

# Create a specific date
birth_date = datetime.date(1995, 3, 15)
print(birth_date)  # 1995-03-15

# Get today's date
today = datetime.date.today()
print(f"Today is {today}")  # Today is 2026-05-13

# Access individual components
print(f"Year: {today.year}")        # 2026
print(f"Month: {today.month}")      # 5
print(f"Day: {today.day}")          # 13
print(f"Weekday: {today.weekday()}")  # 0 (Monday)

# Weekday with name
days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
print(f"Today is {days[today.weekday()]}")

# Create date from Unix timestamp
date_from_ts = datetime.date.fromtimestamp(1700000000)
print(date_from_ts)

2. datetime.time — Time Only

The time class represents a specific time, without date information. Useful for scheduling, reminders, and time tracking.

import datetime

# Create a specific time
meeting_time = datetime.time(14, 30, 0)  # 2:30 PM
print(meeting_time)  # 14:30:00

# With microseconds
exact_time = datetime.time(14, 30, 45, 123456)
print(exact_time)  # 14:30:45.123456

# Access components
print(f"Hour: {meeting_time.hour}")        # 14
print(f"Minutes: {meeting_time.minute}")     # 30
print(f"Seconds: {meeting_time.second}")     # 0

# Format time
print(meeting_time.strftime("%H:%M"))      # 14:30
print(meeting_time.strftime("%I:%M %p"))  # 02:30 PM

3. datetime.datetime — Date and Time Combined

The datetime class is the most used and complete, combining date and time in a single object. It's perfect for timestamps, logs, event records, and much more.

import datetime

# Create specific datetime
now = datetime.datetime.now()
print(f"Now: {now}")

# Create specific datetime
event = datetime.datetime(2026, 12, 25, 18, 0, 0)
print(f"Christmas: {event}")

# Current datetime with timezone
utc_now = datetime.datetime.utcnow()
print(f"UTC now: {utc_now}")

# Create from string
date_str = "2026-05-13 15:30:00"
date_parsed = datetime.datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S")
print(f"Parsed: {date_parsed}")

# Access all components
print(f"Date: {now.date()}")
print(f"Time: {now.time()}")
print(f"Year: {now.year}, Month: {now.month}, Day: {now.day}")
print(f"Hour: {now.hour}, Minute: {now.minute}, Second: {now.second}")

4. datetime.timedelta — Time Differences

The timedelta class represents the difference between two dates or times. It's essential for calculating durations, intervals, and performing arithmetic operations with dates.

import datetime

# Create timedelta
one_day = datetime.timedelta(days=1)
two_hours = datetime.timedelta(hours=2)
half_hour = datetime.timedelta(minutes=30)

# Operations with datetime
today = datetime.datetime.now()
tomorrow = today + one_day
print(f"Tomorrow: {tomorrow}")

last_week = today - datetime.timedelta(weeks=1)
print(f"Last week: {last_week}")

# Calculate difference between dates
start = datetime.datetime(2026, 1, 1)
end = datetime.datetime(2026, 5, 13)
diff = end - start
print(f"Difference: {diff}")
print(f"Days: {diff.days}")
print(f"Seconds: {diff.seconds}")

# Total timedelta in hours
total_hours = diff.total_seconds() / 3600
print(f"Total hours: {total_hours:.2f}")

📝 Formatting Dates and Times

Formatting dates is crucial for displaying information to users in a clear and understandable way. Python offers two main functions for this: strftime() to format datetime to string and strptime() to convert string to datetime.

Most Used Format Codes

import datetime

now = datetime.datetime.now()

# Common formats
print(now.strftime("%d/%m/%Y"))           # 13/05/2026
print(now.strftime("%d-%m-%Y"))           # 13-05-2026
print(now.strftime("%d de %B de %Y"))     # 13 de maio de 2026
print(now.strftime("%H:%M:%S"))           # 14:30:45
print(now.strftime("%H:%M"))              # 14:30
print(now.strftime("%d/%m/%Y às %H:%M"))  # 13/05/2026 às 14:30

# International formats
print(now.strftime("%Y-%m-%d"))           # 2026-05-13 (ISO 8601)
print(now.strftime("%B %d, %Y"))          # May 13, 2026

# Day of week
print(now.strftime("%A"))                 # quarta-feira
print(now.strftime("%a"))                 # qua
print(now.strftime("%A, %d de %B de %Y")) # Wednesday, 13 de May de 2026

# Month
print(now.strftime("%B"))                 # maio
print(now.strftime("%b"))                 # may
print(now.strftime("%m"))                # 05

# 12-hour format
print(now.strftime("%I:%M %p"))           # 02:30 PM

For a complete reference of all format codes, W3Schools offers a useful and educational guide on Python datetime.

🔄 Converting String to datetime

One of the most frequent tasks in programming is converting strings in various formats to datetime objects. The strptime() (string parse time) does exactly that.

import datetime

# Various input formats
formats = [
    ("2026-05-13", "%Y-%m-%d"),
    ("13/05/2026", "%d/%m/%Y"),
    ("13-05-2026", "%d-%m-%Y"),
    ("May 13, 2026", "%B %d, %Y"),
    ("13 May 2026", "%d %B %Y"),
    ("2026-05-13 14:30:00", "%Y-%m-%d %H:%M:%S"),
    ("13/05/2026 14:30", "%d/%m/%Y %H:%M"),
]

for date_str, fmt in formats:
    try:
        dt = datetime.datetime.strptime(date_str, fmt)
        print(f"'{date_str}' -> {dt}")
    except ValueError as e:
        print(f"Error converting '{date_str}': {e}")

# Function to detect format automatically
def convert_date(date_str):
    formats = [
        "%Y-%m-%d",
        "%d/%m/%Y",
        "%d-%m-%Y",
        "%Y/%m/%d",
        "%B %d, %Y",
        "%d %B %Y",
    ]
    for fmt in formats:
        try:
            return datetime.datetime.strptime(date_str, fmt)
        except ValueError:
            continue
    raise ValueError(f"Could not convert: {date_str}")

print(convert_date("13/05/2026"))

🌍 Working with Time Zones

Handling time zones is one of the most complex aspects of date manipulation. Python uses the pytz library to manage time zones efficiently.

import datetime
import pytz

# Create datetime with timezone
tz_brazil = pytz.timezone('America/Sao_Paulo')
tz_usa = pytz.timezone('America/New_York')
tz_japan = pytz.timezone('Asia/Tokyo')

# Current datetime in different timezones
now_brazil = datetime.datetime.now(tz_brazil)
now_usa = datetime.datetime.now(tz_usa)
now_japan = datetime.datetime.now(tz_japan)

print(f"Brazil: {now_brazil}")
print(f"USA: {now_usa}")
print(f"Japan: {now_japan}")

# Create datetime in specific timezone
specific_date = datetime.datetime(2026, 12, 25, 18, 0, 0, tzinfo=tz_brazil)
print(f"Christmas in Brazil: {specific_date}")

# Convert between timezones
christmas_brazil = datetime.datetime(2026, 12, 25, 18, 0, 0, tzinfo=tz_brazil)
christmas_usa = christmas_brazil.astimezone(tz_usa)
christmas_japan = christmas_brazil.astimezone(tz_japan)

print(f"Christmas 6pm in Brazil = {christmas_usa.strftime('%H:%M')} in USA")
print(f"Christmas 6pm in Brazil = {christmas_japan.strftime('%H:%M')} in Japan")

# Get timezone from ISO 8601 string
data_iso = "2026-05-13T14:30:00-03:00"
dt_iso = datetime.datetime.fromisoformat(data_iso)
print(f"From ISO: {dt_iso}")

# List available timezones
print("Some available time zones:")
for tz in ['America/Sao_Paulo', 'America/New_York', 'Europe/London', 'Asia/Tokyo', 'Australia/Sydney']:
    print(f"  - {tz}")

The Real Python documentation offers a detailed guide on how to work with time zones correctly.

📊 Arithmetic Operations with Dates

One of the great advantages of the datetime module is the ability to perform arithmetic operations directly between dates and time intervals.

import datetime

# Addition and subtraction
date = datetime.datetime(2026, 5, 13)

# Add days
plus_10_days = date + datetime.timedelta(days=10)
print(f"10 days later: {plus_10_days}")

# Subtract weeks
minus_2_weeks = date - datetime.timedelta(weeks=2)
print(f"2 weeks before: {minus_2_weeks}")

# Add months (needs additional library or custom logic)
def add_months(date, months):
    month = date.month + months
    year = date.year + (month - 1) // 12
    month = ((month - 1) % 12) + 1
    day = min(date.day, [31, 29 if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) else 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month-1])
    return date.replace(year=year, month=month, day=day)

result = add_months(date, 6)
print(f"6 months later: {result}")

# Calculate age
def calculate_age(birth_date):
    today = datetime.date.today()
    age = today.year - birth_date.year
    if (today.month, today.day) < (birth_date.month, birth_date.day):
        age -= 1
    return age

born = datetime.date(1995, 3, 15)
print(f"Age: {calculate_age(born)} years")

# Check if it's a business day
def is_business_day(date):
    return date.weekday() < 5  # 0-4 = Mon to Fri

print(f"13/05/2026 is business day? {is_business_day(date)}")

🗓️ Generating Date Sequences

For many applications, you need to generate date sequences, such as creating a schedule, generating reports by period, or iterating over business days.

import datetime

# Generate sequence of days
start = datetime.date(2026, 5, 1)
end = datetime.date(2026, 5, 15)

days = []
date = start
while date <= end:
    days.append(date)
    date += datetime.timedelta(days=1)

print("Days of May (1-15):")
for d in days:
    print(f"  {d.strftime('%d/%m')}")

# Using list comprehension
days_may = [start + datetime.timedelta(days=i) for i in range(15)]
print(f"Total days: {len(days_may)}")

# Generate months between dates
def months_between(start, end):
    result = []
    current = start
    while current < end:
        result.append(current)
        if current.month == 12:
            current = current.replace(year=current.year + 1, month=1)
        else:
            current = current.replace(month=current.month + 1)
    return result

months = months_between(datetime.date(2026, 1, 1), datetime.date(2026, 12, 31))
print("Months of 2026:")
for m in months:
    print(f"  {m.strftime('%B')}")

# Business days between dates
def business_days(start, end):
    days = []
    date = start
    while date <= end:
        if date.weekday() < 5:
            days.append(date)
        date += datetime.timedelta(days=1)
    return days

business = business_days(datetime.date(2026, 5, 1), datetime.date(2026, 5, 31))
print(f"Business days in May: {len(business)}")

# Generate times for a specific day
def day_times(date, start_hour=9, end_hour=18, interval=30):
    times = []
    for h in range(start_hour, end_hour):
        for m in [0, interval]:
            if h == end_hour - 1 and m > 0:
                break
            times.append(date.replace(hour=h, minute=m))
    return times

schedule = day_times(datetime.datetime(2026, 5, 13))
print("Available times for 13/05:")
for t in schedule[:6]:
    print(f"  {t.strftime('%H:%M')}")

🔍 Comparing Dates

Comparing dates is essential for validations, orderings, and period checks. datetime allows various forms of comparison.

import datetime

# Example dates
d1 = datetime.datetime(2026, 5, 13)
d2 = datetime.datetime(2026, 6, 15)
d3 = datetime.datetime(2026, 5, 13, 14, 30)

# Direct comparisons
print(f"d1 == d2: {d1 == d2}")  # False
print(f"d1 != d2: {d1 != d2}")  # True
print(f"d1 < d2: {d1 < d2}")    # True
print(f"d1 <= d2: {d1 <= d2}")  # True
print(f"d1 > d2: {d1 > d2}")    # False

# Compare dates only (ignoring time)
print(f"d1.date() == d3.date(): {d1.date() == d3.date()}")  # True

# Check if it's in past, present or future
now = datetime.datetime.now()
if now < d1:
    print("d1 is in the future")
elif now > d1:
    print("d1 is in the past")
else:
    print("d1 is now")

# Check if within a range
start = datetime.datetime(2026, 1, 1)
end = datetime.datetime(2026, 12, 31)
print(f"d1 is between {start.date()} and {end.date()}? {start <= d1 <= end}")

# Sort list of dates
dates = [
    datetime.datetime(2026, 3, 15),
    datetime.datetime(2026, 1, 10),
    datetime.datetime(2026, 12, 25),
    datetime.datetime(2026, 7, 4),
]
dates.sort()
print("Sorted dates:")
for d in dates:
    print(f"  {d.strftime('%d/%m/%Y')}")

⏰ Measuring Execution Time

Datetime is often used to measure code execution time, which is fundamental for performance optimization.

import datetime
import time

# Method 1: Using datetime
start = datetime.datetime.now()
time.sleep(1)  # Simulating processing
end = datetime.datetime.now()
duration = end - start
print(f"Duration: {duration.total_seconds():.2f} seconds")

# Method 2: Using time.time()
start_ts = time.time()
time.sleep(0.5)
end_ts = time.time()
print(f"Duration (time): {end_ts - start_ts:.2f} seconds")

# Method 3: Using context manager
class Timer:
    def __init__(self):
        self.start = None
        self.end = None

    def __enter__(self):
        self.start = datetime.datetime.now()
        return self

    def __exit__(self, *args):
        self.end = datetime.datetime.now()
        self.duration = self.end - self.start

    def elapsed_time(self):
        return self.duration.total_seconds()

with Timer() as timer:
    time.sleep(0.3)
    print(f"Measured time: {timer.elapsed_time():.3f} seconds")

# Measure multiple executions
def measure_function(func, times=100):
    results = []
    for _ in range(times):
        start = datetime.datetime.now()
        func()
        end = datetime.datetime.now()
        results.append((end - start).total_seconds())

    average = sum(results) / len(results)
    return {
        'average': average,
        'min': min(results),
        'max': max(results),
        'times': times
    }

def test_function():
    total = 0
    for i in range(1000):
        total += i

result = measure_function(test_function, times=50)
print(f"Average: {result['average']*1000:.2f}ms")
print(f"Min: {result['min']*1000:.2f}ms, Max: {result['max']*1000:.2f}ms")

🛠️ Practical Use Cases

1. Calculate days until a future date

import datetime

def days_until(target):
    today = datetime.date.today()
    if isinstance(target, datetime.datetime):
        target = target.date()
    diff = target - today
    return diff.days

# Birthday
birthday = datetime.date(2026, 12, 25)
print(f"Days until Christmas: {days_until(birthday)}")

# Holiday
holiday = datetime.date(2027, 1, 1)
print(f"Days until New Year: {days_until(holiday)}")

2. Check if a year is leap year

import datetime

def is_leap_year(year):
    return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)

# February in different years
for year in [2024, 2026, 2027, 2028]:
    print(f"{year}: {'Leap' if is_leap_year(year) else 'Normal'}")

# Using dateutil
from datetime import date
import calendar
print(f"February 2024 has {calendar.monthrange(2024, 2)[1]} days")

3. Generate filename with timestamp

import datetime

def generate_filename(prefix, extension):
    now = datetime.datetime.now()
    timestamp = now.strftime("%Y%m%d_%H%M%S")
    return f"{prefix}_{timestamp}.{extension}"

print(generate_filename("backup", "zip"))
print(generate_filename("report", "pdf"))
print(generate_filename("log", "txt"))

4. Calculate event duration

import datetime

events = [
    ("Meeting", datetime.datetime(2026, 5, 13, 14, 0), datetime.datetime(2026, 5, 13, 15, 30)),
    ("Workshop", datetime.datetime(2026, 5, 13, 9, 0), datetime.datetime(2026, 5, 13, 12, 0)),
    ("Talk", datetime.datetime(2026, 5, 13, 16, 0), datetime.datetime(2026, 5, 13, 17, 45)),
]

for name, start, end in events:
    duration = end - start
    print(f"{name}: {duration} ({duration.total_seconds()/3600:.2f}h)")

5. Validate expiration date

import datetime

def is_expired(expiration_date):
    return datetime.datetime.now() > expiration_date

# Simulate SSL certificate
certificate_expires = datetime.datetime(2026, 12, 31, 23, 59, 59)
print(f"Certificate expired? {is_expired(certificate_expires)}")

# Check days remaining
def days_to_expire(expiration_date):
    now = datetime.datetime.now()
    if isinstance(expiration_date, datetime.datetime):
        expiration_date = expiration_date.replace(tzinfo=None) - now
    else:
        expiration_date = expiration_date - now.date()
    return expiration_date.days

print(f"Days until expiration: {days_to_expire(certificate_expires)}")

📚 Complementary Libraries

Although the datetime module is extremely powerful, there are libraries that can make working with dates in Python even easier.

dateutil - Useful extensions

# dateutil provides advanced features
from dateutil import parser
from dateutil.relativedelta import relativedelta
from dateutil.rrule import rrule, MONTHLY

# Automatic string parsing
data = parser.parse("tomorrow")
print(f"Parsed: {data}")

data = parser.parse("15 de maio de 2026")
print(f"Parsed: {data}")

# Add months with relativedelta
from datetime import datetime
now = datetime.now()
plus_3_months = now + relativedelta(months=3)
print(f"3 months later: {plus_3_months}")

# Generate monthly occurrences
from dateutil.rrule import rrule, MONTHLY
start = datetime(2026, 1, 1)
end = datetime(2026, 12, 31)
occurrences = list(rrule(MONTHLY, dtstart=start, until=end))
print(f"Months of 2026: {len(occurrences)}")

pandas - For time series

For data analysis and time series, pandas is the most recommended library. It offers advanced features for date manipulation in data analysis contexts.

import pandas as pd

# Create DataFrame with dates
dates = pd.date_range(start='2026-01-01', end='2026-12-31', freq='D')
df = pd.DataFrame({'date': dates, 'value': range(len(dates))})

# Time operations
df['year'] = df['date'].dt.year
df['month'] = df['date'].dt.month
df['day_of_week'] = df['date'].dt.day_name()
df['quarter'] = df['date'].dt.quarter

print(df.head())

# Resample - aggregate by period
df['value'] = range(1, len(dates) + 1)
monthly = df.set_index('date')['value'].resample('M').sum()
print(f"\nTotal by month:\n{monthly}")

⚠️ Common Pitfalls and How to Avoid Them

1. Mutability of datetime

import datetime

# WARNING: datetime is mutable!
dt = datetime.datetime(2026, 5, 13)
dt_modified = dt.replace(month=12)  # Creates new object
print(f"Original: {dt}")
print(f"Modified: {dt_modified}")

# To change, you need to assign
dt = dt.replace(month=12)
print(f"Changed: {dt}")

2. Time zones and localization

import datetime
import pytz

# Always specify timezone
tz = pytz.timezone('America/Sao_Paulo')
dt = datetime.datetime(2026, 5, 13, 12, 0, tzinfo=tz)
print(f"With timezone: {dt}")

# WARNING: localize, not just assign
dt_not_localized = datetime.datetime(2026, 5, 13, 12, 0)
dt_localized = tz.localize(dt_not_localized)
print(f"Localized: {dt_localized}")

# Use timezone when creating fromisoformat (Python 3.11+)
dt_iso = datetime.datetime.fromisoformat("2026-05-13T12:00:00-03:00")
print(f"From ISO: {dt_iso}")

3. Dates outside valid range

import datetime

# WARNING: invalid dates can raise errors or produce unexpected results
try:
    dt = datetime.datetime(2026, 2, 30)  # February doesn't have 30 days
except ValueError as e:
    print(f"Error: {e}")

# Always validate dates
def valid_date(year, month, day):
    try:
        datetime.date(year, month, day)
        return True
    except ValueError:
        return False

print(f"2026-02-30 valid? {valid_date(2026, 2, 30)}")
print(f"2026-02-28 valid? {valid_date(2026, 2, 28)}")
print(f"2024-02-29 valid? {valid_date(2024, 2, 29)}")  # Leap year

🚀 Next Steps

Now that you master Python's datetime module, you're prepared to handle most situations involving dates and times in your applications. To further deepen your knowledge, we recommend:

Mastering datetime is essential for any Python developer. Keep practicing and exploring the possibilities of this powerful module! 🎯