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.
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' fort1. 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")afterastimezone(PACIFIC)gives '2023-12' fort1, because the trip ended at 23:30 on December 31 for the rider and for the books.
Common follow-up questions
- How would you make this correct year-round instead of a fixed UTC-8? (Swapping the fixed offset for `ZoneInfo("America/Los_Angeles")` so DST transitions are handled.)
- What changes for a city with a half-hour offset, like Mumbai? (A real zone database generalizes; hand-rolled hour math does not.)
- Now roll these labels up into monthly totals in SQL. Where does the conversion go? (Converting with `AT TIME ZONE` before truncating to the month, not after.)