Unit 9 • Lesson 1

Parsing Dates

Overview

A date read from a file is text until you say otherwise.

Intermediate 20–25 min

What You Will Learn in This Lesson

By the end of this lesson, you will be able to:

  • Convert a column of date text into real datetimes.
  • Parse at load time rather than afterwards.
  • Handle a value that will not parse.
  • Say why day-first and month-first matter.

Why This Matters

Text that looks like a date sorts alphabetically and compares alphabetically, so "02/2024" comes before "1/2023" and every ordering you build on it is wrong. Worse, an ambiguous format can be parsed the wrong way round and still succeed, giving you a year of data where every day up to the twelfth is a different date than you think.

Step 1: to_datetime turns text into dates

Until it does, sorting is alphabetical and "greater than" means something other than "later".

1

What text does to order

Run this once. The text ordering is not an approximation of the date ordering; it is unrelated to it.

import pandas as pd

text = pd.Series(["10/01/2024", "02/01/2024", "1/03/2023"])
print(sorted(text.tolist()))
print(pd.to_datetime(text, dayfirst=True).sort_values().dt.strftime("%Y-%m-%d").tolist())
2

The conversion

Once converted, the column has a datetime dtype and every comparison means what you expect.

import pandas as pd

days = pd.to_datetime(pd.Series(["2024-03-01", "2024-01-15"]))
print(days.dtype)
print(days.min(), days.max())
print(days.max() - days.min())

Mini Practice #1: Text order against date order

Try It Yourself

Sort these as text and as dates, and compare the two answers.

Press Run to see output

Step 2: Parsing on the way in

parse_dates on read_csv does it at load time, which is one fewer step to forget.

1

At load time

Cheaper than converting afterwards, and it means the column is never text long enough for you to sort it by accident.

import pandas as pd
from io import StringIO

text = "day,n\n2024-01-05,1\n2024-02-01,2\n"
df = pd.read_csv(StringIO(text), parse_dates=["day"])
print(df.dtypes)
2

When a value will not parse

errors="coerce" gives NaT — the datetime version of NaN — instead of raising. Counting them tells you how bad the column is before you decide what to do.

import pandas as pd

messy = pd.Series(["2024-01-05", "not a date", "2024-02-01"])
days = pd.to_datetime(messy, errors="coerce")
print(days.tolist())
print("unparseable:", int(days.isna().sum()))
3

The ambiguity that does not raise

03/04/2024 is the third of April or the fourth of March, and both readings succeed. If the file is British, say so — otherwise every date whose day is 12 or under is silently wrong.

import pandas as pd

value = pd.Series(["03/04/2024"])
print(pd.to_datetime(value, dayfirst=False).dt.strftime("%d %B %Y").iloc[0])
print(pd.to_datetime(value, dayfirst=True).dt.strftime("%d %B %Y").iloc[0])

Mini Practice #2: Count what will not parse

Try It Yourself

Coerce this column and report how many values failed, before deciding anything.

Press Run to see output

Step 3: When to Use It

Any date column from a file

to_datetime turns text into real dates that sort, compare and subtract correctly.

df["date"] = pd.to_datetime(df["date"])

Known formats

Pass format= when you know it; it is faster and refuses dates that do not fit.

When Not to Use It

Do not let an ambiguous date like 03/04/2024 be guessed. Say the format or dayfirst=True, or March and April swap silently.

End-of-Lesson Exercises

Exercise 1: The earliest date

Write earliest(dates) that takes a Series of date strings and returns the earliest as a string in YYYY-MM-DD form.

Parse first, then take the minimum, then format it back. On ISO-formatted text the alphabetical answer happens to agree, which is exactly why the habit matters when the format changes.

Press Run to try it, then Check my work to mark it.

Exercise 2: How many dates are usable

Write usable_dates(values) that takes a Series of date strings and returns a two-element list of [how many parsed, how many did not]. Report the bad ones rather than dropping them quietly.

to_datetime with errors="coerce" turns the failures into NaT instead of raising. notna() counts the good ones and isna() the bad.

Press Run to try it, then Check my work to mark it.