The Timezone Trap

A medium Python interview practice problem on DataDriven. Write the Python and run it against test cases, with instant feedback.

Domain
Python
Difficulty
medium
Seniority
L4
Asked in
Uber

Problem

Return the billing month, as YYYY-MM, of every completed San Francisco trip in trips, keyed by its trip_id, where we bill by the calendar month on a Pacific clock even though completed_utc is recorded in UTC. For this report we treat Pacific as a fixed UTC-8 all year and ignore daylight saving.

Trip data and timezones. They're not the same thing.

Example

Input:

trips = [{"city":"San Francisco","status":"completed","trip_id":"t1","completed_utc":"2024-01-01T07:30:00Z"},{"city":"San Francisco","status":"completed","trip_id":"t2","completed_utc":"2024-01-15T20:00:00Z"},{"city":"San Francisco","status":"cancelled","trip_id":"t3","completed_utc":"2024-01-15T20:00:00Z"},{"city":"Los Angeles","status":"completed","trip_id":"t4","completed_utc":"2024-01-15T20:00:00Z"}]

Output:

{"t1":"2023-12","t2":"2024-01"}

Worked solution and explanation

What this really is

This is a calendar-boundary problem dressed up as a reporting lookup. The filter is a warm-up. The skill being probed is whether you place an instant on the business's clock before you ask which month it belongs to. Anyone can slice completed_utc[:7]. The trap is that the slice is right for sixteen hours of every day and wrong for the other eight, so it passes a casual glance. Get it wrong and every Pacific evening at the end of a month is billed to the next one, including New Year's Eve trips landing in the wrong year.

Step 1: Keep only completed San Francisco trips

Read city and status with trip.get(...) and skip anything that is not 'San Francisco' and 'completed'. A record missing either field simply does not qualify instead of raising. Run the cheap checks first so parsing only happens on rows that matter.

Step 2: Parse completed_utc as an aware UTC instant

The trailing 'Z' means UTC. Anchor it explicitly so the datetime knows its offset. A naive datetime silently borrows whatever the machine's local zone happens to be.

Step 3: Move the instant onto the Pacific clock

Convert with astimezone to a fixed timezone(timedelta(hours=-8)). Same instant, new wall clock: 07:30 UTC on January 1 becomes 23:30 on December 31.

Step 4: Read the month off the local clock

Only now format with strftime("%Y-%m") and pair it with the trip's trip_id. Collect the pairs and hand them to dict(...) at the end. The order of steps 3 and 4 is the whole problem.

Convert first, then read the month
from datetime import datetime, timedelta, timezone

PACIFIC = timezone(timedelta(hours=-8))  # fixed offset: DST ignored by design


def sf_trip_months(trips: list[dict]) -> dict[str, str]:
    billed = []
    for trip in trips:
        if trip.get("city") != "San Francisco" or trip.get("status") != "completed":
            continue
        completed = datetime.fromisoformat(trip["completed_utc"].replace("Z", "+00:00"))
        local = completed.astimezone(PACIFIC)
        billed.append((trip["trip_id"], local.strftime("%Y-%m")))
    return dict(billed)
Month from the UTC string
trip["completed_utc"][:7] gives '2024-01' for t1. It is right whenever the trip finished after 08:00 UTC, which is why it survives a quick test.
Month from the Pacific clock
local.strftime("%Y-%m") after astimezone(PACIFIC) gives '2023-12' for t1, because the trip ended at 23:30 on December 31 for the rider and for the books.

Common follow-up questions