Python Datetime Handling Tutorial

Python Datetime Handling Tutorial

If you work with data in Python, you will deal with dates and times constantly — parsing timestamps from logs, calculating the number of days between events, filtering data by date range, formatting dates for reports, or handling time zones across international systems.

Python’s datetime handling is powerful but has enough moving parts that it trips up beginners regularly. Formats, time zones, parsing vs formatting, timedeltas — each piece matters and each has its own quirks.

In this guide, we will walk through everything you need to handle dates and times confidently in Python from basic datetime objects to time zones and pandas datetime operations.

The datetime Module

Python’s built-in datetime module provides the core tools for working with dates and times. It contains several important classes:

  • date — Stores year, month, and day
  • time — Stores hour, minute, second, and microsecond
  • datetime — Combines date and time into one object
  • timedelta — Represents a duration or difference between two dates/times
  • timezone — Handles time zone information

python

from datetime import datetime, date, time, timedelta, timezone
import datetime as dt

# Most common imports you will use
print(datetime.now())
# Output: 2024-01-15 14:30:45.123456

Creating Date and Datetime Objects

Creating a date Object

python

from datetime import date

# Create a specific date
d = date(2024, 1, 15)
print(d)          # Output: 2024-01-15
print(d.year)     # Output: 2024
print(d.month)    # Output: 1
print(d.day)      # Output: 15

# Get today's date
today = date.today()
print(today)      # Output: 2024-01-15 (current date)

Creating a datetime Object

python

from datetime import datetime

# Create a specific datetime
dt = datetime(2024, 1, 15, 14, 30, 45)
print(dt)         # Output: 2024-01-15 14:30:45

# With microseconds
dt = datetime(2024, 1, 15, 14, 30, 45, 123456)
print(dt)         # Output: 2024-01-15 14:30:45.123456

# Get current datetime
now = datetime.now()
print(now)        # Output: current local datetime

# Get current UTC datetime
utc_now = datetime.utcnow()
print(utc_now)    # Output: current UTC datetime

Accessing Components

python

dt = datetime(2024, 6, 15, 14, 30, 45)

print(dt.year)        # 2024
print(dt.month)       # 6
print(dt.day)         # 15
print(dt.hour)        # 14
print(dt.minute)      # 30
print(dt.second)      # 45
print(dt.weekday())   # 5 (0=Monday, 6=Sunday)
print(dt.isoweekday()) # 6 (1=Monday, 7=Sunday)
print(dt.date())      # 2024-06-15 (date object only)
print(dt.time())      # 14:30:45 (time object only)

Formatting Dates — strftime()

strftime() converts a datetime object to a string in any format you choose. The name stands for string format time.

Common Format Codes

CodeMeaningExample
%Y4-digit year2024
%y2-digit year24
%mMonth as zero-padded number01, 12
%BFull month nameJanuary
%bAbbreviated month nameJan
%dDay as zero-padded number05, 31
%AFull weekday nameMonday
%aAbbreviated weekdayMon
%HHour (24-hour clock)00, 23
%IHour (12-hour clock)01, 12
%MMinute00, 59
%SSecond00, 59
%pAM or PMAM, PM
%fMicroseconds123456

strftime() Examples

python

from datetime import datetime

dt = datetime(2024, 1, 15, 14, 30, 45)

# Common formats
print(dt.strftime('%Y-%m-%d'))
# Output: 2024-01-15

print(dt.strftime('%d/%m/%Y'))
# Output: 15/01/2024

print(dt.strftime('%B %d, %Y'))
# Output: January 15, 2024

print(dt.strftime('%A, %B %d, %Y'))
# Output: Monday, January 15, 2024

print(dt.strftime('%I:%M %p'))
# Output: 02:30 PM

print(dt.strftime('%Y-%m-%d %H:%M:%S'))
# Output: 2024-01-15 14:30:45

print(dt.strftime('%d %b %Y'))
# Output: 15 Jan 2024

