Skip to content

The event_date Column That Was Always NULL

Adityo Guni Waluyo

Indonesian month-name dates kept an event_date column empty. The stdlib locale is not the answer; an explicit month table is.

TL;DR

Date-range queries kept returning empty because Indonesian date strings silently failed ISO parsing. Locale-based fixes proved fragile, so the team chose a simple 13-entry month table with one shared field list in the domain layer. Fifty-three tests plus a live remap of 20 cases filled every date with zero errors.

The date-range query that always came back empty

The event_date column existed with the right type, yet every date-range query on DemandScope returned nothing. A quick look at the raw data showed why: the harvest feed ships dates as Indonesian text like Senin, 08 Jun. 2026, while the mapper only tried date.fromisoformat, which throws a ValueError about an invalid ISO string for that text. The exception was caught, the result was None, and the column stayed empty. Twenty cases in, twenty dates gone without a single error.

The most troubling part of a bug like this is not the failure but how polite it is. No red logs, no failed retries. A NULL value looks like data that simply has not arrived yet.

First guess: lean on the locale

My first assumption was simple: Python has strptime with %b for abbreviated month names, so switching the process locale to Indonesian should be enough. That assumption collapsed at three separate points. The locale module is documented as opening access to the POSIX locale database, and setlocale is not thread-safe because it changes global process state [6]. Month names in the calendar module follow the current locale rather than a fixed list [7]. The stdlib locale machinery itself has a long record: a strptime failure against strftime in several locales has been open since 2010 [8], and a fix for month names containing the character I-dot in certain locales only landed in 2025 [9].

A live probe confirmed it. On this host, strptime with the format %d %b %Y rejects the text 08 Jun. 2026, and that is before touching hosts running non-English locales. Switching the process language for the sake of one parser drags the whole application into fragile global state.

A 13-entry month table and one source of truth

The fix that was chosen is the least sophisticated one: a parse_sipp_date function backed by a 13-entry table of Indonesian month names, including a double mapping of agu and ags to month 8. The _SIPP_DATE regex ignores the weekday prefix and captures the day-month-year pattern, so abbreviations and full names both pass. The event_date_for function orders the sources: verdict fields first, registration_date as the fallback, and a shortcut for text that is already ISO.

The field list matters more than the parser. The EVENT_DATE_FIELDS tuple now lives in the domain layer as the single source of truth, and the mapper imports it for two purposes at once: deciding which fields get parsed and which fields are stripped from details. Previously there were two separate tuples with the same content, and either could drift silently. A failed parse is not discarded either: the raw text stays in details while the target column honestly holds NULL, its original evidence intact.

Proof on a small production set

The verification is layered and repeatable. Fifty-three tests green, four of them new for this parser. A live remap of 20 old cases through mapper_version:3 filled 20 out of 20 event_date values, date-range queries became useful immediately, and re-shipping the same 20 items twenty times in steady state produced zero changes. The next parser that meets a local date format will take the same road: an explicit table in the domain, not a process locale that can change without anyone noticing.

Sources

  1. Python Documentation: locale (POSIX locale database, setlocale thread-safety)
  2. Python Documentation: calendar (month_name follows the current locale)
  3. CPython issue gh-53203: strptime %c fails in some locales (open since 2010)
  4. CPython issue gh-136028: strptime month names with U+0130 (fixed 2025)

Related articles