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
| Code | Meaning | Example |
|---|---|---|
| %Y | 4-digit year | 2024 |
| %y | 2-digit year | 24 |
| %m | Month as zero-padded number | 01, 12 |
| %B | Full month name | January |
| %b | Abbreviated month name | Jan |
| %d | Day as zero-padded number | 05, 31 |
| %A | Full weekday name | Monday |
| %a | Abbreviated weekday | Mon |
| %H | Hour (24-hour clock) | 00, 23 |
| %I | Hour (12-hour clock) | 01, 12 |
| %M | Minute | 00, 59 |
| %S | Second | 00, 59 |
| %p | AM or PM | AM, PM |
| %f | Microseconds | 123456 |
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:
| date | year | month | day_of_week | quarter |
|---|---|---|---|---|
| 2024-01-15 | 2024 | 1 | Monday | 1 |
| 2024-02-20 | 2024 | 2 | Tuesday | 1 |
| 2024-03-10 | 2024 | 3 | Sunday | 1 |
| 2024-06-30 | 2024 | 6 | Sunday | 2 |
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 info —
datetime.utcnow()returns a naive datetime even though it represents UTC time. Usedatetime.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.
%Yis 4-digit year,%yis 2-digit.%mis month number,%Mis minutes. Getting these swapped is extremely common - Assuming date.today() and datetime.now() return the same type —
date.today()returns adateobject.datetime.now()returns adatetimeobject. They are not directly comparable - Not setting the index when resampling in pandas —
df.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
| Task | Code |
|---|---|
| Current date | date.today() |
| Current datetime | datetime.now() |
| Current UTC datetime | datetime.now(timezone.utc) |
| Format datetime to string | dt.strftime('%Y-%m-%d') |
| Parse string to datetime | datetime.strptime(s, '%Y-%m-%d') |
| Add days | dt + timedelta(days=30) |
| Subtract days | dt - timedelta(days=7) |
| Days between dates | (dt2 - dt1).days |
| Convert to pandas datetime | pd.to_datetime(column) |
| Extract year in pandas | df['date'].dt.year |
| Filter by date range in pandas | df[(df['date'] >= start) & (df['date'] <= end)] |
| Resample monthly | df.resample('ME').sum() |
| Convert timezone | dt.astimezone(pytz.timezone('US/Eastern')) |
| Unix timestamp to datetime | datetime.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, andtimedeltaare the core classes in the datetime modulestrftime()formats a datetime object to a string.strptime()parses a string to a datetime objecttimedeltarepresents durations and enables date arithmetic- Always use aware datetimes when working across time zones — use
zoneinfo(Python 3.9+) orpytz - Pandas provides powerful datetime support through
pd.to_datetime(), the.dtaccessor, andresample() - 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.