TLDR: When your upstream data is corrupted, stop trying to clean it. Find a reference in your own system that you know is correct, and derive everything from that instead.
The Setup
I've been building a webinar analytics dashboard — a Next.js analytics dashboard that pulls Zoom webinar attendance data and overlays it with Shopify purchase timestamps to show exactly when sales happen during a live webinar.
One of the key charts plots sales dots on a viewer timeline. To place a dot, I need to know: how many minutes into the webinar did this order land?
Simple math. Until it wasn't.
What Broke
Sales dots were disappearing entirely.
Not wrong position — completely absent. I checked the data was coming through. The orders were there. The timeline was there. The dots just… weren't rendering.
The first commit was honest: debug: add sales update diagnostics to trace missing timeline data. I had no idea where they were going. Then a second: debug: add timezone diagnostics to sales update response.
That one told me everything.
The Actual Problem
Here's the thing I hadn't fully reckoned with: our older webinars had webinar.date stored as ET wall-clock time in UTC.
Say the webinar started at 7:00 PM ET. That should be stored as 23:00 UTC. Instead it was stored as 19:00 UTC — the ET hour jammed straight into a UTC field, no conversion. A Zoom parser I'd written months earlier, before I'd built out proper timezone handling.
Shopify orders? new Date() — real UTC, no ambiguity.
So when I computed contentStart = webinar.date + contentStartMin * 60000, I was anchoring to a broken timestamp. The resulting offset was ~240 minutes too high (4-hour EDT gap). The sales dots were being placed way past the end of the visible timeline. Invisible.
What I Tried That Didn't Fix It
My first instinct was to correct the stored dates — audit which webinars were affected, write a migration, re-parse them correctly.
I stopped myself. Older webinars had already been analyzed and shared with stakeholders. Mutating that data felt risky. And more importantly: I realized I'd just be chasing the same problem again anytime there was a parsing inconsistency.
There had to be a smarter anchor.
The Fix That Actually Worked
This is the part I'm genuinely proud of.
The viewer timeline labels — things like "7:15 PM" at minute 15 — those came straight from Zoom's CSV export as human-readable ET wall-clock strings. They were never touched by the broken parser. They were trustworthy.
So instead of deriving contentStart from webinar.date (corrupted), I flipped the logic:
- Find a labeled timeline point — say
"7:15 PM"at minute15 - Parse that label as ET, convert to real UTC via
dateInTimezone() - Subtract:
contentStart = labelUTC - labeledPoint.minute * 60000
You're working backwards from something you trust to get the value you need.
The beauty is it works for both old webinars (ET-as-UTC stored date) and new webinars (correct UTC stored date) — because you're not touching webinar.date at all.
// labeledPoint.label = "7:15 PM", labeledPoint.minute = 15
const labelDateUTC = dateInTimezone(year, month, day, labelHour, labelMin, 0, ZOOM_TIMEZONE)
contentStart = new Date(labelDateUTC.getTime() - Number(labeledPoint.minute) * 60000)
Sales dots reappeared. Right where they should be.
Why This Matters to Me
I've spent way too much energy in the past trying to fix broken upstream data at the source — migrations, re-parses, backfills. Sometimes that's right. But sometimes the data is just done, it's been used, it's been shared.
When that's the case, look for what in your system you actually trust, and measure from there.
The viewer timeline labels weren't the "authoritative" timestamp field. They looked like display metadata. But they were the only thing that hadn't been mangled — so they became the baseline.
Sometimes the trustworthy reference is right in front of you. You just have to stop staring at the broken one.