Skip to main content
RapidDev - Software Development Agency
retool-tutorial

How to Handle Time Zones in Retool Applications

Store all dates in UTC in the database. Enable the 'Manage time zone' toggle on Date Pickers to auto-convert between local and UTC. For display, use Intl.DateTimeFormat with the timeZone option to render UTC values in the user's local zone. For complex conversions, load moment-timezone via Retool's Preloaded JS setting.

What you'll learn

  • Store all dates in UTC in the database and convert to local time only at display time
  • Use the Date Picker's 'Manage time zone' toggle to handle UTC storage automatically
  • Convert UTC timestamps to user local time using Intl.DateTimeFormat with a timezone parameter
  • Import and use moment-timezone (loaded via Preloaded JS) for complex timezone conversions
  • Display the user's local timezone in a Text component using Intl.DateTimeFormat().resolvedOptions()
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner9 min read15-20 minRetool Cloud and Self-hostedLast updated March 2026RapidDev Engineering Team
TL;DR

Store all dates in UTC in the database. Enable the 'Manage time zone' toggle on Date Pickers to auto-convert between local and UTC. For display, use Intl.DateTimeFormat with the timeZone option to render UTC values in the user's local zone. For complex conversions, load moment-timezone via Retool's Preloaded JS setting.

Quick facts about this guide
FactValue
ToolRetool
DifficultyBeginner
Time required15-20 min
CompatibilityRetool Cloud and Self-hosted
Last updatedMarch 2026

UTC Storage and Local Display — the Timezone Strategy for Retool

Timezone bugs are among the most confusing issues in data apps. The root cause is almost always the same: dates stored in local time instead of UTC, causing records to appear one day off for users in different timezones. The solution is a simple rule: always store UTC, always display local.

Retool runs in the user's browser, so component values default to the browser's local timezone. This creates subtle bugs: a Date Picker without timezone management enabled stores whatever the user selected as a bare timestamp, without timezone context. When another user in a different timezone loads that record, the same timestamp displays at a different local time.

This tutorial covers the correct timezone strategy for Retool apps: enabling the Date Picker's Manage time zone toggle for automatic UTC conversion, displaying stored UTC values in local time using the Intl API, and handling complex cross-timezone displays with moment-timezone for apps serving users across many zones.

Prerequisites

  • A Retool app with a Date Picker and a Table showing datetime values
  • A database resource that stores TIMESTAMPTZ (PostgreSQL) or DATETIME (MySQL) columns
  • Basic familiarity with JavaScript Date objects

Step-by-step guide

1

Store dates as TIMESTAMPTZ in PostgreSQL

The foundation of correct timezone handling is the database column type. Use TIMESTAMPTZ (timestamp with time zone) in PostgreSQL rather than TIMESTAMP (without time zone). TIMESTAMPTZ stores the moment in time in UTC and PostgreSQL converts to the session timezone for display. In MySQL, use DATETIME with explicit UTC storage. Always insert UTC values from your application.

typescript
1-- PostgreSQL: use TIMESTAMPTZ
2CREATE TABLE events (
3 id SERIAL PRIMARY KEY,
4 title TEXT,
5 event_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
6 created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
7);
8
9-- Insert UTC timestamp from Retool (Date Picker with Manage Time Zone enabled):
10INSERT INTO events (title, event_at)
11VALUES ({{ titleInput.value }}, {{ datePicker1.value }}::TIMESTAMPTZ);
12-- datePicker1.value with Manage Time Zone ON = UTC ISO string
13
14-- Query with timezone conversion:
15SELECT
16 title,
17 event_at AT TIME ZONE 'UTC' AT TIME ZONE 'America/New_York' AS event_at_eastern
18FROM events;

Expected result: Dates are stored as UTC TIMESTAMPTZ. PostgreSQL can convert to any timezone on retrieval.

2

Enable 'Manage time zone' on the Date Picker

