Formatting and Parsing
Date and Time in Practice
Chapter 2 ยท Formatting and Parsing
Date and Time in Practice 1 introduced strftime and strptime. This chapter goes deeper: the format codes you will actually use, the ways date text can be ambiguous, the stricter and safer fromisoformat, and a pattern for handling input that does not parse. The examples use Python 3.14, and every snippet was run; the output shown in the comments is what Python printed.
The Format Codes Worth Knowing
All the examples below format the same moment, datetime(2026, 3, 5, 9, 7, 4, 123456), which is a Thursday.
| Code | Meaning | Output |
|---|---|---|
%y | 2-digit year | 26 |
%a %b | short weekday; short month | Thu Mar |
%I %p | hour 01-12; AM or PM | 09 AM |
%f | microseconds, 6 digits | 123456 |
%j | day of the year, 001-366 | 064 |
%w %u | weekday number: Sunday is 0; Monday is 1 | 4 4 |
%V %G | ISO week number; ISO year of that week | 10 2026 |
%% | a literal percent sign | % |
%A, %B, %a, %b and %p produce names from the machine's current locale. The output above is English because the program ran in an English locale; on a machine set to another language the same code gives different words, and text in that language will not parse on an English machine. Names are fine for display, but do not use them in stored or exchanged data.
Parsing with strptime
The format must describe the text. Names, 12-hour clocks and two-digit years all parse, but each has a catch worth knowing.
Three things are visible there. Fields missing from the format are filled with defaults, so a time with no date becomes 1 January 1900, a placeholder you should ignore (take .time() from it). Text with no time of day gives midnight. And a two-digit year has to be guessed:
Python maps 69 to 99 onto the 1900s and 00 to 68 onto the 2000s. A birth year written 68 becomes 2068, which is in the future. Prefer four-digit years whenever you have a choice.
Ambiguous Text
Parsing succeeds whenever the text fits the format, even if the format is the wrong one. The same text can be valid under two formats and mean two different days:
Nothing in the code can tell you which was meant. Only the context can: where the data came from, or whether another date in the same file has a first number above 12. When you must accept day-first or month-first text, make the choice explicit and documented, or ask the person. When you can choose, switch to ISO YYYY-MM-DD, where the order is fixed.
| Text | Day first | Month first | Verdict |
|---|---|---|---|
03/04/2026 | 3 April | 4 March | ambiguous |
13/04/2026 | 13 April | invalid | only one reading works |
2026-04-03 | always year, month, day | unambiguous | |
Strict and Lenient Parsing
strptime is stricter than you might expect about extra text, and more relaxed than you might expect about padding:
fromisoformat: the Safe Default
When the text is ISO 8601, use fromisoformat. It needs no format string, so there is nothing to get wrong. From Python 3.11 it accepts most ISO forms, not only the one isoformat() writes. Note the difference between date and datetime:
A date-only function rejects text that carries a time, so use datetime.fromisoformat(s).date() if the input might have either. The ISO week form 2026-W10-4 (year, week 10, day 4) is understood too. Versions of Python before 3.11 were stricter, so check your version if a form that works here fails elsewhere.
Handling Bad Input
Anything a person typed, or another system sent, can fail to parse. The pattern is to try, catch ValueError, and decide what failure means for your program: reject, ask again, or try another format. Do not use a bare except, which would hide real bugs.
The list of formats is a deliberate choice: 2/11/2026 is rejected because no listed format is day-first with slashes. Accepting fewer formats is safer than guessing. Order matters when two formats could fit the same text, since the first match wins.
fromisoformat when the input is ISO, and with an explicit, short list of formats otherwise. Treat a parse failure as normal data, not as a surprise.
Challenges
Write readings(text) that tries the text as both day-first (%d/%m/%Y) and month-first (%m/%d/%Y) and returns the sorted list of distinct dates that are valid. Then print, for each of 03/04/2026, 13/04/2026, 05/05/2026 and 31/02/2026, the dates in ISO form and a verdict of ambiguous, clear or invalid. Decide what you expect for 05/05/2026 before running it.
Write to_iso(text) that converts text such as 05 Mar 2026 09:07 PM into ISO form with minutes precision (2026-03-05T21:07), and returns "invalid: " followed by the text if it does not parse. Test it with that example, with 12:00 AM and 12:00 PM on the same day, with 13:00 PM, and with 31 Apr 2026 01:00 AM. Predict what the two twelve o'clock cases give.
Using datetime(2026, 3, 5, 9, 7, 4), build a single line of text in the form Thu 05 Mar 2026, week 10, day 064 with one strftime call. Then check your answer by parsing the date part back with strptime and confirming it equals the original date. Hint: one code gives the ISO week and one gives the day of the year; a comma is an ordinary character.
Quick Reference
- Common codes:
%yshort year,%a %bshort names,%I %p12-hour clock,%fmicroseconds,%jday of year,%VISO week - Names and AM/PM follow the locale: display only, never store
- Missing fields default (time of day to midnight, a bare time to 1 January 1900)
- Two-digit years: 69-99 become 19xx, 00-68 become 20xx; prefer four digits
- Ambiguity:
1/2/26fits both day-first and month-first; only context decides - strptime errors: leftover text and impossible dates raise
ValueError; strip spaces first - ISO input:
fromisoformat; usedatetime.fromisoformat(s).date()if a time may be present