Writing a Reliable Regex for Extracting Delivery Times
TIL that a regex tuned against a handful of examples ('DELIVERY TIME: 2:00 PM') broke on real data within a day because of extra whitespace and inconsistent AM/PM casing I hadn't accounted for.
The Problem #
Extracting a delivery time from a string like DELIVERY TIME: 2:00 PM seemed simple enough for a quick regex, and the first version worked against every example I'd copied from the POS while writing it — and failed on real orders within the first day.
Context #
The label text came from rendered DOM text content, which meant it carried whatever whitespace, casing, and formatting quirks the POS's own templating happened to produce — not the clean, single-spaced strings I'd been testing against.
What I Tried #
const match = text.match(/DELIVERY TIME: (\d{1,2}:\d{2} [AP]M)/);What Went Wrong #
Real extracted text had inconsistent whitespace (DELIVERY TIME: 2:00PM, no space before AM/PM), and some orders had lowercase am/pm. Each of these silently failed to match, which meant match was null and the delivery time quietly went missing for that order rather than throwing anything I'd notice immediately.
The Solution #
Made the whitespace and casing assumptions explicit instead of implicit, and normalized the input before matching rather than trying to make one regex handle every variant.
const normalized = text.trim().replace(/\s+/g, " ");
const match = normalized.match(/delivery time:\s*(\d{1,2}:\d{2}\s?[ap]m)/i);
if (!match) {
console.error("[extension] Could not parse delivery time from:", text);
}Why It Works #
Normalizing whitespace before matching removes an entire category of "regex almost matches" failures, and the i flag removes the casing assumption instead of trying to encode every casing variant into the pattern itself. Logging the raw text on a failed match also means the next format surprise is visible immediately instead of silently dropped.
Lessons Learned #
A regex that matches every example I hand-picked while writing it is not the same thing as a regex that matches real data — real strings carry formatting noise that curated test cases don't, and that gap is exactly where these bugs live.
What I Would Do Differently #
I'd normalize input and log unmatched cases from the first version, instead of tuning the pattern against a small set of examples and discovering the gap once real orders started failing silently.
Related Concepts #
Regex normalization before matching, case-insensitive matching, silent-failure vs. loud-failure parsing.
Related content
Extracting Delivery Information from Dynamically Rendered HTML
TIL that a content script querying the DOM immediately on page load found nothing, because the POS's order details render client-side, after the content script's own 'document_idle' point had already passed.
Injecting Custom UI Into a Website You Don't Own
TIL that a naive injected panel kept getting wiped out by the host page's own re-renders — the fix was watching for that, not fighting it, and mounting into a container the host page has no reason to touch.
Reading Data from an Existing Web App with a Chrome Content Script
TIL that a content script reading order data straight from the DOM breaks every time the POS ships a markup change — the fix was reading from the most stable layer available, not the most convenient one.