Parsing Dates — strptime()

strptime() does the opposite of strftime, it parses a string into a datetime object. The name stands for string parse time.

python

from datetime import datetime

# Parse different string formats
dt1 = datetime.strptime('2024-01-15', '%Y-%m-%d')
print(dt1)   # Output: 2024-01-15 00:00:00

dt2 = datetime.strptime('15/01/2024', '%d/%m/%Y')
print(dt2)   # Output: 2024-01-15 00:00:00

dt3 = datetime.strptime('January 15, 2024', '%B %d, %Y')
print(dt3)   # Output: 2024-01-15 00:00:00

dt4 = datetime.strptime('2024-01-15 14:30:45', '%Y-%m-%d %H:%M:%S')
print(dt4)   # Output: 2024-01-15 14:30:45

dt5 = datetime.strptime('Mon Jan 15 2024', '%a %b %d %Y')
print(dt5)   # Output: 2024-01-15 00:00:00

Handling Parse Errors Safely

python

from datetime import datetime

def safe_parse(date_string, format_string):
    try:
        return datetime.strptime(date_string, format_string)
    except ValueError as e:
        print(f"Could not parse '{date_string}': {e}")
        return None

result = safe_parse('2024-13-45', '%Y-%m-%d')
# Output: Could not parse '2024-13-45': time data '2024-13-45' does not match...

Using dateutil for Flexible Parsing

When date strings come in inconsistent formats, dateutil.parser.parse() handles many formats automatically.

python

from dateutil import parser

# Handles many formats without specifying the format string
print(parser.parse('January 15, 2024'))
print(parser.parse('15-Jan-2024'))
print(parser.parse('01/15/2024'))
print(parser.parse('2024.01.15'))
print(parser.parse('15 Jan 24'))

# Install if needed: pip install python-dateutil

Timedelta

A timedelta represents a duration i.e. the difference between two dates or times. You can add or subtract timedeltas from datetime objects to move forward or backward in time.

Creating Timedeltas

python

from datetime import timedelta

# Create timedeltas
one_day = timedelta(days=1)
one_week = timedelta(weeks=1)
two_hours = timedelta(hours=2)
ninety_minutes = timedelta(minutes=90)
complex_delta = timedelta(days=5, hours=3, minutes=30, seconds=15)

print(one_week)      # Output: 7 days, 0:00:00
print(two_hours)     # Output: 2:00:00
print(complex_delta) # Output: 5 days, 3:30:15

Adding and Subtracting Time

python

from datetime import datetime, timedelta

now = datetime(2024, 1, 15, 14, 30, 0)

# Add time
tomorrow = now + timedelta(days=1)
next_week = now + timedelta(weeks=1)
three_hours_later = now + timedelta(hours=3)
thirty_days_later = now + timedelta(days=30)

print(tomorrow)          # 2024-01-16 14:30:00
print(next_week)         # 2024-01-22 14:30:00
print(three_hours_later) # 2024-01-15 17:30:00
print(thirty_days_later) # 2024-02-14 14:30:00

# Subtract time
yesterday = now - timedelta(days=1)
last_month = now - timedelta(days=30)

print(yesterday)    # 2024-01-14 14:30:00
print(last_month)   # 2023-12-16 14:30:00

Calculating the Difference Between Dates

python

from datetime import datetime

start = datetime(2024, 1, 1)
end = datetime(2024, 6, 15)

difference = end - start

print(difference)             # Output: 166 days, 0:00:00
print(difference.days)        # Output: 166
print(difference.seconds)     # Output: 0
print(difference.total_seconds()) # Output: 14342400.0

# Practical example — days until an event
today = datetime.now()
event_date = datetime(2024, 12, 31)
days_until = (event_date - today).days
print(f"Days until New Year: {days_until}")

Comparing Datetime Objects

Datetime objects support all comparison operators directly.

python

from datetime import datetime

