You can parse timestamps with regex now. You pull 2026-03-31T14:22:05Z out of a raw log line reliably. But I want you to tell me — what does that string let you do that you couldn't do with the raw text?
I can sort them. Alphabetically, which happens to work for ISO format. I can check if two strings are equal. I can print them. But I can't do anything like... "give me all log entries from the last four hours". That requires actual subtraction. I can't subtract strings.
That is the exact wall this week knocks down. datetime.fromisoformat('2026-03-31T14:22:05Z') gives you a datetime object. You can subtract two datetime objects and get a timedelta. You can compare a datetime to another datetime. You can add four hours to right now and use that as a filter cutoff. The log entries you were treating as strings become moments in time you can do arithmetic on.
Wait, and the ops team has logs in three different timestamp formats because three different services were configured by three different people at three different times. So I'd need to parse all of them into datetime objects first, and then everything is comparable regardless of the original format?
That is precisely what datetime.strptime() is for. You give it the string and the format, it gives you a datetime. Then strftime() converts it back to any output format you choose. Normalize on parse, output in whatever the report needs. The format codes are an acquired taste — %Y versus %y, %H versus %I — but you learn the eight that matter and the rest are reference material.
strptime and strftime. Who named these?
POSIX, in 1988. You get used to it. The mnemonic: p for parse, f for format. Week 3 also covers math, statistics, random, and collections — the Counter and defaultdict you've been building manually for the last two weeks exist as ready-made tools. We will end the week with you able to count error types across a full day's log file in one line.
The datetime module is deceptively large. On the surface it gives you datetime.now() and datetime.fromisoformat(). Below the surface it contains timedelta for duration arithmetic, timezone for UTC-aware timestamps, and the full strptime/strftime formatting system inherited from C. The important mental model shift is from timestamp-as-string to timestamp-as-object: once parsed, a datetime object participates in comparison operators and arithmetic just like a number.
Timezone awareness is the trap. Naive datetime objects — those without timezone info — cannot be compared to aware objects. The ops team's logs likely mix both, because different services were configured at different times by people with different opinions about whether timestamps should be UTC. The datetime.timezone.utc constant and datetime.astimezone() are the tools for normalizing.
math and statistics cover the numeric ground beyond basic arithmetic: square roots, logarithms, means, medians, standard deviations. statistics.stdev() over a list of response times gives you the spread in one call. math.log10() lets you build logarithmic buckets for latency distribution.
collections is the week's highest-leverage module. Counter is a dict subclass that counts hashable objects — pass it any iterable and it produces a frequency map. most_common(n) returns the top n items sorted by count. defaultdict eliminates the if key not in d: d[key] = [] pattern that appears constantly in grouping code. deque is a double-ended queue with O(1) append and pop from both ends — the right structure for a rolling window over a stream of log events. These are not exotic data structures; they are the containers Python programmers reach for when plain dicts and lists create unnecessary boilerplate.
Sign up to save your notes.
You can parse timestamps with regex now. You pull 2026-03-31T14:22:05Z out of a raw log line reliably. But I want you to tell me — what does that string let you do that you couldn't do with the raw text?
I can sort them. Alphabetically, which happens to work for ISO format. I can check if two strings are equal. I can print them. But I can't do anything like... "give me all log entries from the last four hours". That requires actual subtraction. I can't subtract strings.
That is the exact wall this week knocks down. datetime.fromisoformat('2026-03-31T14:22:05Z') gives you a datetime object. You can subtract two datetime objects and get a timedelta. You can compare a datetime to another datetime. You can add four hours to right now and use that as a filter cutoff. The log entries you were treating as strings become moments in time you can do arithmetic on.
Wait, and the ops team has logs in three different timestamp formats because three different services were configured by three different people at three different times. So I'd need to parse all of them into datetime objects first, and then everything is comparable regardless of the original format?
That is precisely what datetime.strptime() is for. You give it the string and the format, it gives you a datetime. Then strftime() converts it back to any output format you choose. Normalize on parse, output in whatever the report needs. The format codes are an acquired taste — %Y versus %y, %H versus %I — but you learn the eight that matter and the rest are reference material.
strptime and strftime. Who named these?
POSIX, in 1988. You get used to it. The mnemonic: p for parse, f for format. Week 3 also covers math, statistics, random, and collections — the Counter and defaultdict you've been building manually for the last two weeks exist as ready-made tools. We will end the week with you able to count error types across a full day's log file in one line.
The datetime module is deceptively large. On the surface it gives you datetime.now() and datetime.fromisoformat(). Below the surface it contains timedelta for duration arithmetic, timezone for UTC-aware timestamps, and the full strptime/strftime formatting system inherited from C. The important mental model shift is from timestamp-as-string to timestamp-as-object: once parsed, a datetime object participates in comparison operators and arithmetic just like a number.
Timezone awareness is the trap. Naive datetime objects — those without timezone info — cannot be compared to aware objects. The ops team's logs likely mix both, because different services were configured at different times by people with different opinions about whether timestamps should be UTC. The datetime.timezone.utc constant and datetime.astimezone() are the tools for normalizing.
math and statistics cover the numeric ground beyond basic arithmetic: square roots, logarithms, means, medians, standard deviations. statistics.stdev() over a list of response times gives you the spread in one call. math.log10() lets you build logarithmic buckets for latency distribution.
collections is the week's highest-leverage module. Counter is a dict subclass that counts hashable objects — pass it any iterable and it produces a frequency map. most_common(n) returns the top n items sorted by count. defaultdict eliminates the if key not in d: d[key] = [] pattern that appears constantly in grouping code. deque is a double-ended queue with O(1) append and pop from both ends — the right structure for a rolling window over a stream of log events. These are not exotic data structures; they are the containers Python programmers reach for when plain dicts and lists create unnecessary boilerplate.