Select the Date Picker component. In the Inspector → Advanced section, find the 'Manage time zone' toggle and enable it. With this on, when a user selects a date and time in their local timezone, Retool automatically converts it to UTC before exposing the value in {{ datePicker1.value }}. When loading a UTC value from the database into the Date Picker's Default Value, Retool converts it back to the user's local time for display. This toggle handles the most common timezone use case automatically.

typescript
1// With 'Manage time zone' ON:
2// User in New York selects: 2026-03-25 09:00 AM EST
3// datePicker1.value = '2026-03-25T14:00:00.000Z' (UTC)
4
5// User in London selects: 2026-03-25 09:00 AM GMT
6// datePicker1.value = '2026-03-25T09:00:00.000Z' (UTC)
7
8// Database INSERT — same UTC value regardless of user timezone:
9INSERT INTO events (event_at)
10VALUES ({{ datePicker1.value }}::TIMESTAMPTZ);
11
12// Loading a UTC value back into the Date Picker:
13// Default Value: {{ getEvent.data[0].event_at }}
14// Display: shows in user's LOCAL time (e.g., 9 AM for both users)

Expected result: Date Picker stores UTC values regardless of user timezone. Users in different timezones see the correct local time for the same event.

3

Display UTC timestamps in user local time using Intl API

When displaying stored UTC timestamps in Text components, Table columns, or Text Inputs, convert them to the user's local timezone using the browser's built-in Intl.DateTimeFormat API. No additional libraries needed. This approach uses the user's actual browser timezone, ensuring correct display regardless of their location.

typescript
1// Display UTC timestamp in user's browser timezone:
2{{ new Intl.DateTimeFormat('en-US', {
3 timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
4 year: 'numeric',
5 month: 'short',
6 day: 'numeric',
7 hour: '2-digit',
8 minute: '2-digit',
9 timeZoneName: 'short'
10}).format(new Date(table1.selectedRow.data.event_at)) }}
11// Output: 'Mar 25, 2026, 09:00 AM EST'
12
13// Short display (date only, local timezone):
14{{ new Date(row.created_at).toLocaleDateString() }}
15
16// With explicit timezone:
17{{ new Intl.DateTimeFormat('en-US', {
18 timeZone: 'America/New_York',
19 dateStyle: 'medium',
20 timeStyle: 'short'
21}).format(new Date(row.event_at)) }}
22// Output: 'Mar 25, 2026, 9:00 AM'

Expected result: UTC timestamps from the database display in the correct local time for each user's timezone.

4

Show the user's current timezone in the UI

Display the user's detected timezone in the app so they know which timezone dates are displayed in. Use Intl.DateTimeFormat().resolvedOptions().timeZone to get the IANA timezone identifier from the user's browser. Show it in a Text component or tooltip.

typescript
1// Text component showing current timezone:
2{{ `Times shown in: ${Intl.DateTimeFormat().resolvedOptions().timeZone}` }}
3// Output: 'Times shown in: America/New_York'
4
5// Shorter display with UTC offset:
6{{ (() => {
7 const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
8 const offset = new Date().toLocaleTimeString('en-US', {
9 timeZoneName: 'short',
10 timeZone: tz
11 }).split(' ').pop();
12 return `${tz} (${offset})`;
13})() }}
14// Output: 'America/New_York (EST)'
15
16// Store user timezone in a Temporary State for use in queries:
17// In a 'Run on page load' JS Query:
18await userTimezone.setValue(Intl.DateTimeFormat().resolvedOptions().timeZone);

Expected result: The app displays the user's timezone identifier. Users understand which timezone their dates are displayed in.

5

Use moment-timezone for complex conversions via Preloaded JS

For apps with explicit timezone selection (users choose which timezone to view data in) or complex DST-aware conversions, use the moment-timezone library. Load it via App Settings → Preloaded JavaScript by adding the CDN URL. After loading, moment() and moment.tz() are available globally in all JS Queries and transformer expressions.

