For the past few months I’ve been leading a date standardization effort across our whole web app, retiring Moment.js and two other date libraries that crept in over the years, and routing every date operation through one internal library with one set of rules for formatting, parsing and time zones. Several hundred files later, I became the person who knows where every date call lives and what it assumes 😅
So when users started reporting that our app wouldn’t load at all, I was one of the first people pulled in, even though the error said nothing about dates, and believe me, that turned out to be the right call.
The reports were strange, not slow, not one broken feature, but a blank error screen before anything rendered, with a message like “Cannot read properties of undefined (reading ‘split’)”. It only happened on Windows, only in Chromium-based browsers, and only for some people, and nothing we deployed touched time zones.
In this article I will tell you what it turned out to be, how we found it, and what we changed so it can’t happen again.
The symptom
The reports had three things in common:
- Windows 11, recently updated.
- Chrome, Edge or our Electron desktop app, while Firefox users on the same machines could still load the app.
- A specific region, western Canada.
That last one was the clue, everything else about those sessions looked normal.
The cause: a time zone the browser couldn’t name
In September and October 2026, Microsoft shipped Windows updates with new time zones for three Canadian provinces, like “(UTC-07:00) Pacific Time (British Columbia)”, next to similar ones for Alberta and Manitoba. The reason is that British Columbia, Alberta and Manitoba are moving to permanent daylight saving time. On November 1, 2026 their clocks won’t fall back, and they will stay on UTC-7 (most of British Columbia), UTC-6 (Alberta) and UTC-5 (Manitoba) all year round.

Devices that set their time zone automatically switched to the new zone on their own, so most affected users didn’t change anything, they just installed a Windows update. That’s why the crash didn’t wait for November 1: the clocks only change then, but the new zone names arrived with the update.
Here is the thing, browsers don’t use Windows time zone names directly, they map them to IANA identifiers like America/Vancouver, the names that Intl and every date library understand. That mapping lives in the ICU data the browser ships with (windowsZones.txt, generated from CLDR), and when Windows introduces a zone that this data has never heard of, the mapping fails.
What happens next depends on the engine:
- Firefox fell back to a fixed-offset zone like
Etc/GMT+8, which is an hour wrong, but still a string. - Chromium, so Chrome, Edge and Electron, returned
undefinedfor the time zone, and its local clock fell back to UTC-8, an hour behind Windows.
And this is the call that returned undefined in Chromium:
Intl.DateTimeFormat().resolvedOptions().timeZoneThe TypeScript types say this property is a string, but it isn’t always one. Someone asked to make it string | undefined back in 2023 (TypeScript issue #55869), and it was closed because the spec says it is always a string, even though people were hitting the same undefined in Chrome at that time. That gap between the types and reality is where the bug started.
✅ Update: the missing mappings were added to CLDR and merged into ICU on October 13, 2026, and here is where each one stands:
- Firefox: fixed in Firefox 159, released on October 27, 2026 (bug 2082211).
- Chromium: fixed on October 23, 2026, and verified in Chrome 156.0.8078.76 on October 27 (issue 570417342).
- Electron: still open at the time of writing (#55137).
Why it took the app down
Having undefined instead of a time zone string shouldn’t crash anything. Ours did because of where the value was used.
We have a small telemetry module that reports how long static assets take to load, and to group those numbers by a rough region without collecting anything sensitive, it took the user’s time zone and kept only the part before the slash, "America/Vancouver" → "America", with code like this:
const region = Intl.DateTimeFormat().resolvedOptions().timeZone.split('/')[0]For years this ran late, after the app had rendered, in a place where a thrown error was swallowed. Then a few weeks earlier, an unrelated and perfectly reasonable change moved the first telemetry flush earlier, into the app’s initialization, so we could capture early-load metrics, and that quietly made telemetry a step the app’s boot depended on.
So the chain was:
- Windows ships a new time zone → Chromium returns
undefinedfor the time zone. - Telemetry calls
.splitonundefined→ throws. - The throw happens inside app initialization → the init promise rejects.
- The top-level error boundary catches it → users see the “initialization failure” screen.
Three things that are harmless on their own: an OS update, a slightly optimistic type, and a telemetry call that moved earlier. Together, they crashed the app for one group of users and nobody else 🤯
Finding it
What made it hard is that nothing in our release lined up with the reports, and we couldn’t reproduce it on our own machines, because none of us had the Windows update with the new zones.
What cracked it was the shape of the error reports: the stack pointed at the telemetry module, the only split call in that module was on a time zone string, and the regions in the reports matched the ones in the Windows release notes. And guess what? I had just spent weeks auditing every place our app reads the user’s time zone, so the pattern looked familiar, it was one of the raw Intl calls still on my migration list, so technically I was a few files away from fixing it before it ever happened 🙈
Once we had that hypothesis, confirming it was easy, we set a Windows VM to the new zone and watched resolvedOptions().timeZone come back undefined in Chrome and as a string in Firefox.
The fix
The immediate fix was small, and we shipped it as a hotfix the same day:
- Treat the time zone as optional everywhere:
timeZone?.split('/')[0] ?? 'unknown'. - Keep telemetry off the critical path: the init-time telemetry flush is now wrapped, so a failure to report metrics is logged to our error tracker and never fails booting the app.
- Guard the same pattern elsewhere: a web worker happened to read the time zone the same way.
Then we audited every resolvedOptions().timeZone call in the codebase. Our shared date library already exposed the time zone through a helper typed as string | undefined, so most callers were fine, the few raw calls moved to it, and the lint rule that already bans direct Intl date formatting in product code now covers them too.
What we took away
Telemetry and analytics must be fail-safe. Anything that only observes the app should never be able to bring it down. Moving the flush earlier wasn’t wrong, but it showed that the telemetry module assumed it ran where throwing didn’t matter, so now “can this throw during boot?” is a review question for anything that touches initialization.
Environment values are inputs, not constants. Time zone, locale, language, screen size, they all come from the user’s machine, and that machine can change under you with an OS update, a policy or a new device, so handle every value from the environment as if it might be missing or unfamiliar, whatever the types say.
Type definitions describe the spec, not reality. resolvedOptions().timeZone: string is what the spec intends and what almost always happens, but browsers diverge from the spec in edge cases, and an undefined from a built-in API is one of the worst things to debug, because nothing in your code looks wrong.
Geographic clustering is a signal. When a bug affects a region rather than a browser version or a feature flag, think about everything that varies by region, time zone, locale data, keyboard layout and date formats. In our case, the region pointed straight at the cause once we looked at it that way.
Keep a short path to production. Our hotfix process let us branch from the exact production build, ship the guard and verify it with affected users within hours, without waiting for the regular release train, and that is worth investing in before you need it.
If you own a web app
Search your codebase for resolvedOptions().timeZone and check what happens when it’s undefined. If the answer is “it throws”, you have the same bug, just waiting for an OS update to trigger it. And if any of that code runs during startup, move it off the critical path or wrap it.
The new Canadian zones were the trigger this time, but time zone databases change several times a year, and browsers and operating systems won’t always agree on the same day.
If you hit something similar, or you have a question, let’s discuss it in the comments below 👇
Resources
- Microsoft: Interim guidance for the B.C. time zone change
- Microsoft: Windows updates for Canada’s 2026 time zones
- B.C. government: B.C.’s new time zone (Pacific time)
- Chromium: Windows time zone detection fails
- Mozilla: New Canadian time zones not mapped
- Electron: Time zone detection returns undefined
- ICU: CLDR 49 with the missing Windows mappings
- TypeScript: timeZone is possibly undefined
- MDN:
resolvedOptions()
Tot ziens 👋