← All blogs

Product engineering

Booking time zones: the hour that happens twice

A booking for 1:30 a.m. can mean two slots an hour apart on the night the clocks go back. The date and venue don't settle it. We'd make the picker spell out which one you're choosing, because the confirmation email should leave you knowing when to turn up.

Rubix Labs5 min read
TikiTaka's sports-facility discovery screen on a phone against a purple background.
TikiTaka, from Rubix's published portfolio. The time-zone examples below are synthetic and do not describe this client's implementation.

Two slots can have the same label

Take a synthetic venue using America/Denver, the IANA time zone used for Wyoming. On 1 November 2026, 07:30 UTC converts to 01:30 with offset -06:00. An hour later, 08:30 UTC converts to 01:30 again, this time with offset -07:00. UTC is the common timeline; the offset tells you how far the local clock is from it at that moment.

A dropdown showing two identical '1:30 a.m.' entries doesn't give the customer enough information. Silently keeping only one is also a product decision, whether the developer meant to make it or not. For a venue open through that hour, we'd label the earlier and later occurrence, including the offset, and carry the chosen slot ID through checkout.

You can reproduce both rows with the linked Node.js check. It starts with fixed UTC instants and formats them in America/Denver using Intl.DateTimeFormat. The venue and bookings are made up.

Two slots can have the same label
UTC input on 1 November 2026Venue timeOffset from UTC
07:3001:30, earlier occurrence-06:00
08:3001:30, later occurrence-07:00

Keep the venue's time zone with the booking

For a confirmed slot, we'd keep its UTC start and end alongside the venue's IANA zone, the local date and time the customer selected, and the offset that resolved an ambiguous choice. A bare offset such as -06:00 describes one moment; it doesn't tell you the venue's rules for another date. A label such as 'Mountain time' leaves too much for the application to infer.

A traveller might book before they've changed their phone's time zone. The court still opens on the venue's clock. Have the server resolve and validate the slot using that configured zone, and name it on the confirmation. Showing the traveller's local time too can help, as long as the two labels make it obvious which is which.

Rubix Labs' TikiTaka work covered sports-facility discovery and booking tools for players and owners. For a new product like that, we'd agree what each slot means with the owner before drawing the picker. The public TikiTaka case study describes the booking product; it doesn't document its daylight-saving behavior. We're using a separate synthetic example here.

Time-zone rules can change. Keep the original booking intent so an update can identify affected future reservations. Whether a reservation should retain its local start time or its absolute instant is a business decision; review and communicate any change instead of moving a confirmed appointment without notice.

In March, the slot might not exist

In our Denver test, 8 March 2026 moves from 01:59 at 08:59 UTC to 03:00 at 09:00 UTC. There is no local 02:30 that morning. A text field will still let somebody type it, of course. That's where a perfectly reasonable-looking request needs a validation error.

We'd prevent that slot from appearing in the picker and reject it at the server if an older screen or API request submits it anyway. For a typed time, explain that it falls in the clock change and offer valid alternatives. Automatically shifting it to 03:30 might suit some products, but the customer needs to know they've chosen a different time.

The sample checks conversion around that jump. It isn't a parser for arbitrary local-time input, an availability engine or a concurrency test. Those still need their own checks in the booking application.

Next Sunday at nine can be 169 hours away

Now give the customer a recurring Sunday booking at 09:00. In the same example, 25 October 2026 at 09:00 is 15:00 UTC. Add exactly 168 elapsed hours and the next reservation lands at 08:00 venue time on 1 November. The code has added a week of seconds and moved the customer's appointment an hour earlier.

For a weekly booking that follows the venue clock, retain the local time, weekday and venue zone, then resolve each occurrence. Decide what should happen if a future occurrence lands in a missing or repeated hour: skip it, ask for a choice or apply an agreed rule. A reservation at a fixed UTC time has a different intention. The scheduling rule should say which one you promised.

Keeping 09:00 on both Sundays takes 169 elapsed hours across this transition.

Next Sunday at nine can be 169 hours away
Scheduling choiceUTC instantDenver time
Original Sunday booking25 October 2026, 15:0009:00, offset -06:00
Add 168 hours1 November 2026, 15:0008:00, offset -07:00
Keep next Sunday at 09:001 November 2026, 16:0009:00, offset -07:00

Try the dates in your own booking flow

Save the linked file as booking-time-zone-check.mjs and run node booking-time-zone-check.mjs with Node.js 20 or later. It uses built-in APIs, makes no network requests and changes no files. Our run passed ten assertions on Node.js 22.17.1 with ICU 77.1 and bundled time-zone data 2025b. We also checked the Denver rules against IANA's 2026e data; those rules agree for these dates. The script prints your runtime versions with its results.

Once that runs, try the same dates in your booking flow. Follow the repeated hour from the picker to the confirmation email and owner calendar. Try submitting the missing hour through the API too, and inspect weekly bookings on either side of the change. If the picker says earlier 1:30 and the email loses that distinction, you've found something the conversion checks alone couldn't catch.

Rubix Labs

A few finishing touches.