typescript
1// Preloaded JS URL to add:
2// https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.43/moment-timezone-with-data.min.js
3
4// After loading, available in transformers and JS Queries:
5
6// Convert UTC to a specific timezone:
7{{ moment.tz(row.event_at, 'America/Los_Angeles').format('MMM D, YYYY h:mm A z') }}
8// Output: 'Mar 25, 2026 6:00 AM PDT'
9
10// Convert to the timezone selected in a dropdown:
11{{ moment.tz(row.event_at, timezoneSelect.value).format('MMM D, YYYY h:mm A z') }}
12
13// Convert local time to UTC for storing:
14// JS Query:
15const localTime = datePicker1.value; // user's local selection
16const utcTime = moment.tz(localTime, selectedTimezone.value).utc().toISOString();
17await insertEvent.trigger({ additionalScope: { eventAt: utcTime } });

Expected result: After loading moment-timezone, all JS Queries and transformer expressions can call moment.tz() for any timezone conversion.

6

Build a timezone selector for user-configured timezone display

For apps where users want to view data in a specific timezone (regardless of their browser timezone), add a timezone Select dropdown. Create a Temporary State named displayTimezone defaulting to the browser timezone. Populate the Select with common IANA timezone options. When the user changes it, update displayTimezone and the table/text display expressions update reactively.

typescript
1// Temporary State: displayTimezone
2// Default value: {{ Intl.DateTimeFormat().resolvedOptions().timeZone }}
3
4// Timezone Select static options (common zones):
5// Label: UTC Value: UTC
6// Label: Eastern US (ET) Value: America/New_York
7// Label: Pacific US (PT) Value: America/Los_Angeles
8// Label: London (GMT) Value: Europe/London
9// Label: Paris (CET) Value: Europe/Paris
10// Label: Tokyo (JST) Value: Asia/Tokyo
11
12// Display expression in Table column or Text component:
13{{ new Intl.DateTimeFormat('en-US', {
14 timeZone: displayTimezone.value,
15 dateStyle: 'medium',
16 timeStyle: 'short',
17}).format(new Date(row.event_at)) }}
18
19// JS Query: updateDisplayTimezone
20await displayTimezone.setValue(timezoneSelect.value);

Expected result: Users can switch the display timezone and all date/time values in the app update to show the selected timezone.

Complete working example

JS Query: convertAndInsertEvent
1// convertAndInsertEvent
2// Reads from a Date Picker with 'Manage time zone' enabled
3// and inserts a UTC timestamp into PostgreSQL
4
5// Validate that a date is selected
6if (!datePicker1.value) {
7 utils.showNotification({
8 title: 'Date required',
9 description: 'Please select an event date and time.',
10 notificationType: 'warning',
11 });
12 return;
13}
14
15// datePicker1.value is already UTC because 'Manage time zone' is enabled
16const utcTimestamp = datePicker1.value;
17
18// Display what we are storing (for debugging)
19console.log('Storing UTC timestamp:', utcTimestamp);
20console.log('User timezone:', Intl.DateTimeFormat().resolvedOptions().timeZone);
21console.log('Local display:', new Date(utcTimestamp).toLocaleString());
22
23// Insert into database
24await insertEvent.trigger({
25 additionalScope: {
26 title: titleInput.value,
27 eventAt: utcTimestamp, // UTC ISO string → TIMESTAMPTZ
28 }
29});
30
31utils.showNotification({
32 title: 'Event created',
33 description: `Scheduled for ${new Date(utcTimestamp).toLocaleString()} (your local time)`,
34 notificationType: 'success',
35});
36
37form1.reset();

Common mistakes when handling Time Zones in Retool Applications

Why it's a problem: Not enabling 'Manage time zone' on Date Pickers — dates stored in the browser's local timezone cause different users to see different UTC values for the same selection

How to avoid: Enable 'Manage time zone' in Inspector → Advanced for every Date Picker. This converts the user's local selection to UTC before the value is stored.

Why it's a problem: Using TIMESTAMP (without time zone) columns in PostgreSQL instead of TIMESTAMPTZ — stored values have no timezone information, making cross-timezone queries ambiguous

