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

Sponsored

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 into datetime objects.
  • 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 datetime unless you truly need only the date (e.g., datetime.date). The full datetime type 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:

  1. Parse — Convert your string column to datetime using pd.to_datetime(). Specify the format if your strings aren't ISO-8601 (e.g., '2024-03-15').
  2. Inspect — Verify the conversion succeeded by checking the dtype (should be datetime64[ns] or similar) and looking for NaT (Not a Time) values from parsing failures.
  3. Extract components — Use the .dt accessor to get year, month, day, weekday, hour, etc. This is essential for grouping, aggregating, and feature engineering.
  4. Filter — Use boolean conditions with datetime comparisons or the between method to select rows within a date range.
  5. Set index — Set the datetime column as the index to unlock time-series features like resample() and time-based slicing with df.loc['2024'].
  6. Resample and aggregate — Group data by calendar periods (e.g., daily, monthly) and compute summaries like mean, sum, or count.
  7. Compute durations — Subtract two datetime columns to get Timedelta values, 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 using pd.to_datetime(..., errors='coerce') to get NaT for out-of-range values.
  • NaT appearing unexpectedly — Some string formats aren't recognized (e.g., '2024/03/15' vs '2024-03-15'). Use the format parameter: 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: pass format=... or use errors='coerce' and then investigate the resulting NaT rows.
  • 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 use pd.to_datetime(..., utc=True).
  • Performance on large data — Parsing a million strings can be slow. Specify format when you know it, and consider using errors='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 for NaT values and specify format if 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 .dt accessor, which is clean, vectorized, and less error-prone.

Variations

  1. Use pd.to_datetime() with a custom format string (e.g., '%Y/%m/%d') for fast and unambiguous parsing of non-ISO formats.
  2. For timezone-aware series, use tz_localize() to assign a timezone and tz_convert() to shift between zones.
  3. 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 datetime with pd.to_datetime() — it's the foundation for all time-based analysis.
  • Use the .dt accessor 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 NaT values after parsing and use errors='coerce' + format= to handle messy strings.
  • Mind timezone-awareness: localize or convert to compare timestamps consistently.

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.