dt1 = datetime(2024, 1, 15)
dt2 = datetime(2024, 6, 30)

print(dt1 < dt2)   # True — dt1 is earlier
print(dt1 > dt2)   # False
print(dt1 == dt2)  # False
print(dt1 != dt2)  # True

# Find the most recent date
dates = [
    datetime(2024, 3, 15),
    datetime(2024, 1, 5),
    datetime(2024, 6, 20)
]

latest = max(dates)
earliest = min(dates)

print(f"Latest: {latest.strftime('%Y-%m-%d')}")    # Latest: 2024-06-20
print(f"Earliest: {earliest.strftime('%Y-%m-%d')}") # Earliest: 2024-01-05

Handling Time Zones

Time zones are one of the trickiest parts of datetime handling. Python distinguishes between:

  • Naive datetime — No time zone information attached
  • Aware datetime — Has time zone information attached

Always use aware datetimes when working with data across multiple time zones.

Using the Built-in timezone

python

from datetime import datetime, timezone, timedelta

# UTC aware datetime
utc_now = datetime.now(timezone.utc)
print(utc_now)
# Output: 2024-01-15 14:30:45.123456+00:00

# Create a fixed offset timezone
est = timezone(timedelta(hours=-5))
cst = timezone(timedelta(hours=-6))
ist = timezone(timedelta(hours=5, minutes=30))

# Create aware datetime in EST
est_time = datetime(2024, 1, 15, 9, 30, tzinfo=est)
print(est_time)
# Output: 2024-01-15 09:30:00-05:00

Using pytz for Named Time Zones

python

import pytz
from datetime import datetime

# Get named time zones
eastern = pytz.timezone('America/New_York')
pacific = pytz.timezone('America/Los_Angeles')
london = pytz.timezone('Europe/London')
india = pytz.timezone('Asia/Kolkata')

# Create aware datetime
naive_dt = datetime(2024, 1, 15, 14, 30, 0)
eastern_dt = eastern.localize(naive_dt)
print(eastern_dt)
# Output: 2024-01-15 14:30:00-05:00

# Convert between time zones
pacific_dt = eastern_dt.astimezone(pacific)
london_dt = eastern_dt.astimezone(london)
india_dt = eastern_dt.astimezone(india)

print(f"Eastern: {eastern_dt.strftime('%Y-%m-%d %H:%M %Z')}")
print(f"Pacific: {pacific_dt.strftime('%Y-%m-%d %H:%M %Z')}")
print(f"London:  {london_dt.strftime('%Y-%m-%d %H:%M %Z')}")
print(f"India:   {india_dt.strftime('%Y-%m-%d %H:%M %Z')}")

# Install if needed: pip install pytz

Using zoneinfo (Python 3.9+)

python

from zoneinfo import ZoneInfo
from datetime import datetime

# Modern approach — no third-party library needed
eastern_dt = datetime(2024, 1, 15, 14, 30, tzinfo=ZoneInfo('America/New_York'))
pacific_dt = eastern_dt.astimezone(ZoneInfo('America/Los_Angeles'))

print(eastern_dt)  # 2024-01-15 14:30:00-05:00
print(pacific_dt)  # 2024-01-15 11:30:00-08:00

Pandas Datetime

In real data science work, you will handle datetime columns inside pandas DataFrames constantly. Pandas has excellent built-in datetime support.

Parsing Datetime Columns

python

import pandas as pd

df = pd.DataFrame({
    'date_str': ['2024-01-15', '2024-02-20', '2024-03-10', '2024-06-30'],
    'sales': [1500, 2200, 1800, 3100]
})

# Convert string column to datetime
df['date'] = pd.to_datetime(df['date_str'])
print(df.dtypes)
# date_str    object
# sales        int64
# date    datetime64[ns]

# pandas is flexible with formats
dates = pd.to_datetime(['January 15 2024', '15/02/2024', '2024.03.10'])
print(dates)

Extracting Date Components

