> For the complete documentation index, see [llms.txt](https://manual.bubble.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://manual.bubble.io/help-guides/data/time-dates-and-time-zones.md).

# Time, dates and time zones

## How apps handle time

Time is a fundamental part of most apps, but working with dates and times in a global, online environment comes with its own challenges. Computers don't understand human time zones, local calendars, or daylight saving time on their own. Instead, they store and process time in one consistent format that every system can refer to, wherever it is.

That format is called a *timestamp*. When your app saves a date and time, it's stored as a timestamp that isn't tied to any time zone, and it's only converted to local time when it's shown to a user. This lets users in different time zones work with the same data without confusion: the moment that was recorded stays the same, no matter where it's viewed from or whether daylight saving time has kicked in.

The challenge for app developers is that time has to be both *absolute* and *context-aware*. "3:00 PM" makes sense to a user in a given place, but your app needs to store it in a way that means the same moment to someone on the other side of the world.

### Absolute and context-aware time

Calendar events are a good way to see this in action. Say you schedule a meeting in a calendar app such as Google Calendar, Apple Calendar, or Outlook while you're in New York. You set it for 3:00 PM ET, and that's when it appears in your calendar.

If you later travel to Los Angeles, your calendar app may offer to adjust to the local time zone. If you accept, the same meeting now shows as 12:00 PM PT. The meeting itself hasn't changed: it still happens at the same moment. Only the way it's displayed has changed to match where you are.

That's what it means for time to be absolute, in that it's one fixed moment worldwide, and context-aware, in that it's shown in the viewer's local time zone. Calendar apps handle this for you, and Bubble does too.

### Quick facts about dates and times in Bubble

<table><thead><tr><th width="261.21484375">Fact</th><th>What it means</th></tr></thead><tbody><tr><td>Dates always include a time</td><td>Bubble always saves a date and a time together, as most computer systems do. You can't save just one, but there are <a href="#storing-only-a-date-or-only-a-time">workarounds</a>.</td></tr><tr><td>Dates are stored as Unix time</td><td>Each value is stored as the number of milliseconds since January 1, 1970, 00:00:00 UTC.</td></tr><tr><td>Dates are shown in the user's time zone</td><td>Bubble converts dates to the user's local time zone when displaying them, unless you've set a time zone override.</td></tr><tr><td>The server uses UTC by default</td><td>On the server, Bubble uses UTC if the user's time zone isn't available and no time zone override is set.</td></tr><tr><td>Daylight saving time changes UTC offsets</td><td>Regions that observe daylight saving time switch between two UTC offsets during the year. Eastern Time (ET), for example, is UTC-5 in winter and UTC-4 in summer, so the difference between two places can change with the season.</td></tr><tr><td>Date and time inputs use local time</td><td>Date and time pickers use the user's local time zone, unless you've set a time zone override.</td></tr></tbody></table>

### Unix time explained

To keep time consistent, Bubble stores dates in Unix time, a widely used standard that counts the milliseconds since January 1, 1970, at 00:00:00 UTC. That starting point is known as the Unix epoch.

<details>

<summary>What is UTC?</summary>

UTC (Coordinated Universal Time) is the world's reference time. It doesn't change with the seasons, and every time zone is described by how far it is from UTC, such as UTC-5 or UTC+1.

UTC is very close to Greenwich Mean Time (GMT), and for most practical purposes, the two show the same time.

</details>

Unix time is simple, it counts upward at a steady rate, and it has no time zone. A Unix timestamp always refers to the same moment, wherever the user is. That makes it easy for systems to compare, sort, and calculate with dates.

For example, the timestamp 1,700,000,000,000 is the moment 1.7 trillion milliseconds after the Unix epoch, which is November 14, 2023, at 10:13 PM UTC.

Every date and time in your Bubble database is stored this way. When a date is shown to a user, Bubble converts it to their time zone, so each user sees the right local time.

Because Unix time is so widely used, it also makes it easier for your Bubble app to exchange dates with other apps and systems, since they're all "speaking the same language."

## Time zones

Time zones exist so that clock time lines up with daylight in different parts of the world. Because the sun rises and sets at different times around the globe, time zones let people in each region keep a schedule that makes sense locally.

Each time zone is defined by its offset from UTC, such as UTC-5 for Eastern Time in winter or UTC+1 for Central European Time (CET). Some regions also observe daylight saving time (DST), which shifts local time by one hour for part of the year.

For app developers, time zones add complexity when users are spread around the world. Here's how a single timestamp is displayed in different places in winter:

<table><thead><tr><th width="170.015625">Location</th><th>Displayed time</th></tr></thead><tbody><tr><td>UTC</td><td>10:00 AM</td></tr><tr><td>Los Angeles</td><td>2:00 AM</td></tr><tr><td>New York</td><td>5:00 AM</td></tr><tr><td>Paris</td><td>11:00 AM</td></tr></tbody></table>

Because the stored value doesn't change, the conversion happens on the fly, based on each user's time zone.

Users expect times to make sense to them. If someone books a meeting for 3:00 PM, they assume it's 3:00 PM where they are, while a colleague in another time zone might see the same meeting at 9:00 PM. Without proper time zone handling, appointments can show up at the wrong time, and scheduled notifications can go out at unexpected hours.

Most modern apps detect the user's time zone from their device settings and adjust times automatically. Some situations call for a fixed time zone instead.

### When to override time zones

By default, Bubble shows dates in the time zone reported by the user's browser. That's usually the time zone the user is actually in, and it updates if they travel and reload the page.

Some apps, though, need a fixed time zone that stays the same no matter where the user is:

<table><thead><tr><th width="236.9453125">Use case</th><th>Why a fixed time zone helps</th></tr></thead><tbody><tr><td>Flight and travel booking</td><td>Users usually expect times to be shown in the time zone where the trip or event takes place. If you're in Oslo booking a tour in Tokyo, it's most helpful to see the tour time in Tokyo time.</td></tr><tr><td>Event management</td><td>Conferences and in-person events often use one fixed time zone, so every participant sees the same time. For online events, users may prefer to see the time in their own time zone, since that's where they'll join from.</td></tr><tr><td>Work shift scheduling</td><td>Global businesses may schedule shifts in the time zone of their headquarters, to keep things consistent across locations.</td></tr></tbody></table>

In these cases, you can set a time zone explicitly. Bubble then uses that time zone instead of converting to each user's local time, so everyone sees the same time.

### Overriding time zones

Bubble's timezone override controls let you choose which time zone your app uses to interpret and display dates, instead of the user's local time zone. This can be useful for scheduling, financial transactions, or logs, where everyone needs to see the same reference time.

{% hint style="info" %}
Timezone overrides are an advanced feature. In most apps, letting Bubble handle time zones automatically works well, so only use overrides when your app needs them.
{% endhint %}

#### Enabling timezone override controls

{% stepper %}
{% step %}

#### Open the general settings

Go to the **Settings** tab and open the **General** section.
{% endstep %}

{% step %}

#### Turn on the controls

Check *Enable timezone override controls*. This adds time zone properties to the places listed below.
{% endstep %}
{% endstepper %}

#### Where you can override the time zone

<table><thead><tr><th width="193.13671875">Where</th><th>What the override does</th></tr></thead><tbody><tr><td>Date/time inputs</td><td>Controls the time zone that date and time pickers use to interpret and display values. Every user picks and sees times in the same time zone, which is useful when a meeting time should read the same for everyone.</td></tr><tr><td>Pages</td><td>Applies a fixed time zone to the whole page. Every date on the page uses that time zone instead of each user's local time, which is useful for dashboards and reports that need to look the same for every user.</td></tr><tr><td>Backend workflows</td><td>Applies to date calculations inside the workflow. It doesn't change the time zone used to schedule the workflow itself (see below).</td></tr></tbody></table>

Backend workflows need a bit of extra care. If a backend workflow runs a date calculation, the override applies to that calculation. But if the workflow schedules another workflow to run at a specific time, that time is still set in the time zone of the original request.

If you don't use a time zone override, a backend workflow scheduled from a page uses the page's time zone, which is the time zone reported by the user's browser.

#### Using timezone overrides well

Before you set a time zone override, think about:

* How your users expect to see and enter times. Should an event's start time be shown in each user's local time, or in a fixed business time zone?
* Whether the override affects workflows, such as scheduled notifications or API calls that depend on exact timing.
* Testing a range of scenarios, to confirm the override works as expected for users in different locations. See Testing other time zones in your browser below.

## Storing only a date or only a time

Since every date in Bubble includes a time, you need a workaround if your app only cares about one of them. There are two common approaches: storing the value in a date field with a fixed time or date, or storing its parts in separate fields.

<table><thead><tr><th width="164.3515625">Approach</th><th>Good for</th><th>Keep in mind</th></tr></thead><tbody><tr><td>A date field</td><td>Moments in time, and anything that needs date math, like adding days, comparing dates, or calendar elements</td><td>Time zones affect how the value is displayed, so you need to store and display it in the same time zone.</td></tr><tr><td>Separate fields</td><td>Dates that should read the same everywhere, like birthdays, anniversaries, and holidays</td><td>Date operators don't work on number fields, so you combine the fields yourself to display or calculate with them.</td></tr></tbody></table>

### Using a date field

<table><thead><tr><th width="150.7421875">What you need</th><th>Workaround</th></tr></thead><tbody><tr><td>Only a date</td><td>Store the date at midnight UTC (00:00 UTC), and display it in UTC with a format that leaves out the time.</td></tr><tr><td>Only a time</td><td>Store the time on a fixed reference date, such as January 1, 1970, and display it with a format that leaves out the date. Use the same time zone when you store the value and when you display it.</td></tr></tbody></table>

{% hint style="warning" %}
The time zone you display the value in matters. If you store a date at midnight UTC but display it in each user's local time zone, users west of UTC see the evening before, so the date appears one day early. Display date-only values in the same time zone you stored them in, such as UTC.
{% endhint %}

To control how a date is displayed, use the `:formatted as` operator. It converts the date to text in the format you choose, such as only the date or only the time, while the stored value stays the same.

### Using separate fields

Some dates aren't really moments in time. A birthday on May 14 is May 14 wherever the person is, and it shouldn't turn into May 13 because someone looks at it from another time zone. The same goes for anniversaries, holidays, and other dates that belong to the calendar rather than to a specific moment.

For these, you can skip the date field type and store each part of the date in its own field. Here's what that could look like for a birthday on the User data type:

| Field       | Field type | Example |
| ----------- | ---------- | ------- |
| Birth day   | number     | 14      |
| Birth month | number     | 5       |
| Birth year  | number     | 1990    |

Since these are plain numbers, no time zone is involved. Every user sees the same values, and nothing shifts by a day. If your app doesn't need to know the year, such as for a birthday reminder, you can leave the *Birth year* field out.

{% hint style="info" %}
**Tip:** Instead of a number field for the month, you can create an option set called *Month* with one option for each month and a number attribute for its position (1–12). That gives you the month's name for display and the number for sorting and comparing, in one field.

**Article**: [Option sets](/help-guides/data/static-data/option-sets.md)
{% endhint %}

The same idea works for times. For opening hours, for example, you can store *Hour* and *Minute* as two number fields, so 9:30 reads as 9:30 for every user.

### **Setting up a birthday field**

{% stepper %}
{% step %}

#### Create the fields

In the **Data** tab, open **Data types**, select the **User** data type, and create the fields *Birth day*, *Birth month*, and *Birth year*, all of type *number*.
{% endstep %}

{% step %}

#### Collect the values

The simplest option is to give the user three inputs, such as dropdowns for the day and month and a number input for the year. Since the user picks each part directly, time zones never come into play.

If you'd rather use a date picker, you can pull out each part of its value with the `:extract` operator. The picker's value is in the user's time zone, so extracting it on the page gives you the date they picked. For the day, choose the option that returns the day of the month, not the day of the week.
{% endstep %}

{% step %}

#### Save the values

Save the three values to the user with the **Make changes to current user** action. If you collect them during signup, you can save them with *Change another field* in the **Sign the user up** action instead.
{% endstep %}

{% step %}

#### Display the date

Combine the fields in a text element, for example `Current User's Birth month` / `Current User's Birth day` / `Current User's Birth year`. If you used a *Month* option set, show the option's name instead of the number.
{% endstep %}
{% endstepper %}

### **Finding today's birthdays**

To find users who have a birthday today, add two constraints to a *Do a search for* Users:

* *Birth month* = `Current date/time:extract month`
* *Birth day* = `Current date/time:extract day`

{% hint style="warning" %}
`Current date/time` is evaluated in a time zone too. On a page, that's the user's time zone, but in a backend workflow it's UTC unless you've set a time zone override. If you send birthday messages from a scheduled backend workflow, users far from UTC may get theirs a few hours early or late.
{% endhint %}

#### **Calculating age**

Separate fields don't support date operators, but you can still work out someone's age. Subtract *Birth year* from the current year, then subtract one more if their birthday hasn't happened yet this year, meaning the current month is before *Birth month*, or it's the same month and the current day is before *Birth day*.

If your app does a lot of date math with these values, it can be easier to store a date field alongside the separate fields and use each one where it fits best.

## Testing other time zones in your browser

When your app works with dates, it's useful to see how it behaves for users in other time zones. Some browsers let you override the time zone in their developer tools, without changing your computer's settings. This is especially handy for testing scheduling features and time-sensitive logic.

### Chromium-based browsers

Chrome, Edge, Brave, Vivaldi, and other browsers built on Chromium share the same developer tools, so the steps are the same in all of them.

{% stepper %}
{% step %}

#### Open the developer tools

Press F12 or Ctrl + Shift + I on Windows, or Cmd + Option + I on Mac.
{% endstep %}

{% step %}

#### Open the Sensors panel

Click the three-dot menu in the top-right corner of the developer tools, then select **More tools** and **Sensors**.
{% endstep %}

{% step %}

#### Choose a location

In the **Location** dropdown, choose a preset city, such as Tokyo, to use its time zone. To use a different time zone, select **Other** and enter a time zone ID in the *Timezone ID* field, such as `America/New_York`.
{% endstep %}

{% step %}

#### Reload the page

Reload the page so your app picks up the new time zone.
{% endstep %}
{% endstepper %}

### Firefox and Safari

Firefox and Safari don't have a built-in way to override the time zone in their developer tools:

<table><thead><tr><th width="125.7578125">Browser</th><th>How it handles time zones</th></tr></thead><tbody><tr><td>Firefox</td><td>Turning on <code>privacy.resistFingerprinting</code> in <code>about:config</code> makes Firefox report every time zone as UTC to reduce tracking. You can't use it to choose a specific time zone.</td></tr><tr><td>Safari</td><td>Safari uses your computer's time zone. To test another time zone, you'll need to change it in your system settings.</td></tr></tbody></table>

More advanced methods, such as browser extensions or automation tools, can change the time zone in these browsers too, but they're outside the scope of this article.

## FAQ: Time, dates, and time zones

#### General questions

<details>

<summary>Does Bubble store a time zone when I save a date?</summary>

No. Bubble saves the value as Unix time, the number of milliseconds since January 1, 1970, 00:00:00 UTC. The time zone only affects how that value is interpreted and displayed.

That doesn't mean you can ignore time zones, though. When a user enters a date and time, Bubble interprets it in their time zone. Here's what happens when two users enter the same local date and time:

| User             | Enters                    | UTC offset | Stored Unix time (ms) |
| ---------------- | ------------------------- | ---------- | --------------------- |
| User in New York | January 31, 2050, 1:00 PM | UTC-5      | 2,527,264,800,000     |
| User in Sydney   | January 31, 2050, 1:00 PM | UTC+11     | 2,527,207,200,000     |

Both users entered "January 31, 2050, at 1:00 PM," but the stored values are different, because Sydney is 16 hours ahead of New York. The difference between the two values is exactly 16 hours (57,600 seconds).

</details>

<details>

<summary>Can I store just a date or just a time?</summary>

Not in a date field. Every date in Bubble is stored as Unix time, which is one exact moment, so a date field always includes both a date and a time.

Time zones don't just change the time a user sees, they can change the date too. When it's 11:00 PM on January 31 in New York, it's already 3:00 PM on February 1 in Sydney. That's why a date field works well for moments in time, like meetings and deadlines.

For dates that should read the same everywhere, like birthdays, you can store the parts in separate fields instead. See Storing only a date or only a time.

</details>

<details>

<summary>How should I store birthdays and other yearly dates?</summary>

Store the day, month, and optionally the year in separate number fields. That way, a birthday on May 14 reads as May 14 for every user, regardless of their time zone. See Using separate fields.

</details>

<details>

<summary>Why does a date show up as the day before for some users?</summary>

This usually happens when a date is stored at midnight in one time zone, such as UTC, and displayed in each user's local time zone. For users west of UTC, midnight UTC falls on the evening before, so the date appears one day early. To fix it, display the date in the same time zone you stored it in, or store it in separate fields.

</details>

<details>

<summary>Is the time zone reported by the user's browser always correct?</summary>

Usually, but not always. A few things can affect it:

| Factor           | How it affects the time zone                                                                                             |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------ |
| User settings    | The user may have set their device's clock or time zone incorrectly.                                                     |
| VPNs and proxies | Depending on how they're set up, the reported time zone may not match the user's actual location.                        |
| Older devices    | Some older devices or systems don't update time zone information correctly.                                              |
| Browser behavior | Browsers can handle time zone detection differently, and some privacy settings or extensions can change what's reported. |

If accurate times are critical in your app, consider letting users choose their time zone themselves.

</details>

#### Backend workflows and time zones

<details>

<summary>How do I set the time zone for a backend workflow request?</summary>

Include a parameter called `timezone_string` in the API request, whether the request comes from inside your Bubble app or from an external system, such as a webhook. The value needs to be a valid time zone from the [IANA Time Zone Database](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones), such as `Europe/Oslo` or `America/New_York`.

</details>

<details>

<summary>How do I set the time zone when sending a date as text to a backend workflow?</summary>

When you send a date as text for Bubble to read, you can include the time zone in the text itself. For example, instead of `12/23/21 12:05`, send `12/23/21 12:05 GMT+2`, and Bubble interprets the date in that time zone.

</details>
