Charlie KrugThe Build Log

← All posts

2:30 AM never happened. Your date code returned it anyway.

Chronofuzz runs your JavaScript or Python date function against 20 hand-researched time landmines, from the spring-forward hour that never existed to Feb 29 1900, and shows exactly which one breaks.

On the second Sunday of March in the United States, the wall clock jumps from 1:59 AM straight to 3:00 AM. The hour from 2:00 to 2:59 does not occur. So 2023-03-12T02:30:00 in a US timezone names a moment that never existed, and a correct date function should reject it. Paste new Date("2023-03-12T02:30:00") into a console, though, and JavaScript hands you back a real instant with a straight face. It made one up.

Chronofuzz is built around that class of bug. You paste a JavaScript or Python function that takes an ISO string and a target IANA timezone and returns a normalized instant, and it runs your function against a curated battery of real date/time landmines, then draws a red strike over each one your function gets wrong and shows the wrong value inline. The reason it exists is narrow and, I think, honest: most engineers have never written a DST-fallback test, not because they don't know DST exists, but because building the fixture is its own research project. Which offset? Which zone? Which exact instant? Chronofuzz does that research once and packages it as something you can point a function at in seconds.

Twenty landmines, researched once

The corpus is 20 hand-picked cases, at least three per category, each with a real-world citation for why it matters. A sampling of what's in there:

  • DST: 2:30 AM on a spring-forward Sunday (the hour that never existed), and its evil twin, a fall-back hour that happens twice.
  • Leap day: Feb 29 1900, which was not a leap year, against Feb 29 2000, which was. Years divisible by 100 skip the leap day, unless they're also divisible by 400. 1900 failed that test; 2000 passed it. A correct function validates the day against the year rather than trusting the constructor, and JavaScript's Date silently rolls 1900-02-29 forward to March 1.
  • Epoch boundary: 2038-01-19T03:14:08Z, one second past the point a signed 32-bit Unix timestamp overflows.
  • ISO week: Jan 1 2023 belongs to ISO week 52 of 2022, because ISO weeks follow the Thursday, not the calendar year.
  • Leap second: 1972-06-30T23:59:60Z, the very first leap second ever inserted, with its illegal-looking :60.

None of these are trick questions. They are the exact inputs that quietly corrupt a billing run or a calendar sync in production, collected so you don't have to go dig up the right instant yourself.

The verdict has three values, not two

Here's the design decision I'd carry away even if you never open the app. Every landmine has an automated evaluator that grades your output, and it can return pass, fail, or ambiguous. The third one matters. A DST fall-back hour genuinely maps to two valid instants; 01/02/2024 is either January 2 or February 1 depending on locale, with no single right answer. Grading those pass/fail would be lying. So Chronofuzz marks them ambiguous instead of silently coercing your output into a verdict it can't actually justify. The tool refuses to be more certain than the problem allows, which for a thing about correctness felt like the whole point.

Your pasted code runs in a sandbox with no access to the DOM, the host page, or the network. JavaScript gets a fresh dedicated Web Worker per landmine, so an infinite loop can be killed by terminating that one worker without poisoning the runs after it. Python runs through Pyodide, which is real CPython compiled to WebAssembly, loaded lazily from a CDN only if you actually pick Python. Everything is client-side: nothing you paste ever leaves the tab, and a shareable permalink keeps the whole paste in the URL hash, so even a link stays local.

The name oversells one thing

I'll be straight about the label. "Fuzz" usually means throwing random or mutated input at code to find crashes. Chronofuzz does not do that. It runs a fixed, hand-researched corpus, the same 20 cases every time, no randomness anywhere. It's a curated landmine battery wearing a fuzzer's name, because the interesting date bugs aren't uniformly distributed in input space, they cluster on a few dozen infamous instants, and a curated set finds them faster than a million random dates ever would. Real generative fuzzing over calendar arithmetic is a fine idea for a later version. This one just knows where the mines already are.

Try it

Open Chronofuzz. It loads pre-filled with a naive passthrough sample that trusts new Date and ignores the target zone, so just hit run and watch it go red. The spring-forward row fails because it accepted a time that never existed; the 1900-02-29 row fails on the leap-year rule while 2000-02-29 passes. Expand a failing row to see the actual-vs-expected diff and the real-world context, then swap in your own function and find out how many of the twenty it survives. The whole corpus and every evaluator is on GitHub.

Chronofuzz is live. Free, in your browser, no signup.

This post is part of the build log: every app my automated factory ships gets written up here, honestly. Browse everything at apps.charliekrug.com. Comments are open below.

Comments

Loading comments…