How to avoid: Use TIMESTAMPTZ for all datetime columns. Migrate existing TIMESTAMP columns: ALTER TABLE events ALTER COLUMN event_at TYPE TIMESTAMPTZ USING event_at AT TIME ZONE 'UTC'.

Why it's a problem: Displaying dates without timezone conversion by using {{ row.event_at }} directly — shows the raw ISO string '2026-03-25T14:00:00.000Z' instead of a formatted local time

How to avoid: Wrap all date display expressions with new Date(row.event_at).toLocaleString() or Intl.DateTimeFormat with the timeZone option.

Why it's a problem: Assuming the browser timezone is the correct display timezone for all users in a multi-user admin app — admin users may all be in a different timezone than the data's primary timezone

How to avoid: Add a timezone selector component and store the display preference in Temporary State. Reference displayTimezone.value in all date formatting expressions.

Best practices

  • Always store dates in UTC using TIMESTAMPTZ columns — never store in local time, which makes cross-timezone queries and comparisons unreliable
  • Enable 'Manage time zone' on every Date Picker in your app — it is off by default but almost always should be on for multi-user apps
  • Display the user's timezone in the UI so users understand which timezone date values are shown in — prevents support tickets about 'wrong times'
  • Do timezone conversion only at display time (in expressions and Transformers), not at storage time — store UTC, display local
  • Use Intl.DateTimeFormat for simple timezone conversion (no library needed) and moment-timezone for DST-aware complex scenarios
  • When comparing dates across timezones in SQL, always compare in UTC — use TIMESTAMPTZ columns which PostgreSQL stores and compares in UTC internally
  • Test your app with users in at least UTC-8 (US Pacific) and UTC+9 (Japan) to catch date-boundary bugs that only appear in specific offset ranges

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I am building a Retool event scheduling app used by teams across multiple timezones. I have a Date Picker (datePicker1) for selecting event times and a Table showing events from PostgreSQL (event_at TIMESTAMPTZ). I need: (1) what 'Manage time zone' toggle does on the Date Picker and whether I should enable it, (2) a SQL INSERT that stores the UTC value correctly, (3) an expression for displaying the UTC event_at column in the user's local timezone using Intl.DateTimeFormat, (4) how to show the user's current timezone in a Text component.

Retool Prompt

My Retool app shows dates one day behind for users in US timezones. The dates are stored as TIMESTAMP (not TIMESTAMPTZ) in PostgreSQL. When a user in New York selects March 25 in the Date Picker, it stores 2026-03-24 in the database. How do I fix this? Should I enable 'Manage time zone' on the Date Picker, change the column type, or both?

Frequently asked questions

Why does my Date Picker show March 24 when I stored March 25 in the database?

This is the classic timezone offset bug. The UTC timestamp '2026-03-25T00:00:00.000Z' (midnight UTC) is March 24 at 7 PM in US Pacific time (UTC-7). Enable 'Manage time zone' on the Date Picker so Retool converts display values to your local timezone, and use TIMESTAMPTZ columns in PostgreSQL to store timezone-aware values.

Should I store user preferences for timezone or just use the browser's detected timezone?

For internal tools, browser detection is usually sufficient — team members are typically in known timezones and their browsers are correctly configured. For customer-facing tools or apps used by global teams, provide an explicit timezone selector and store the preference in the user's profile. The Intl API's detected timezone is accurate but users appreciate being able to override it.

How do I run SQL queries that filter by date range in the user's local timezone rather than UTC?

The safest approach is to filter in UTC in the SQL and let Retool handle display conversion. The Date Picker with 'Manage time zone' enabled gives you UTC values in {{ datePicker1.value }}, which you pass directly to the TIMESTAMPTZ WHERE clause. PostgreSQL compares UTC to UTC correctly regardless of what timezone the user is in.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Learning is great. Shipping is faster with help.

Our engineers have built 600+ apps on Retool and the tools around it. If your project needs to be live sooner than your learning curve allows — book a free consultation.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.