Every few years the calendar quietly breaks a BES pipeline somewhere in Turkey. Not with a crash, not with a 500, not with a paged on-call engineer at 3 AM. It breaks with a participant balance that is legally correct according to the code and legally wrong according to EGM. That gap is the interesting part.
How the failure actually looks
The canonical BES pipeline has three temporal anchors: contribution settlement date (T+2 in most fund structures), NAV pricing day, and the EGM reporting cutoff. All three are computed off a business-day calendar. The calendar itself is usually one of:
- A hardcoded list of
2025_HOLIDAYSin a config file - A database table maintained by whoever remembered to update it last
- A call to a shared internal service that nobody owns anymore
- Turkey locale rules in a date library that has not been updated since installation
All four fail the same way when the government announces a half-day, a bridge day, or a religious holiday adjustment two weeks before it happens. And these announcements happen. Ramazan Bayramı and Kurban Bayramı get official adjustments. Half-days before national holidays get declared by presidential decree. A regular Friday becomes an administrative holiday when it lands between a Thursday holiday and the weekend.
The pipeline runs on Wednesday. It calculates that Friday is a business day. It settles contributions with Friday's NAV. Friday turns out to be a holiday. The NAV that was used is now the wrong NAV. Every participant who contributed that week has a balance calculated against a price that does not exist in the official record.
Why nobody catches it
This is where BES pipelines differ from equity trading systems. In trading, a settlement date mismatch produces an immediate reconciliation break. Somebody sees it that afternoon. In pension pipelines, the wrong NAV produces a mathematically valid balance. It reconciles internally. It reconciles against the custodian if the custodian used the same wrong calendar. It only breaks when EGM's own reporting infrastructure — which uses the actual official calendar — produces a delta against the submitted file.
By that point the participant statements have been generated. Some of them have been mailed. A few have been used to justify partial withdrawals.
The wrong fix
The instinct is to make the calendar more current. Subscribe to a feed. Pull from Diyanet. Add a nightly job that checks the Official Gazette. This is treating the symptom.
The actual problem is that business-day logic sits inside pure functions that assume the calendar is a constant. A function like next_settlement_date(trade_date) has no concept of "the calendar I was using when I computed this may no longer be the calendar of record." It returns a date. The date is used. The date is wrong retroactively and nothing in the system knows.
The event model fix
A holiday calendar is not configuration. Configuration is something you can hot-swap without changing semantics. A holiday calendar change is a semantic change to every unsettled position in the pipeline.
What has actually worked in production:
- Every date computation is stored as a tuple:
(computed_date, calendar_version_id). The calendar itself is versioned like any other domain event, with an effective timestamp and a source reference (Resmi Gazete number, presidential decree, EGM circular). - When a new calendar version arrives, the pipeline emits
CalendarAmendedevents. Downstream services subscribe. Any unsettled contribution, any pending NAV assignment, any EGM submission that has not been acknowledged gets recomputed against the new calendar. The old computation is not overwritten — it becomes a superseded event. - Settlement and NAV pricing become explicit state machines. A contribution is not "settled on 2025-06-13." It is in state
PendingSettlement(target=2025-06-13, calendar=v47)until the settlement date actually passes without a calendar amendment invalidating it. - EGM submissions carry the calendar version they were computed against. If a late holiday amendment changes the effective business days in the reporting period, the submission is regenerated before the acknowledgment deadline, not after.
What this costs
More storage, obviously. More event volume. A meaningful amount of engineering discomfort because most Turkish finance developers were trained to treat dates as scalars. There is real pushback the first time you tell someone that today() is not a safe function to call in a settlement pipeline.
The payoff is that when the government announces on a Tuesday that Friday is now a holiday, the pipeline does not need heroics. It emits an event, recomputes affected positions, and the participant balances reflect the actual official calendar rather than the calendar that existed when the code ran.
The general lesson
Any system operating under a regulator whose temporal rules can change with short notice needs to model time itself as an event stream, not as a property of the wall clock. This applies to BES, to individual retirement products, to the parts of insurance that touch statutory reporting, and to anything that hits EGM, SEDDK, or MASAK deadlines.
The test is simple. Ask the pipeline: what calendar did you use to compute this settlement date, and can you prove it was the calendar of record at the moment of settlement? If the answer is "the one in the config file at runtime," the pipeline is one late Resmi Gazete announcement away from a legally incorrect balance sheet.
Holiday calendars are not configuration. They are temporal facts, and temporal facts belong in the event model.