Skip to content
AFM Studio
Personal ProjectCalendar ToolWeb App

Tanggal Merah

An Indonesian holiday planner that shows the year's public holidays and works out exactly where to spend annual leave for the longest stretches off — while making explicit the rule most people get wrong: whether cuti bersama is charged against your leave

All projects4 min read

Role

Solo Developer

Timeframe

Aug 2026

On this page

The problem

Indonesia publishes its public holidays each year via an SKB — a joint decree of three ministers — that sets two different kinds of day off: libur nasional (national holidays, free) and cuti bersama (collective leave days that are charged against your 12-day annual leave entitlement — but only for some people).

The rule branches by employment status, and published reporting on it is contradictory: at least one outlet reversed the assignment entirely. For 2026 there are 8 cuti bersama days against a 12-day entitlement, so a private-sector worker at a company that closes and deducts is left with 4 days, while a colleague at a company that doesn't close keeps all 12. Most people discover this in November when they try to book a trip — and every other holiday calendar treats these as one undifferentiated "holiday" and never tells you what the days cost you. The second problem it solves: given a leave budget, which specific days do you buy? A single workday between a holiday and a weekend converts one leave day into a four-day stretch, and working that out by eye across twelve months is tedious and error-prone.

The approach

Holidays are decreed, not computed

Idul Fitri, Nyepi, Waisak, and Imlek all have astronomical or calendrical determinations — but the official holiday is whatever the government's decree says, and the decree itself defers the Islamic dates to a separate Ministry of Religion ruling. So the app ships transcribed decree data per year and computes no religious date anywhere. A year with no published decree returns a structured refusal naming the gap, never an extrapolated calendar — because being confidently wrong about whether someone has a day off is the only failure mode that really matters here. Every holiday carries its citation (date, type, decree number, signing date) and the build rejects an uncited entry, a duplicate date, or a leftover placeholder.

Contradictions are recorded and priced, not silently resolved

Where sources disagree on the cuti bersama rule, the app holds the competing readings with their sources — and a ledger page prices the rejected reading, stating what it would do to your entitlement if it were true. Employment status is a first-class input (ASN, private-with-deduction, private-without are three outcomes with each branch citing its instrument, not a boolean), and the sheet marks which cuti bersama days a deducting employer charged, with a test asserting the per-day marks sum to the ledger's total across every year, pattern, and status.

The optimiser is exact, not greedy

The problem is small enough to solve properly: enumerate the gaps between fixed off-blocks, cost each in workdays, and select under budget with a DP — and a twenty-line brute-force oracle (never imported outside tests) was written first, so "optimal" is a claim rather than a hope. That oracle caught a real bug: the longest single stretch objective can't reuse the DP with max swapped in — that returns the right number and the wrong plan — so it needed a completely different algorithm, and each objective has its own oracle. There are no Date objects in the engine: days are integer day numbers, week position is arithmetic, and the year is always an explicit argument.

From "correct" to "comprehensible"

The engine was finished and verified, then a long run of pedagogy/design/a11y work rebuilt the front end for a reader who has never heard of cuti bersama: a worked example with real dates before any control, citations moved onto the card that applies the rule, and each objective shown by what it produces. The honest lesson recorded for a client: the arithmetic being provably right did nothing for a reader who didn't know the rule existed. And the one thing blocking a fully "verified" claim is documented rather than papered over — the 2026 data pack is flagged perluVerifikasi (dates transcribed from the widely-circulated calendar but decree numbers not yet checked against source documents), so the site carries a banner rather than presenting a plausible fabrication as fact.

Outcome

Live and public, bilingual, fully static, offline after first load. It ships the year sheet — twelve months laid out as an Indonesian wall calendar, national holidays in red and cuti bersama in amber (a different thing at a different cost), with runs of consecutive days off drawn as continuous bars, so toggling a leave day and watching two blocks snap into one stretch is the app's central moment; the four status cards on the landing page (three leave you 12 days, one leaves you 4); exact bridge suggestions with the leverage arithmetic exposed ("1 day → 4 days off"); a non-monotone marginal curve showing what the nth leave day actually buys; "holidays the weekend ate" for both five- and six-day weeks; and ICS + PNG export with full state in the URL hash.

Built solo in a concentrated build — ~9,150 lines across engine, UI, and data, 134 tests, on three runtime dependencies and no date, calendar, or holiday library (Zod is build-time only — a commit explicitly moved it out of the browser bundle). It states plainly that it is a personal project, not employment-law advice, and that company policy governs cuti bersama in the private sector.

Outcomes

Leave left if your employer deducts cuti bersama
12 → 4
Bridge optimiser, verified vs brute force
Exact
Tests; refuses years with no published decree
134
Date libraries — pure integer day math
0

Screenshots

Beranda
Kalender
Rencana
Aturan dan Sitasi

Have a project like this?

If you need a system built with the same care — clear scope, solid execution — let's talk.

Start a project