How do I convert text to dates in pandas and DuckDB?
Tell pandas and DuckDB exactly how to read your dates, and decide what to do with the ones that can't be read.
Free course. Your Maestro, an AI, .
A format is an instruction
You already know ventas_exportadas puts the day first. Now tell the program. A format describes the text piece by piece, with codes that start with %:
| Code | What it reads | Example |
|---|---|---|
%d | day of the month, two digits | 05 |
%m | month, two digits | 02 |
%Y | year, four digits | 2026 |
%H | hour from 00 to 23 | 20 |
%M | minute | 44 |
%S | second | 40 |
Anything that isn't a code is matched as is. 05/02/2026 is described as %d/%m/%Y: day, a slash, month, a slash, year. pandas and DuckDB use the same codes for these pieces.
pd.to_datetime takes the format in format:
data["fecha"] = pd.to_datetime(data["fecha"], format="%d/%m/%Y")The column goes from str to datetime64.
strptime takes the text and the format, and returns a TIMESTAMP. Since there's no time here, turn it into a DATE with ::DATE:
SELECT strptime(fecha, '%d/%m/%Y')::DATE AS fechaFROM ventas_exportadas;Cleaning up the fecha column in ventas_exportadas
Worked example · 5 steps
Convert the fecha column from text to a date and check that the conversion came out right.
Work out the next step yourself, then open it to compare.
What doesn't fit the format
A real file almost always has some value that doesn't fit: a day that doesn't exist, a date written another way, an empty cell. With pd.to_datetime and an explicit format, a nonempty incompatible value stops conversion with ValueError. Missing values ("", None) become NaT even without coerce. Sometimes that's what you want. Other times you'd rather convert what you can and look at the rest separately. Predict what errors="coerce" does.
Five strings and an explicit format. Write what you think it prints and why.
Five values, one of each kind
import pandas as pdfechas = pd.Series(["28/02/2026", "31/02/2026", "1/3/2026", "2026-03-05", ""])print(pd.to_datetime(fechas, format="%d/%m/%Y", errors="coerce"))A guess is fine. What you write stays in this browser and waits in your course.
NaT means there is no date: it may be an original absence or a value that could not be converted. Keep the original column, count original absences and new failures separately, and inspect them before moving on; a Glossary term of those rows tells you whether it's an empty cell or a different format you need to convert separately. Not sure what to do with your rows without a date? Ask your Maestro.
DuckDB gives you both options as two functions: strptime stops with an error at the first value that doesn't fit (with 31/02/2026 it says Date out of range), and try_strptime returns NULL instead. An original NULL remains NULL. If you use read_csv(parse_dates=...), check the type afterward: an incompatible value can leave the entire column as text without raising that error; it does not behave the same way as to_datetime.
Warning: dayfirst is not a format
pandas also accepts dayfirst=True. The pandas documentation warns that it isn't strict: it prefers to read the day first, but doesn't guarantee it. An explicit format isn't a preference, it's an instruction.
Try it
Now with less help. The query adds up sales by day, but it groups and sorts by the text column. Change it so it converts fecha with its format, calls the result dia, and sorts by the real date.
A good answer has 22 rows, one for each day with sales, from 2026-01-02 to 2026-03-28 in order. If you converted the date and the days are still out of order, tell your Maestro what you put in GROUP BY and ORDER BY.
Your turn
Add up total sales by day, with the converted date (call it dia) in chronological order.
Tables: ventas_exportadas (folio, fecha, sucursal, total)
ventas_exportadas · 29 rows
| Column | Type |
|---|---|
| folio Número de ticket del punto de venta. | BIGINT |
| fecha La fecha tal como la exporta el punto de venta: texto en formato día/mes/año (dd/mm/aaaa). | VARCHAR |
| sucursal Roma, Coyoacán o Del Valle. | VARCHAR |
| total Total del ticket en pesos. | BIGINT |
First rows
| folio | fecha | sucursal | total |
|---|---|---|---|
| 1001 | 02/01/2026 | Roma | 460 |
| 1002 | 05/01/2026 | Roma | 120 |
| 1003 | 05/01/2026 | Del Valle | 120 |
Tab indents; Esc then Tab leaves
Try it on your own
Now in pandas, from scratch: convert the fecha column in ventas_exportadas, then print the earliest date, the latest date, and how many rows ended up without a date. A good answer prints January 2 and March 28, 2026, and 0 rows without a date.
Then try a small column containing 28/02/2026, an empty string and 31/02/2026. Preserve the original, identify absences before conversion, use coerce and count only new failures. Distinguish one original absence from one incompatible value, and show 31/02/2026 as the text that failed.
Your turn
Convert the fecha column with its explicit format. Print the earliest date, the latest date, and how many rows ended up without a date. Then, using the three-value sample in the instructions, print original absences and conversion failures separately, and show the original that failed.
Data: ventas-exportadas, 29 rows
| Row | Remove | ||||
|---|---|---|---|---|---|
| 1 | |||||
| 2 | |||||
| 3 | |||||
| 4 | |||||
| 5 | |||||
| 6 | |||||
| 7 | |||||
| 8 | |||||
| 9 | |||||
| 10 | |||||
| 11 | |||||
| 12 | |||||
| 13 | |||||
| 14 | |||||
| 15 | |||||
| 16 | |||||
| 17 | |||||
| 18 | |||||
| 19 | |||||
| 20 |
Enter saves; Esc undoes; empty is a missing value
Tab indents; Esc then Tab leaves
Think of a file with dates that you use at work. What format do the dates come in, and how would you check that against the data before converting them?
Reflect: Think of a file with dates that you use at work. What format do the dates come in, and how would you check that against the data before converting them?
Only you and your Maestro read this. It's kept on your account and deleted with it.