New:AI-Generated Infrastructure

Timezone-Aware Booking API: Built for AI Agents & Custom Frontends

by Mateusz Sowa
Timezone-Aware Booking API: Built for AI Agents & Custom Frontends

Your AI agent just booked a consultation for 3 PM. The customer is in Tokyo, the therapist is in Berlin. Which 3 PM did the agent mean?

If the answer isn't instantly clear from the data the API returned, you have a timezone bug — the kind that silently costs businesses thousands in no-shows, support tickets, and lost trust. And when autonomous AI agents handle bookings without a human double-checking, timezone precision isn't a nice-to-have. It's critical infrastructure.

This is where GraphQL shines. Unlike REST endpoints that return opaque timestamp strings and leave timezone interpretation to the caller, a well-typed GraphQL API can encode timezone semantics directly into the schema — giving AI agents unambiguous, machine-readable time data in every response.

At Timerise, we've spent years engineering timezone handling that eliminates this entire category of bugs — for human users and for AI agents. Here's how our GraphQL API does it, and what it means for developers building custom frontends or connecting autonomous agents.

The Core Problem: Local Time Is a Lie (and AI Agents Can't Guess)

Every developer eventually learns this the hard way: local time doesn't exist in isolation. "3:00 PM" means nothing without a timezone anchor. A human might infer the right timezone from context. An AI agent can't — it needs explicit, structured data. And timezone handling is far more complex than adding or subtracting hours from UTC.

Consider these complications:

  • Daylight Saving Time (DST) shifts clocks forward or backward, meaning some local times happen twice a year and some don't exist at all
  • IANA timezone rules change — governments modify DST rules, sometimes with only weeks of notice
  • UTC offsets aren't staticEurope/Warsaw is +01:00 in winter and +02:00 in summer

If your booking system stores times as "3:00 PM CET," you're already in trouble. CET is ambiguous — it doesn't tell you whether DST applies. That's why Timerise follows a different approach entirely.

Timerise's UTC-First Architecture

Our API follows a strict UTC-first storage model. Every timestamp in the database is stored as a UTC value. No local times in storage, no ambiguity, no DST surprises waiting to happen.

We pair this with IANA timezone identifiers — the industry standard maintained by the IANA Time Zone Database. Instead of vague abbreviations like "EST" or "CET," we use precise identifiers like America/New_York or Europe/Warsaw that encode the complete history of timezone rules for a given location.

Every project in Timerise carries a required localTimeZone field — a single, queryable source of truth that AI agents can read programmatically:

type Project { localTimeZone: TimeZone! # e.g. "Europe/Warsaw" now: DateTime! # Current UTC time nowISO: DateTimeISO! # Current time in project's timezone }

This single field governs all slot generation, booking output, and display formatting for every service under that project. One source of truth, zero guessing games. An AI agent can query localTimeZone once and use it to correctly interpret every timestamp the project returns.

Two DateTime Types: Machine-Readable and Human-Readable

The Timerise API exposes two scalar types that solve the "which 3 PM?" problem. This distinction is what makes the API exceptionally AI-agent-friendly — the agent always knows which field to use for logic and which for display:

ScalarFormatExampleUse Case
DateTimeUTC ISO 86012026-02-16T19:43:29.000ZTime arithmetic, storage, comparisons
DateTimeISOISO 8601 with offset2026-02-16T20:43:29+01:00Human-readable display

DateTime is the machine-friendly format. It always ends with Z (Zulu time = UTC). AI agents should use this for all time arithmetic — comparisons, sorting, conflict detection, and scheduling logic.

DateTimeISO is the human-friendly format. It shows the local time with the UTC offset, so a user in Warsaw sees 20:43:29+01:00 — immediately clear, no mental math required. When an AI agent needs to present times to end users, DateTimeISO provides a ready-to-display value with the correct local offset baked in.

Every slot in the system exposes both:

type Slot { dateTimeFrom: DateTime! # "2026-02-16T19:43:29.000Z" dateTimeFromISO: DateTimeISO! # "2026-02-16T20:43:29+01:00" dateTimeTo: DateTime! dateTimeToISO: DateTimeISO! }

For developers building custom frontends, the rule is straightforward: use dateTimeFrom / dateTimeTo for any time arithmetic, and dateTimeFromISO / dateTimeToISO for rendering to users.

Auto-Detecting the User's Timezone in Custom Frontends

When you build a custom booking page on top of Timerise, your users might be in a different timezone than the service provider. Here's how to handle this gracefully.

Browser-Side Detection

Modern browsers expose the user's timezone through the Intl API (Application Programming Interface):

const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; // Returns IANA identifier, e.g. "America/New_York"

This returns a proper IANA identifier — exactly the format Timerise expects. You can pass it directly to the API when creating bookings:

mutation CreateBooking { bookingCreate( serviceId: "srv_12345" slots: ["slot_abc"] timeZone: "America/New_York" # from Intl API ) { bookingId dateTimeFrom dateTimeFromISO } }

The timeZone parameter gets stored on the booking document. Later, when the user views their confirmation, the dateTimeFromISO field automatically formats the time in their timezone with the correct offset.

Recalculating Display Times for Users

What if your frontend needs to show slot times in the user's local timezone rather than the provider's? The API returns both UTC and provider-local times, giving you full flexibility.

Option A — Let the API handle it. Query slots normally and use the DateTime (UTC) fields. Then format them client-side using the detected timezone:

const slotUTC = "2026-02-16T19:43:29.000Z"; const userTZ = Intl.DateTimeFormat().resolvedOptions().timeZone; const localTime = new Date(slotUTC).toLocaleString("en-US", { timeZone: userTZ, hour: "2-digit", minute: "2-digit", hour12: true, }); // "2:43 PM" for America/New_York (UTC-5)

Option B — Show both perspectives. Display the provider's local time (dateTimeFromISO) alongside the user's local time. This builds trust in cross-timezone bookings:

Session Time: 8:43 PM Warsaw (2:43 PM your time)

Both approaches work because the API gives you the raw UTC data and the provider-formatted local time. You choose how to present it.

Slot Generation: Where Timezones Get Tricky

Behind the scenes, Timerise's slot generation engine handles the hardest timezone problems so you don't have to.

When a service provider sets their availability as "Monday through Friday, 9:00 AM to 5:00 PM," they mean local time. But the system stores everything in UTC. The conversion has to account for DST transitions, where the offset changes mid-schedule.

Timerise solves this with the date-fns-tz library and three core functions:

  • fromZonedTime(localDate, timeZone) — converts wall-clock time to UTC
  • toZonedTime(utcDate, timeZone) — converts UTC to wall-clock time
  • formatInTimeZone(utcDate, timeZone, format) — formats a UTC date for display in any timezone

The generation pipeline works like this:

  1. Provider's schedule (local times) → converted to UTC boundaries via fromZonedTime
  2. Slot overlap detection → pure UTC millisecond arithmetic, no timezone conversions
  3. Working hours filtering → toZonedTime shifts back to local for time-of-day comparisons
  4. Output assembly → both DateTime (UTC) and DateTimeISO (offset-formatted) fields

This means a 9:00 AM slot in Europe/Warsaw correctly becomes 08:00:00Z in winter (+01:00) and 07:00:00Z in summer (+02:00). The slot generation handles this automatically across DST boundaries — no manual intervention needed.

Practical Tips for Frontend Developers

Building a custom frontend on Timerise? Keep these guidelines in mind:

1. Always Send UTC for DateTime Inputs

The DateTime scalar parses inputs as UTC. Don't send local times without a Z suffix — they'll be misinterpreted:

✅ Correct: "2026-02-16T19:43:29.000Z" ❌ Wrong: "2026-02-16T20:43:29" (treated as UTC, off by 1 hour)

2. Know When to Use DateTimeISO vs DateTime Inputs

DateTimeISO output fields are computed at query time from the stored UTC value and the booking's timezone. But DateTimeISO is also accepted as an inputbookingCreate, bookingReschedule, bookings, and services all support dateTimeISOFrom / dateTimeISOTo parameters. Use DateTimeISO inputs when you already have a local time with offset (e.g., from a user's calendar). Use DateTime (UTC) inputs when doing time arithmetic or when you've already converted to UTC.

3. Handle Missing Timezone Gracefully

Older booking records might lack a timeZone field, causing dateTimeFromISO to return null. Always fall back to the UTC dateTimeFrom field:

const displayTime = booking.dateTimeFromISO ?? new Date(booking.dateTimeFrom).toLocaleString();

4. Detect Timezone Once, Reuse Everywhere

Call Intl.DateTimeFormat().resolvedOptions().timeZone once on page load and store it. Pass the same IANA identifier to every bookingCreate or bookingReschedule mutation. Consistency prevents mismatched timezone records. For AI agents, the timezone should be captured from the user's profile or session context and passed consistently through every API call.

5. Remember: DST Days Aren't Always 24 Hours

If you're building a day-view calendar, don't assume 24 * 60 * 60 * 1000 milliseconds equals one day. During DST transitions, a local day can be 23 or 25 hours. Use library functions (like date-fns) for date arithmetic instead of raw millisecond math.

DateTimeISO as Input: Send Local Times Directly

Beyond being an output scalar, DateTimeISO is accepted as an input type for queries and mutations. This means you can send local times with their UTC offset directly — the API parses the offset and converts to UTC internally.

This is a significant improvement for AI agents and LLM-powered apps. Instead of converting local times to UTC before every API call, an agent can pass the user's local time as-is with the offset — reducing conversion logic, eliminating a class of bugs, and making the agent's code simpler and more reliable.

The following queries and mutations accept dateTimeISOFrom / dateTimeISOTo parameters:

  • Queries: bookings and services — filter results by local time range
  • Mutations: bookingCreate and bookingReschedule — specify booking times in local time
mutation CreateBooking { bookingCreate( serviceId: "srv_12345" dateTimeISOFrom: "2026-03-15T09:00:00+01:00" dateTimeISOTo: "2026-03-15T10:00:00+01:00" ) { bookingId status } }
query GetBookings { bookings( dateTimeISOFrom: "2026-03-15T00:00:00+01:00" dateTimeISOTo: "2026-03-16T00:00:00+01:00" ) { bookingId dateTimeFrom dateTimeFromISO # local start time with UTC offset (e.g. "2026-03-15T09:00:00+01:00") } }

No manual UTC conversion. No offset arithmetic. The API handles it.

Why GraphQL + Full Timezone Support Is the Best Stack for AI Agents

Timezone bugs aren't edge cases — they're revenue killers. Every wrong appointment time means:

  • No-shows from confused customers
  • Support tickets from frustrated users
  • Double bookings when slots overlap across DST transitions
  • Legal liability in healthcare or legal scheduling

When AI agents handle bookings autonomously, the stakes are even higher. There's no human in the loop to catch a timezone mistake before it reaches the customer. The API must be correct by design.

This is where Timerise's combination of GraphQL and full timezone support creates a uniquely powerful stack for autonomous agents:

  1. Schema-level type safetyDateTime, DateTimeISO, and TimeZone scalars prevent the agent from sending malformed time data. The schema is the contract.
  2. Introspection — an AI agent can query the schema itself to discover available fields and types. No documentation parsing, no hallucination risk.
  3. Precise data fetching — the agent requests only the time fields it needs. No over-fetching, no wasted tokens.
  4. Unambiguous timezone semantics — every response includes both UTC and offset-formatted times. The agent always knows exactly what timezone a value represents.
  5. DateTimeISO inputs — agents send local times directly, eliminating client-side UTC conversion entirely.

REST APIs leave timezone interpretation to the caller. GraphQL APIs with typed timezone scalars make it impossible to misinterpret a timestamp. For AI agents operating at scale across multiple timezones, this isn't a convenience — it's a requirement.

Timerise's API handles the complexity. Your frontend — or your AI agent — handles the experience. Your users see the right time, every time.


Ready to build timezone-aware booking experiences with AI agents?

Explore the Timerise API documentation →

Read: GraphQL — The Perfect Backend for AI Agents →

Test GraphQL queries live →

Sign up and start building for free →

<br>

Mateusz Sowa