python

df['year'] = df['date'].dt.year
df['month'] = df['date'].dt.month
df['day'] = df['date'].dt.day
df['day_of_week'] = df['date'].dt.day_name()
df['week_number'] = df['date'].dt.isocalendar().week
df['quarter'] = df['date'].dt.quarter
df['is_month_end'] = df['date'].dt.is_month_end

print(df[['date', 'year', 'month', 'day_of_week', 'quarter']])

Output:

dateyearmonthday_of_weekquarter
2024-01-1520241Monday1
2024-02-2020242Tuesday1
2024-03-1020243Sunday1
2024-06-3020246Sunday2

Filtering by Date in Pandas

python

import pandas as pd

df['date'] = pd.to_datetime(df['date_str'])

# Filter by specific date
specific = df[df['date'] == '2024-01-15']

# Filter by date range
start = pd.Timestamp('2024-02-01')
end = pd.Timestamp('2024-06-30')
date_range = df[(df['date'] >= start) & (df['date'] <= end)]

# Filter by year or month
q1 = df[df['date'].dt.quarter == 1]
january = df[df['date'].dt.month == 1]

Resampling — Aggregating by Time Period

python

import pandas as pd
import numpy as np

# Daily sales data
dates = pd.date_range('2024-01-01', periods=180, freq='D')
df = pd.DataFrame({
    'date': dates,
    'sales': np.random.randint(100, 500, 180)
})
df = df.set_index('date')

# Resample to different frequencies
weekly = df.resample('W').sum()
monthly = df.resample('ME').sum()
quarterly = df.resample('QE').mean()

print("Monthly Sales:")
print(monthly)

Date Arithmetic in Pandas

python

import pandas as pd

df['date'] = pd.to_datetime(df['date_str'])

# Add time to a datetime column
df['next_review'] = df['date'] + pd.Timedelta(days=90)
df['one_week_prior'] = df['date'] - pd.Timedelta(weeks=1)

# Calculate days since a reference date
reference = pd.Timestamp('2024-01-01')
df['days_since_start'] = (df['date'] - reference).dt.days

print(df[['date', 'next_review', 'days_since_start']])

Useful Datetime Patterns for Data Science

Generate a Date Range

python

import pandas as pd

# Daily dates for a full year
date_range = pd.date_range(start='2024-01-01', end='2024-12-31', freq='D')
print(f"Days in 2024: {len(date_range)}")

# Business days only
biz_days = pd.date_range(start='2024-01-01', end='2024-03-31', freq='B')
print(f"Business days in Q1: {len(biz_days)}")

# Monthly dates
months = pd.date_range(start='2024-01-01', periods=12, freq='ME')
print(months)

Find the Start and End of a Period

python

from datetime import datetime
import pandas as pd

dt = datetime(2024, 6, 15)

# Start and end of the month
month_start = dt.replace(day=1)
month_end = (dt.replace(day=1) + pd.DateOffset(months=1) - pd.DateOffset(days=1))

# Start and end of the year
year_start = dt.replace(month=1, day=1)
year_end = dt.replace(month=12, day=31)

print(f"Month: {month_start.date()} to {month_end.date()}")
print(f"Year:  {year_start.date()} to {year_end.date()}")

Unix Timestamp Conversion

python

from datetime import datetime, timezone

# DateTime to Unix timestamp
dt = datetime(2024, 1, 15, 14, 30, 0, tzinfo=timezone.utc)
timestamp = dt.timestamp()
print(f"Unix timestamp: {timestamp}")
# Output: Unix timestamp: 1705327800.0

# Unix timestamp to DateTime
dt_back = datetime.fromtimestamp(timestamp, tz=timezone.utc)
print(f"Back to datetime: {dt_back}")
# Output: Back to datetime: 2024-01-15 14:30:00+00:00

# Pandas — convert Unix timestamps in a column
import pandas as pd
df['datetime'] = pd.to_datetime(df['unix_timestamp'], unit='s')

