De bug begint meestal met een zin die onschuldig klinkt
De meeste datum- en tijdbugs beginnen niet met ingewikkelde astronomie. Ze beginnen met een productzin die vanzelfsprekend klinkt: “stuur de herinnering morgenochtend”, “laat deze proefperiode na 30 dagen verlopen”, “toon de bestellingen van vandaag”, “draai deze taak om 2:30”, “sla de timestamp op”. Elke zin verbergt een regel. Morgenochtend waar? Dertig kalenderdagen of 30 keer 24 uur? Vandaag in de stad van de klant, het magazijn of de databaseserver? Wat gebeurt er als 2:30 niet bestaat?
Software is goed in tellen. Kalenders zijn niet alleen tellen. Ze zijn een stapel menselijke afspraken: tijdzones, zomertijdwissels, werkdagen, lokale notaties, schrikkeljaren, ISO-weken, databasetypen en juridische cut-off tijden. Bugs ontstaan wanneer code de ene laag behandelt alsof het een andere is.
Deze gids is een veldhandleiding voor fouten die productiesystemen steeds opnieuw breken. Hij is geschreven voor ontwikkelaars, productmanagers, analisten en iedereen die ooit naar een dashboard keek en zich afvroeg waarom de cijfers van gisteren na het ontbijt veranderd waren.
1. Naive datetimes: de timestamp zonder paspoort
Een naive datetime is een datum en kloktijd zonder tijdzone. 2026-03-08 02:15 lijkt precies, maar zegt niet waar het gebeurde. In New York bestaat tijdens de zomertijdovergang in het voorjaar van 2026 het uur van 02:00 tot 02:59 niet. In een andere stad kan het geldig zijn. In UTC betekent het weer iets anders.
De veelgemaakte fout is lokale wandtijd opslaan alsof het een instant is. Een vergadering voor “9 uur Europe/Madrid” moet 9 uur in Madrid blijven wanneer Spanje de klok verzet. Een betalingsevent dat al gebeurd is, moet als instant worden opgeslagen. Dat zijn verschillende modellen.
| Gebruik | Opslaan | Waarom |
|---|---|---|
| Auditlog, klik, betaling, API-event | Instant, meestal UTC | Het event gebeurde al op één moment. |
| Toekomstige vergadering in een stad | Lokale datum, lokale tijd, IANA-zone | De lokale kloktijd moet DST-regelwijzigingen overleven. |
| Verjaardag, feestdag, factuurdag | Alleen datum | Een tijdstip kan geen betekenis hebben. |
| Openingstijden | Lokale tijd plus plaats of regel | De regel herhaalt zich in lokale burgerlijke tijd. |
Als je exacte momenten moet vergelijken, converteer dan naar instants. Als je een menselijke afspraak wilt bewaren waar die bedoeld was, bewaar de plaats erbij. Een offset zoals UTC+01:00 is niet genoeg voor toekomstige planning, omdat de offset kan veranderen.
2. UTC-verwarring: goede opslag, slechte gebruikerservaring
“Alles in UTC opslaan” is een nuttige regel voor events die al gebeurd zijn. Het is geen volledig productontwerp. Gebruikers denken niet in UTC wanneer ze een tandartsafspraak plannen, een webinar publiceren of een hoteldatum kiezen. Ze denken in lokale burgerlijke tijd.
De bug verschijnt wanneer een systeem UTC correct opslaat maar de bedoelde zone van de gebruiker kwijtraakt. Een uitnodiging die in Los Angeles voor 9 uur is gemaakt, kan intern als 17:00 UTC verschijnen. Dat is prima. Maar als de app het event later opnieuw opbouwt met de tijdzone van de server, of alleen een vaste offset opslaat, kan de vergadering na een zomertijdwijziging verschuiven.
Goede systemen scheiden drie vragen: welk instant is dit, welke lokale tijd bedoelde de gebruiker, en welke tijdzoneregels verklaren die lokale tijd? Onze tijdzoneconverter is juist nuttig omdat datum, plaats en offset samen tellen.
3. DST-gaten en folds: tijden die verdwijnen of twee keer gebeuren
Zomertijd maakt twee klassieke valkuilen. In het voorjaar kan een lokaal uur worden overgeslagen. In het najaar kan een lokaal uur twee keer plaatsvinden. Als je planner “2026-03-08 02:30 America/New_York” accepteert, wat moet hij doen? Afwijzen? Verplaatsen naar 03:00? Interpreteren in een andere zone? Er is geen universeel juist antwoord. Het product heeft een regel nodig.
Dubbelzinnige herfsttijden zijn nog subtieler. “2026-11-01 01:30 America/New_York” kan verwijzen naar de eerste 01:30 of de tweede 01:30. Beide lokale labels zijn echt. Logs, vervoerssystemen, ziekenhuissystemen en taakschedulers moeten weten welke bedoeld is.
Een nuttige testset bevat echte overgangsdata voor zones die belangrijk zijn voor je gebruikers. Test niet alleen 15 januari. Test een spring-forward dag, een fall-back dag en een land waarvan de DST-regels verschillen van die van jou.
4. Datum-only waarden die per ongeluk middernacht worden
Een datum is niet altijd een datetime met middernacht eraan vast. Een hotelboeking voor 3 juni, een verjaardag, een belastingdeadline en een feestdag zijn datumconcepten. Wanneer code ze omzet naar 2026-06-03T00:00:00Z, heeft de code een tijd en zone verzonnen.
Zo verschuiven verjaardagen naar de vorige dag voor gebruikers ten westen van UTC. Zo kan “het rapport van vandaag” ook de data van gisteren tonen voor het ene team en de data van morgen voor het andere. De oplossing is niet ingewikkeld: gebruik datum-only typen voor datum-only concepten, en maak pas een datetime wanneer de bedrijfsregel echt een tijd nodig heeft.
Stel bij deadlines een scherpere vraag: verloopt het item aan het begin van de datum, het einde van de datum, lokale sluitingstijd of een exact instant? “Verloopt op 3 juni” is producttekst, geen volledige regel.
5. Dagen optellen als seconden
86.400 seconden optellen is niet hetzelfde als één lokale kalenderdag optellen. De meeste dagen duren 24 uur, maar zomertijdovergangen kunnen 23 of 25 uur duren. Als een dagelijkse herinnering om 8 uur lokale tijd moet verschijnen, tel dan een kalenderdag op in de zone van de gebruiker. Als een cache precies na 24 uur moet verlopen, tel dan een duur op.
| Zin | Betekent meestal | Veiliger model |
|---|---|---|
| Zelfde tijd morgen | Volgende lokale kalenderdag op dezelfde kloktijd | Datum/tijd in gebruikerszone |
| Verloopt over 24 uur | Exact verstreken duur | Instant plus duur |
| Vervalt over 3 werkdagen | Lokale werkdagregel | Werkkalender |
| Elke maandag om 9 | Terugkerend lokaal schema | Lokale tijd plus IANA-zone |
Daarom is een werkdagen-calculator niet zomaar een datumverschilcalculator met weekenden verwijderd. Feestdagen, lokale weekends en waargenomen sluitingen kunnen de uitkomst veranderen.
6. Unix timestamp-eenheden: seconden, milliseconden, microseconden, nanoseconden
Unix-tijd is elegant: tel tijd sinds 1970-01-01T00:00:00Z. Het probleem is dat software-ecosystemen het niet eens zijn over de eenheid. Veel APIs gebruiken seconden. JavaScript timestamps gebruiken milliseconden. Sommige databases en observability-systemen gebruiken microseconden of nanoseconden. 1717425600 en 1717425600000 stellen hetzelfde moment voor in verschillende eenheden; gebruik de verkeerde en je datum springt eeuwen.
De snelste sanity check is het aantal cijfers. Huidige Unix-seconden hebben 10 cijfers. Milliseconden hebben 13. Microseconden 16. Nanoseconden 19. Een goede importer moet de eenheid expliciet identificeren of dubbelzinnige invoer weigeren in plaats van stil te gokken. Onze Unix timestamp converter laat hetzelfde idee als tool zien.
7. Datums als strings sorteren in het verkeerde formaat
ISO-achtige datums sorteren goed als tekst: 2026-06-03 komt voor 2026-12-01. Veel lokale formaten doen dat niet. Een lijst met MM/DD/YYYY- of DD/MM/YYYY-strings kan in een kleine sample correct lijken en falen zodra maanden of locales veranderen.
Gebruik ISO 8601 of echte datumtypen voor uitwisseling en opslag. Gebruik gelokaliseerde formaten alleen aan de displayrand. Als een CSV voor mensen bedoeld is, vermeld het verwachte formaat in de header of exportdocumentatie. Als hij voor machines bedoeld is, kies voor timestamps een ondubbelzinnige standaard zoals RFC 3339.
8. Weeknummerbugs rond nieuwjaar
Weeknummers zijn niet universeel. ISO-weken beginnen op maandag en maken week 1 de week met de eerste donderdag. Sommige Amerikaanse systemen beginnen op zondag en tellen de week met 1 januari als week 1. Rond nieuwjaar kunnen die regels een hele week verschillen.
Als een rapport “week 1 omzet” zegt, een payroll-export “2026-W01” zegt en een spreadsheet een standaard weeknummerfunctie gebruikt, bevestig dan de regel voordat je cijfers vergelijkt. Voor internationale teams is het veiligste label het weeksysteem plus de datumrange, bijvoorbeeld: “ISO 2026-W01, 29 dec 2025-4 jan 2026”.
Dezelfde logica zit in onze weeknummer-tool, die de datumrange toont in plaats van het nummer vanzelfsprekend te maken.
9. Server-tijdzonelekken
Een van de frustrerendste productiebugs is code die lokaal elke test doorstaat en na deployment faalt omdat de server in een andere tijdzone draait. De laptop van de ontwikkelaar, CI-runner, Docker-image, database, queue worker en analytics warehouse kunnen allemaal andere defaults hebben.
Zet expliciete tijdzones in tests. Draai minstens één testjob met een niet-lokale tijdzone. Vermijd afhankelijkheid van process defaults voor parsen of formatteren. Als een functie een datumstring ontvangt, maak verwacht formaat en zone onderdeel van het contract.
10. “Einde van de dag” zonder plaats
Einde van de dag klinkt duidelijk totdat klanten op meer dan één plaats wonen. Een promotie die “vrijdag om middernacht” eindigt, kan de lokale middernacht van de klant betekenen, middernacht op het hoofdkantoor, UTC-middernacht of de cut-off van de betaalprovider. Elke regel geeft een andere gebruikerservaring.
Goede producttekst noemt de plaats of gebruikt een countdown die aan de echte deadline gekoppeld is. Goede backendcode slaat de deadline op als een precies instant nadat de bedrijfsregel is toegepast. Goede supportdocumentatie zegt welke regel gebruikt is.
Praktische checklist voordat je datum/tijd-code shipt
- Is deze waarde een instant, lokale datum/tijd, datum-only waarde, duur of herhalingsregel?
- Als hij lokaal is, welke IANA-tijdzone bezit de regel?
- Als het een datum-only waarde is, hebben we vermeden om middernacht UTC te verzinnen?
- Hebben DST-gaten en folds expliciet gedrag?
- Zijn timestamp-eenheden benoemd, gevalideerd en getest?
- Definiëren rapporten het weeknummersysteem en de datumrange?
- Draaien tests buiten de eigen tijdzone van de ontwikkelaar?
- Zegt gebruikerscopy genoeg zodat iemand correct kan handelen?
De diepere regel
Datum- en tijdbugs ontstaan zelden door een gebrek aan slimme code. Ze ontstaan door een ontbrekend zelfstandig naamwoord. “Morgen” heeft een plaats nodig. “Dertig dagen” moet kalender of duur zeggen. “Week 1” heeft een systeem nodig. “Timestamp” heeft een eenheid nodig. “Einde van de dag” heeft een jurisdictie of tijdzone nodig.
Wanneer dat woord expliciet is, wordt de code meestal eenvoudiger. De databasekolom krijgt het juiste type. Het API-contract verrast minder. De UI kan het resultaat uitleggen. En de volgende persoon die de code leest hoeft niet te reconstrueren wat “nu plus één dag” had moeten betekenen.