Back to Feed
article 3m read

Daily charts got the viewer's time zone, and one route pasted it into SQL

The daily charts on my usage dashboard were bucketing days on the database's clock. The change that moved them onto the viewer's clock bound the zone safely in one place, spliced it into the query text in another, and left the range edges at UTC midnight.

Article DepthPractice

Flow

What a date range can mean

A description of the two options, not a measurement.

  1. 1Whole local days

    The caller names days on its own clock and the server turns the edges into exact times, so range and bars agree.

  2. 2A stretch of time

    The caller sends two exact times, and the first and last bars are partial and labeled that way.

  3. 3Where this change landed

    Bars use the viewer's clock while the edges stay at UTC midnight, with no label saying so.

Days were being counted on the wrong clock in the daily charts. They asked the database for the date of each stored time without saying which time zone to use, so the database used its own, which is almost always UTC on a server. For anyone west of London, that day ends in the afternoon or evening. Late work showed up on tomorrow's bar. The totals were right and the labels were wrong.

Copying the hourly chart

One hourly chart already sent the browser's time zone with its request. This change shares that lookup, sends the zone with the daily requests too, and converts each time into that zone before taking the date. Do it in that order: convert, then take the date, then group. If you group first, the split at UTC midnight has already happened.

Bound in one place, pasted in another

The daily queries live in two places, and they don't treat the zone the same way.

The shared query helpers require the zone and send it to the database as a separate value, not as part of the SQL text. That's the safe way. A missing zone stops the build, and a strange one is just a value.

The summary route reads the zone from the URL, uses UTC if it's missing, and pastes it into the SQL text of three queries. A browser sends a clean zone name. Anything else can send a quote mark and extra SQL. I haven't tested how far that would get, but a client string inside SQL text is a problem whatever the answer is.

The same commit added a small check for whether a zone name is real. Nothing calls it. Even if something did, I'd still send the zone as a separate value, because the browser's list of zones and the database's list might not match.

What a missing zone does

The helpers refuse to run without a zone. The route falls back to UTC, which brings back the exact bug this change was fixing, without any sign that it happened.

Partial bars at both ends

The labels moved to the viewer's clock, but the start and end of the date range are still at UTC midnight. For someone behind UTC, the first bar is missing part of its day and the last bar takes some hours from the next one. There are no tests in this commit for any of it. A range can honestly mean one of two things: whole local days, with the edges converted on the server, or a stretch of time with partial bars at each end that the chart labels as partial. This change behaves like the second without saying so.

Keep reading

Pick up a connected idea or branch into a nearby one.

5 paths forward