Één UTC-server, vier tijdzones per boeking
Meetings kruisen viewer, host, gast en cron — allemaal in verschillende zones. Zo houdt Senly datums schoon zonder ooit een JavaScript Date te vertrouwen.
De verzwegen bug: elke getHours() liegt
Railway draait de containers in UTC. Dat is een goede default — behalve dat een developer die op laptop-tijd test, elke bug pas op vrijdagavond ontdekt wanneer een klant een boeking om 23:30 krijgt in plaats van 01:30. Elke aanroep van new Date(y,m,d) of getHours() op een instant in server-code is een tijdbom die klanten in andere tijdzones activeren.
De regel: vier bronnen, één helper-file
lib/timezones.ts is de enige plek die weet welke zone er nodig is. Viewer heeft User.timezone. Host heeft Availability.timezone. Gast heeft BookedMeeting.attendeeTimezone. Cronjobs draaien in Europe/Amsterdam voor werkuren-berekeningen. Nergens anders in de codebase mag een tijdzone-conversie plaatsvinden — die regel wordt via een grep-regel in de CI afgedwongen.
Waarom toISOString().slice(0,10) verboden is
De klassieke datum-truc "toISOString().slice(0,10)" pakt de datum in UTC — dus 2 juli 23:30 Nederlandse tijd wordt "2026-07-02" in de database opgeslagen. Voor een boeking om 01:30 lokale tijd betekent dat: de host krijgt de meeting-notificatie een dag te laat. De helper-file heeft dagInZone(instant, tz) die dit expliciet doet met de juiste tijdzone.
Hoe je het test zonder wachten tot vrijdag
Er staat een Vitest-suite met 40+ combinaties: winter/zomer (DST!), UTC+0/+1/+2, viewer=host, viewer≠host, meeting die over middernacht loopt. Elke lib-functie krijgt vier lopende test-tijdzones. Als de test op UTC en op Los_Angeles beide slaagt, weten we dat de logica geen impliciete lokale zone gebruikt.