Common Mistakes to Avoid

  • Mixing naive and aware datetimes — Adding or comparing a naive datetime (no timezone) with an aware datetime raises a TypeError. Always make all datetimes in a comparison either all naive or all aware
  • Using datetime.utcnow() without timezone infodatetime.utcnow() returns a naive datetime even though it represents UTC time. Use datetime.now(timezone.utc) instead for a proper UTC-aware datetime
  • Wrong strptime format codes — The format string in strptime must exactly match the input string. %Y is 4-digit year, %y is 2-digit. %m is month number, %M is minutes. Getting these swapped is extremely common
  • Assuming date.today() and datetime.now() return the same typedate.today() returns a date object. datetime.now() returns a datetime object. They are not directly comparable
  • Not setting the index when resampling in pandasdf.resample() requires a DatetimeIndex. Always set your datetime column as the index before resampling: df.set_index('date').resample('ME').sum()
  • Ignoring time zones in production systems — Always use UTC for storing timestamps in databases and APIs. Convert to local time zones only for display

Quick Reference Cheat Sheet

TaskCode
Current datedate.today()
Current datetimedatetime.now()
Current UTC datetimedatetime.now(timezone.utc)
Format datetime to stringdt.strftime('%Y-%m-%d')
Parse string to datetimedatetime.strptime(s, '%Y-%m-%d')
Add daysdt + timedelta(days=30)
Subtract daysdt - timedelta(days=7)
Days between dates(dt2 - dt1).days
Convert to pandas datetimepd.to_datetime(column)
Extract year in pandasdf['date'].dt.year
Filter by date range in pandasdf[(df['date'] >= start) & (df['date'] <= end)]
Resample monthlydf.resample('ME').sum()
Convert timezonedt.astimezone(pytz.timezone('US/Eastern'))
Unix timestamp to datetimedatetime.fromtimestamp(ts, tz=timezone.utc)

Python datetime handling covers a lot of ground but once you understand the core building blocks, everything else follows logically.

Here is a quick recap of what we covered:

  • datetime, date, time, and timedelta are the core classes in the datetime module
  • strftime() formats a datetime object to a string. strptime() parses a string to a datetime object
  • timedelta represents durations and enables date arithmetic
  • Always use aware datetimes when working across time zones — use zoneinfo (Python 3.9+) or pytz
  • Pandas provides powerful datetime support through pd.to_datetime(), the .dt accessor, and resample()
  • Store timestamps in UTC. Convert to local time zones only for display

Dates and times appear in almost every real-world dataset. Mastering how to parse, format, calculate, and filter them efficiently is one of those foundational skills that makes everything else in data science faster and cleaner.

FAQs

What is the difference between date and datetime in Python?

date stores only year, month, and day. datetime stores year, month, day, hour, minute, second, and microsecond. Use date when time is not relevant and datetime when you need both date and time.

What is the difference between strftime and strptime?

strftime formats a datetime object into a string. strptime parses a string into a datetime object. A helpful memory trick: strftime = “string from time”, strptime = “string parse time”.

How do I handle time zones in Python?

Use datetime.now(timezone.utc) for current UTC time. For named time zones, use zoneinfo (Python 3.9+) or pytz. Always store timestamps in UTC and convert to local time zones only when displaying to users.

How do I calculate the number of days between two dates?

Subtract one datetime from another: (date2 - date1).days. This returns the difference as a timedelta object — access .days for the integer number of days.

How do I parse dates in pandas?

Use pd.to_datetime(column) to convert a string column to datetime. Pandas automatically detects many common formats. For unusual formats, pass format='%Y/%m/%d' explicitly.

What is the best way to handle dates in production systems?

Always store and process timestamps in UTC using timezone-aware datetime objects. Convert to local time zones only at the presentation layer. Use ISO 8601 format (YYYY-MM-DD) for storing and transmitting dates as strings.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top