Fix the timezone shift in monthly active users
Your task
An AI wrote your monthly active users query, and finance says March is double counting compared with their dashboard. The query bucketed UTC timestamps by month, but finance reports in a local calendar, and only logins and purchases count as activity. You write the metric out with both rules made explicit.
An assistant wrote monthly_active_users(event_rows, qualifying_types, tz_offset_hours). It is in the editor. It has a bug. Find it and fix it.
Input and output:
event_rows: one event per row, writtenuser_id|timestamp|event_type. The timestamp is UTC, writtenYYYY-MM-DDTHH, such as2026-03-01T02. Years are from 1001 to 9998.qualifying_types: the event types that count as activity.tz_offset_hours: the reporting calendar's offset from UTC in whole hours, such as -5.- Return one
YYYY-MM=countstring per month, sorted by month.
Rules the tests check:
- If
qualifying_typesis empty, ortz_offset_hoursis below -12 or above 14, return[]. - Split each row on
|. Skip it unless it has exactly 3 parts, a user id that is not empty, and an event type that exactly equals one ofqualifying_types. - Skip a row whose timestamp is not exactly 13 characters in the form
YYYY-MM-DDTHHwith digits from 0 to 9. Also skip it when the month is not 01 to 12, the day does not exist in that month, or the hour is above 23. February has 29 days in leap years: years divisible by 4, except years divisible by 100 that are not divisible by 400. - Move the event into the reporting calendar: add
tz_offset_hoursto the hour. If the result is below 0, the event falls on the previous day. If it is 24 or more, it falls on the next day. The event's month is the month of that local day. - Count each user once per month. Write each month as
YYYY-MM, such as2026-02.
Examples
monthly_active_users(event_rows: list[str], qualifying_types: list[str], tz_offset_hours: int) -> list[str]Example 1
Input
monthly_active_users(["u1|2026-03-01T02|login", "u1|2026-03-01T14|login", "u2|2026-03-15T09|purchase", "u2|2026-03-16T09|heartbeat"], ["login", "purchase"], -5)Output
["2026-02=1", "2026-03=2"]Example 2
Input
monthly_active_users(["u1|2026-04-01T12|login", "u1|2026-04-02T12|login", "u1|2026-04-03T12|purchase"], ["login", "purchase"], 0)Output
["2026-04=1"]Example 3
Input
monthly_active_users(["u3|2026-02-28T20|login"], ["login"], 5)Output
["2026-03=1"]