Work with Dates and Times in pandas
Learn to work with dates and times in pandas in this Data Analysis with Python tutorial. Master parsing, resampling, and time-based filtering with hands-on examples and troubleshooting tips.
Focus: work with dates and times in pandas
You've cleaned your DataFrames, joined tables, and filtered rows with ease — but then a column of strings like '2024-03-15' stares back at you, and suddenly sorting chronologically, computing month-over-month growth, or plotting a time series feels like wrestling a spreadsheet. Raw text dates are one of the most common and frustrating roadblocks in data analysis: they break sorting, prevent arithmetic, and silently produce wrong answers when you try to compare them. In this lesson, you'll learn how to work with dates and times in pandas — converting strings to proper datetime objects, extracting components, filtering by calendar periods, and resampling — so your time-based analysis becomes fast, accurate, and elegant.
The problem this lesson solves
Datetimes are everywhere: sales timestamps, server logs, sensor readings, user sign-ups. Yet most raw data stores them as strings or, worse, as separate day/month/year columns. Treating these as plain text leads to a cascade of issues:
- Sorting fails:
'2024-01-05'sorts before'2024-01-01'alphabetically because'0'vs'1'— you get lexicographic, not chronological, order. - Filtering is brittle: Writing
df[df['date'] > '2024-01-01']on strings works only if every value has the exact same format — one'2024-1-1'breaks the comparison. - Arithmetic is impossible: You can't subtract two strings to find the number of days between events, compute age, or calculate dwell time.
- Grouping by month/quarter/year requires tedious string slicing:
'2024-03-15'[:7]might give you a month key, but it's fragile and ugly.
pandas solves all of this with a dedicated datetime data type that understands calendar semantics. Once you convert, you get access to a rich set of methods for extracting, filtering, resampling, and timezone handling — all vectorized and fast, even on millions of rows.
Core concept / mental model
Think of a pandas datetime column not as a string, but as a structured object that knows three layers of information: the date (year, month, day), the time (hour, minute, second, microsecond), and the timezone (optional). Under the hood, pandas stores each value as a 64-bit integer representing nanoseconds since the Unix epoch (1970-01-01 UTC) — but you never interact with that integer directly. Instead, you work with rich, human-readable components.
Here's a mental model: a string date is like a label on a box; a datetime is the box itself, with drawers labeled year, month, day, hour, minute, second. You can open any drawer at will, and you can compare two boxes not by their labels but by their internal timeline.
Two key concepts:
pd.to_datetime()— the universal converter that parses strings, numbers, or lists intodatetimeobjects.DatetimeIndex— when you set a datetime column as the index, your DataFrame becomes a time series, enabling powerful time-based operations like resampling and time-based slicing.
Pro tip: Always store dates as
datetimeunless you truly need only the date (e.g.,datetime.date). The fulldatetimetype is more flexible and you can always extract the date part later with.dt.date.
How it works step by step
Let's break down the workflow for handling dates and times in pandas:
- Parse — Convert your string column to
datetimeusingpd.to_datetime(). Specify the format if your strings aren't ISO-8601 (e.g.,'2024-03-15'). - Inspect — Verify the conversion succeeded by checking the
dtype(should bedatetime64[ns]or similar) and looking forNaT(Not a Time) values from parsing failures. - Extract components — Use the
.dtaccessor to get year, month, day, weekday, hour, etc. This is essential for grouping, aggregating, and feature engineering. - Filter — Use boolean conditions with datetime comparisons or the
betweenmethod to select rows within a date range. - Set index — Set the datetime column as the index to unlock time-series features like
resample()and time-based slicing withdf.loc['2024']. - Resample and aggregate — Group data by calendar periods (e.g., daily, monthly) and compute summaries like mean, sum, or count.
- Compute durations — Subtract two datetime columns to get
Timedeltavalues, useful for measuring time spans.
Each step builds on the previous one, transforming raw text into a powerful analytical tool.
Hands-on walkthrough
Let's apply these steps using a real-world scenario: a small CSV of daily sales records. First, parse the date column.
import pandas as pd
data = {
'date': ['2024-01-05', '2024-01-12', '2024-02-03', '2024-02-14', '2024-03-01'],
'sales': [120, 340, 210, 410, 275]
}
df = pd.DataFrame(data)
# Convert 'date' column to datetime
df['date'] = pd.to_datetime(df['date'])
print(df.dtypes)
Output:
date datetime64[ns]
sales int64
dtype: object
Now we can extract components and filter:
# Extract month and weekday
monthly_sales = df.groupby(df['date'].dt.month)['sales'].sum()
print(monthly_sales)
# Filter sales from February onward
feb_onward = df[df['date'] >= '2024-02-01']
print(feb_onward)
Output:
month
1 460
2 620
3 275
Name: sales, dtype: int64
date sales
2 2024-02-03 210
3 2024-02-14 410
4 2024-03-01 275
Now let's set the datetime as index and resample to monthly sums:
df = df.set_index('date')
monthly = df.resample('M').sum()
print(monthly)
Output:
sales
date
2024-01-31 460
2024-02-29 620
2024-03-31 275
Notice that resampling uses the end of month label ('M' → month end, 'MS' → month start). That's a common source of confusion.
Pro tip: Use
'MS'for month start if you prefer labels like'2024-01-01', or use'ME'(month end) in pandas 2.0+ —'M'is deprecated but still works.
Compare options / when to choose what
| Approach | Best for | Pros | Cons |
|---|---|---|---|
pd.to_datetime() |
Converting strings/numbers to datetime | Universal, handles many formats, vectorized | May be slow on huge datasets if format parsing is ambiguous |
pd.date_range() |
Generating a range of dates for testing or calendar creation | Fast, flexible (freq parameter) | Not for parsing |
.dt accessor |
Extracting components (year, month, day) | Clean, readable, vectorized | Requires datetime dtype |
resample() |
Aggregating at calendar frequencies | Built-in, efficient, supports many rules | Labeling defaults can confuse |
pd.Timestamp |
Single point-in-time values | Rich methods, precise | Scalar, not vectorized |
Choose pd.to_datetime() for almost all parsing tasks. If you need to generate a sequence (e.g., for a calendar), pd.date_range() is your friend. For component extraction, stick with .dt. For aggregation, resample() is the idiomatic way.
Troubleshooting & edge cases
Handling dates in pandas is powerful, but a few pitfalls can trip you up:
- Error:
OutOfBoundsDatetime— You have a year outside the supported range (1677–2262). Convert usingpd.to_datetime(..., errors='coerce')to getNaTfor out-of-range values. NaTappearing unexpectedly — Some string formats aren't recognized (e.g.,'2024/03/15'vs'2024-03-15'). Use theformatparameter:pd.to_datetime(df['date'], format='%Y/%m/%d')to force a specific pattern and avoid ambiguity.- Mixed formats in the same column — pandas infers a format from the first few values. If later rows differ, you get
NaT. Solution: passformat=...or useerrors='coerce'and then investigate the resultingNaTrows. - Warning about deprecated
'M'frequency — In pandas 2.0+,'M'for month end is deprecated; use'ME'or'MS'. Prefer new aliases to avoid warnings. - Timezone issues — Naive timestamps (no timezone) compared with timezone-aware ones raise
TypeError. Convert with.dt.tz_localize(),.dt.tz_convert(), or usepd.to_datetime(..., utc=True). - Performance on large data — Parsing a million strings can be slow. Specify
formatwhen you know it, and consider usingerrors='coerce'to parallelize conversion.
What you learned & what's next
You've mastered the core of working with dates and times in pandas: converting strings with pd.to_datetime(), extracting components with .dt, filtering by date ranges, setting a DatetimeIndex, and resampling with resample(). You can now sort chronologically, compute durations, and aggregate by calendar periods — all essential skills for time-series analysis.
This lesson is a stepping stone in your Data Analysis with Python journey. Next, you'll tackle time series analysis — moving averages, lag features, and forecasting — where date handling becomes the backbone. You'll also apply these skills when visualizing trends over time with Matplotlib and Seaborn.
Now, practice on your own: take a messy CSV with date strings in multiple formats, clean them, and compute monthly averages. You'll be amazed how quickly your data becomes insightful.
# Bonus: handling a messy column with mixed formats
df = pd.DataFrame({'date': ['2024-01-01', '02/15/2024', '2024-03-01']})
df['date'] = pd.to_datetime(df['date'], errors='coerce', format='mixed')
print(df)
Output (post-format parsing):
date
0 2024-01-01
1 2024-02-15
2 2024-03-01
Practice recap
Take a CSV with a 'date' column containing a mix of formats like '2024/01/01', 'Jan 15, 2024', and '2024-02-14'. Use pd.to_datetime() with format='mixed', handle any NaT values, then set the index and compute quarterly sales totals with resample('QE'). Print your result and compare it to grouping by .dt.quarter.
Common mistakes
- Assuming
pd.to_datetime()will parse every format automatically — always check forNaTvalues and specifyformatif mixed or non-ISO strings appear. - Using
'M'as a resample frequency and expecting month start — in pandas 2.0+ it means month end; use'MS'or'ME'explicitly. - Comparing timezone-naive and timezone-aware datetimes, which raises a
TypeError— localize or convert both to the same timezone. - Forgetting to set the datetime column as the index before using
resample(), leading to confusing errors or wrong grouping. - Performing string slicing to extract month/year instead of using
.dtaccessor, which is clean, vectorized, and less error-prone.
Variations
- Use
pd.to_datetime()with a customformatstring (e.g.,'%Y/%m/%d') for fast and unambiguous parsing of non-ISO formats. - For timezone-aware series, use
tz_localize()to assign a timezone andtz_convert()to shift between zones. - Generate a regular sequence of dates with
pd.date_range()for resampling onto a fixed calendar grid.
Real-world use cases
- Analyze daily website traffic logs by parsing timestamps to datetime, filtering to business hours, and computing weekly averages for reporting.
- In e-commerce, calculate the time between order creation and shipment by subtracting two datetime columns to find fulfillment delays.
- For IoT sensor data, resample high-frequency readings (e.g., every second) to 5-minute averages to reduce noise and storage size.
Key takeaways
- Convert date strings to
datetimewithpd.to_datetime()— it's the foundation for all time-based analysis. - Use the
.dtaccessor to extract calendar components like year, month, or weekday for grouping and feature engineering. - Set a datetime column as the index to unlock time-series features like
resample()and label-based slicing. - Resample with explicit frequencies (
'MS'for month start,'ME'for month end) to avoid deprecated aliases and confusion. - Always inspect for
NaTvalues after parsing and useerrors='coerce'+format=to handle messy strings. - Mind timezone-awareness: localize or convert to compare timestamps consistently.
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.