How to Show Dates and Times in Each User's Time Zone in Oracle APEX

Store times as TIMESTAMP WITH LOCAL TIME ZONE, turn on Automatic Time Zone, show local, presenter and UTC times, and let users pick another time zone.

An application used in several countries has one problem with every time it shows: 15:00 means something different in Mumbai, London and New York. If the page shows the time as stored, most users must convert it in their head, and daylight saving time makes that even harder.

Oracle APEX can do the conversion for you. Store the time as TIMESTAMP WITH LOCAL TIME ZONE, turn on the application setting Automatic Time Zone, and every user sees the time in the time zone of their own browser. This guide also adds a picker, so users can view the times in any other time zone.

This guide uses webinars as the example; the same steps work for any table with a date and time. The SQL guide How to Handle Daylight Saving Time Gaps in Oracle covers time zone arithmetic in SQL; here the focus is on APEX pages showing and saving local times.

Step 1: Create the Tables

The example needs two tables. WEBINAR_PRESENTERS stores each presenter with the time zone they live in. WEBINARS stores the start time of each webinar in a TIMESTAMP(0) WITH LOCAL TIME ZONE column.

Create the tables, the index and the sample data:

Example:

set lines 200 pages 100 feedback on
create table webinar_presenters (
  presenter_id   number generated by default on null as identity constraint webinar_presenters_pk primary key,
  full_name      varchar2(100) not null,
  home_time_zone varchar2(64)  not null,
  constraint webinar_presenters_name_uk unique (full_name)
);

create table webinars (
  webinar_id   number generated by default on null as identity constraint webinars_pk primary key,
  title        varchar2(200) not null,
  presenter_id number        not null,
  starts_at    timestamp(0) with local time zone not null,
  duration_min number(3)     default on null 60 not null,
  constraint webinars_presenter_fk foreign key (presenter_id)
    references webinar_presenters (presenter_id),
  constraint webinars_duration_ck check (duration_min between 15 and 480)
);

create index webinars_presenter_ix on webinars (presenter_id, starts_at);

insert into webinar_presenters (full_name, home_time_zone) values ('Anita Rao', 'Asia/Kolkata');
insert into webinar_presenters (full_name, home_time_zone) values ('Mark Davis', 'America/New_York');
insert into webinar_presenters (full_name, home_time_zone) values ('Sophie Martin', 'Europe/London');

insert into webinars (title, presenter_id, starts_at)
values ('Getting Started with Oracle APEX', 1, timestamp '2026-10-27 15:00:00 Asia/Kolkata');
insert into webinars (title, presenter_id, starts_at, duration_min)
values ('REST Data Sources Deep Dive', 2, timestamp '2026-10-29 11:00:00 America/New_York', 90);
insert into webinars (title, presenter_id, starts_at)
values ('Faceted Search Tips', 3, timestamp '2026-11-05 14:00:00 Europe/London');
insert into webinars (title, presenter_id, starts_at, duration_min)
values ('Year-End Reporting Clinic', 2, timestamp '2026-12-08 10:00:00 America/New_York', 45);
commit;

select w.webinar_id, w.title, p.full_name, p.home_time_zone
from webinars w join webinar_presenters p on p.presenter_id = w.presenter_id
order by w.starts_at;

Output:

Table WEBINAR_PRESENTERS created.

Table WEBINARS created.

Index WEBINARS_PRESENTER_IX created.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

Commit complete.

   WEBINAR_ID TITLE                               FULL_NAME        HOME_TIME_ZONE
_____________ ___________________________________ ________________ ___________________
            1 Getting Started with Oracle APEX    Anita Rao        Asia/Kolkata
            2 REST Data Sources Deep Dive         Mark Davis       America/New_York
            3 Faceted Search Tips                 Sophie Martin    Europe/London
            4 Year-End Reporting Clinic           Mark Davis       America/New_York

4 rows selected.
  • STARTS_AT is TIMESTAMP(0) WITH LOCAL TIME ZONE. The database converts each value to the database time zone when it is saved, and back to the session time zone when it is read. The (0) drops fractions of a second.
  • The sample start times are written as literals with a region name, for example TIMESTAMP '2026-10-29 11:00:00 America/New_York'. So each row stores an exact moment, whatever the time zone of the session that inserts it.
  • WEBINARS_PRESENTER_FK allows only known presenters, WEBINARS_DURATION_CK keeps the length between 15 and 480 minutes, and WEBINAR_PRESENTERS_NAME_UK prevents duplicate names.
  • DURATION_MIN uses DEFAULT ON NULL 60, so no trigger is needed for the default length.

Step 2: See How the Session Time Zone Changes the Display

Before building any page, check the behavior in SQL. The same query runs three times, each time after ALTER SESSION SET TIME_ZONE:

Example:

set lines 200 pages 100 feedback off
alter session set nls_timestamp_format = 'DD-MON-YYYY HH24:MI';

alter session set time_zone = 'UTC';
select sessiontimezone, webinar_id, title, starts_at from webinars order by starts_at;

alter session set time_zone = 'Asia/Kolkata';
select sessiontimezone, webinar_id, title, starts_at from webinars order by starts_at;

alter session set time_zone = 'America/New_York';
select sessiontimezone, webinar_id, title, starts_at from webinars order by starts_at;

Output:

SESSIONTIMEZONE       WEBINAR_ID TITLE                               STARTS_AT
__________________ _____________ ___________________________________ ____________________
UTC                            1 Getting Started with Oracle APEX    27-OCT-2026 09:30
UTC                            2 REST Data Sources Deep Dive         29-OCT-2026 15:00
UTC                            3 Faceted Search Tips                 05-NOV-2026 14:00
UTC                            4 Year-End Reporting Clinic           08-DEC-2026 15:00

SESSIONTIMEZONE       WEBINAR_ID TITLE                               STARTS_AT
__________________ _____________ ___________________________________ ____________________
Asia/Kolkata                   1 Getting Started with Oracle APEX    27-OCT-2026 15:00
Asia/Kolkata                   2 REST Data Sources Deep Dive         29-OCT-2026 20:30
Asia/Kolkata                   3 Faceted Search Tips                 05-NOV-2026 19:30
Asia/Kolkata                   4 Year-End Reporting Clinic           08-DEC-2026 20:30

SESSIONTIMEZONE        WEBINAR_ID TITLE                               STARTS_AT
___________________ _____________ ___________________________________ ____________________
America/New_York                1 Getting Started with Oracle APEX    27-OCT-2026 05:30
America/New_York                2 REST Data Sources Deep Dive         29-OCT-2026 11:00
America/New_York                3 Faceted Search Tips                 05-NOV-2026 09:00
America/New_York                4 Year-End Reporting Clinic           08-DEC-2026 10:00

The rows are the same, but STARTS_AT follows the session. The REST webinar starts at 15:00 UTC, 20:30 in Kolkata and 11:00 in New York.

Look at the New York times of the REST webinar and the Year-End webinar. Both start at 15:00 UTC, but New York shows 11:00 in October and 10:00 in December. A region name such as America/New_York knows when daylight saving time ends, so each date gets the right offset.

Oracle APEX uses the same mechanism. It keeps a time zone for each APEX session and applies it to the database session, so TIMESTAMP WITH LOCAL TIME ZONE columns are shown in that time zone.

Step 3: Write the Report Query

The report will show three versions of each start time: the user's local time, the presenter's local time and UTC. Test the query in SQL first, with the session set to New York:

Example:

set lines 200 pages 100 feedback off
alter session set time_zone = 'America/New_York';
alter session set nls_timestamp_format = 'DD-MON-YYYY HH24:MI';
alter session set nls_timestamp_tz_format = 'DD-MON-YYYY HH24:MI TZR';
column title format a32
column presenter format a14
select w.title,
       p.full_name as presenter,
       w.starts_at,
       w.starts_at at time zone p.home_time_zone as presenter_time,
       sys_extract_utc(w.starts_at) as starts_utc
from webinars w
join webinar_presenters p on p.presenter_id = w.presenter_id
order by w.starts_at;

Output:

TITLE                               PRESENTER        STARTS_AT            PRESENTER_TIME                        STARTS_UTC
___________________________________ ________________ ____________________ _____________________________________ ____________________
Getting Started with Oracle APEX    Anita Rao        27-OCT-2026 05:30    27-OCT-2026 15:00 ASIA/KOLKATA        27-OCT-2026 09:30
REST Data Sources Deep Dive         Mark Davis       29-OCT-2026 11:00    29-OCT-2026 11:00 AMERICA/NEW_YORK    29-OCT-2026 15:00
Faceted Search Tips                 Sophie Martin    05-NOV-2026 09:00    05-NOV-2026 14:00 EUROPE/LONDON       05-NOV-2026 14:00
Year-End Reporting Clinic           Mark Davis       08-DEC-2026 10:00    08-DEC-2026 10:00 AMERICA/NEW_YORK    08-DEC-2026 15:00
  • w.starts_at follows the session time zone, so it becomes "Your Local Time" in the report.
  • w.starts_at AT TIME ZONE p.home_time_zone returns a TIMESTAMP WITH TIME ZONE in the presenter's zone. It does not depend on the viewer, so Mark Davis always sees his own 11:00.
  • SYS_EXTRACT_UTC returns the moment in UTC, which is useful when users in different countries compare times.

Step 4: Create the Report and Form Pages

In App Builder, open the application and click Create Page. On the Component tab, click Interactive Report:

Create a Page dialog with the Interactive Report page type marked with a red box
The Interactive Report page type.

Enter Page Number 640 and Name Webinars. Turn on Include Form Page, enter Form Page Number 641 and Form Page Name Webinar, and keep Form Page Mode Modal Dialog:

Create Interactive Report wizard with page 640 Webinars and form page 641 Webinar marked with red boxes
One wizard creates the report page and the form page.

Under Data Source, keep Source Type Table and select the table WEBINARS:

Data Source section with Table / View Name WEBINARS marked with a red box
The pages are based on the WEBINARS table.

Keep the Navigation settings as they are, so the wizard adds a breadcrumb and a navigation menu entry. Click Next:

Navigation section of the Create Interactive Report wizard with breadcrumb and navigation menu settings
The Navigation settings of the wizard.

On the Primary Key screen, keep WEBINAR_ID (Number) for Primary Key Column 1 and click Create Page:

Primary Key screen of the wizard with WEBINAR_ID (Number) selected and marked with a red box
WEBINAR_ID identifies each webinar.

The wizard creates page 640 with the report and page 641 with the form. It turns PRESENTER_ID into a select list with a new shared list of values, and STARTS_AT into a Date Picker with Data Type Timestamp with Local Time Zone.

Step 5: Turn On Automatic Time Zone

Go to Shared Components. Under Globalization, click Globalization Attributes:

Shared Components page with the Globalization Attributes link marked with a red box
The globalization settings of the application.

Turn on Automatic Time Zone and click Apply Changes:

Edit Globalization Attributes page with Automatic Time Zone turned on and marked with a red box
APEX now uses the time zone of each user's browser.

With this setting, the browser sends its time zone when the session starts. In this example, the login request from a New York browser carried tz=America/New_York;-4:00, and APEX stored America/New_York as the session time zone.

Because APEX stores the region name and not only the offset -4:00, the December webinar still gets the winter offset -5:00. The setting applies to the whole application, not only to the new pages.

Step 6: Show Local, Presenter and UTC Times in the Report

Open page 640 in Page Designer and select the Webinars region. Under Source, set Type to SQL Query and enter the query from Step 3, without the ORDER BY:

Example:

select w.webinar_id,
       w.title,
       p.full_name as presenter,
       w.starts_at,
       w.starts_at at time zone p.home_time_zone as presenter_time,
       sys_extract_utc(w.starts_at) as starts_utc,
       w.duration_min
from webinars w
join webinar_presenters p on p.presenter_id = w.presenter_id
Webinars region with Source Type SQL Query and the query joining WEBINARS and WEBINAR_PRESENTERS marked with a red box
The report reads three versions of each start time.

Expand Columns and select STARTS_AT. Set Heading to Your Local Time and Format Mask to DD-MON-YYYY HH24:MI. Under Source, APEX shows Timezone Aware Y for this column:

Column STARTS_AT with Heading Your Local Time and Format Mask DD-MON-YYYY HH24:MI marked with red boxes
The start time in the user's time zone.

Select PRESENTER_TIME. Set Heading to Presenter's Time and Format Mask to DD-MON-YYYY HH24:MI TZD. TZD adds the zone abbreviation, such as IST, EDT, EST or GMT:

Column PRESENTER_TIME with Heading Presenter's Time and Format Mask DD-MON-YYYY HH24:MI TZD marked with red boxes
The presenter's own time with its zone abbreviation.

Select STARTS_UTC, set Heading to UTC and Format Mask to DD-MON-YYYY HH24:MI. Set the Heading of DURATION_MIN to Duration (Min) and click Save:

Column STARTS_UTC with Heading UTC and Format Mask DD-MON-YYYY HH24:MI marked with red boxes
The same moment in UTC.

The new columns PRESENTER, PRESENTER_TIME and STARTS_UTC are not part of the default report yet. Run page 640, click Actions, Columns, and move Presenter, Presenter's Time and UTC to Display in Report, in this order:

Select Columns dialog with Presenter, Presenter's Time and UTC moved to Display in Report, marked with a red box
The report shows all time columns.

Click Apply, click the Your Local Time heading and sort ascending. Then click Actions, Report, Save Report, select As Default Report Settings, keep Primary and click Apply:

Save Default Report dialog with Default Report Type Primary marked with a red box
All users get the new columns and the sort.

Step 7: Set Up the Form Items

Open page 641 in Page Designer. Select P641_STARTS_AT, turn on Show Time and set Format Mask to DD-MON-YYYY HH24:MI, so users can enter the hour and minute:

P641_STARTS_AT with Show Time turned on and Format Mask DD-MON-YYYY HH24:MI marked with red boxes
The date picker accepts a time.

Under Source, keep Data Type Timestamp with Local Time Zone as the wizard set it. APEX then reads and writes the value in the session time zone:

P641_STARTS_AT Source with Data Type Timestamp with Local Time Zone marked with a red box
The item keeps the column's data type.

Set the Label of P641_PRESENTER_ID to Presenter and of P641_DURATION_MIN to Duration (Minutes).

Users should know which time zone they type in. Add a page item P641_TIME_ZONE to the Webinar region. Set Type to Display Only, Label to Your Time Zone, Sequence to 45, and turn off Send On Page Submit:

P641_TIME_ZONE with Type Display Only, Label Your Time Zone and Send On Page Submit off marked with red boxes
A read-only item below the start time.

Under Source, set Type to SQL Query (return single value), enter select sessiontimezone from dual, and set Used to Always, replacing any existing value in session state:

P641_TIME_ZONE Source with Type SQL Query, the query select sessiontimezone from dual and Used Always marked with red boxes
The item shows the time zone of the session.

Click Save.

Step 8: Let Users Pick a Different Time Zone

The browser time zone is the right default, but a user may want to see the times of another office. Open page 640, add a Static Content region above the report, set Title to Your Time Zone and Sequence to 5:

Region Your Time Zone with Type Static Content and Sequence 5 marked with red boxes
A small region above the report.

Add a page item P640_TIME_ZONE in this region. Set Type to Select List and Label to Show Times In.

Under List of Values, set Type to SQL Query and enter the query below. Turn on Display Extra Values and turn off Display Null Value:

Example:

select distinct tzname as d, tzname as r from v$timezone_names order by 1
P640_TIME_ZONE with Type Select List, Label Show Times In and the List of Values SQL Query on V$TIMEZONE_NAMES marked with red boxes
All region names the database knows.

Display Extra Values is on so the select list can still show a session time zone that is not a region name. In one test below, the session fell back to the offset +00:00, and the select list showed it.

Under Source, set Type to SQL Query (return single value), enter select sessiontimezone from dual and set Used to Always:

P640_TIME_ZONE Source with Type SQL Query, select sessiontimezone from dual and Used Always marked with red boxes
The select list shows the current session time zone.

Add a button to the region. Set Button Name to APPLY_TIME_ZONE, Label to Apply, and turn off Start New Row, so it sits next to the select list. Keep the action Submit Page:

Button APPLY_TIME_ZONE with Label Apply and Start New Row off marked with red boxes
The button submits the chosen time zone.

In a first test without any check, a value forced into the select list with the browser tools, Mars/Olympus, was accepted. APEX stored it as the session time zone and silently showed all times in UTC. No error appeared.

So add a validation on the Processing tab. Name it Valid Time Zone, set Type to Rows returned and enter this query:

Example:

select 1 from v$timezone_names where tzname = :P640_TIME_ZONE

Set Error Message to "Choose a time zone from the list.", Associated Item to P640_TIME_ZONE, and When Button Pressed to APPLY_TIME_ZONE:

Validation Valid Time Zone with Type Rows returned, the SQL Query, the Error Message, Associated Item and When Button Pressed marked with red boxes
Only real time zone names are accepted.

Add a process Set Session Time Zone of Type Execute Code with this PL/SQL code, and set When Button Pressed to APPLY_TIME_ZONE:

Example:

apex_util.set_session_time_zone(p_time_zone => :P640_TIME_ZONE);
Process Set Session Time Zone with Type Execute Code, the PL/SQL code and When Button Pressed APPLY_TIME_ZONE marked with red boxes
APEX_UTIL.SET_SESSION_TIME_ZONE changes the time zone of the APEX session.

Finally, add a branch Back to page 640 under After Processing, with Type Page or URL (Redirect) and Target page 640. Click Save:

Branch Back to page 640 with Behavior Type and Target page 640 marked with red boxes
The page reloads with the new time zone.

Step 9: Test the Pages in Two Time Zones

For the test, two browsers were set to different time zones, Asia/Kolkata and America/New_York, and both signed in as the same user. First, the Kolkata browser. The select list shows Asia/Calcutta and the times are Indian times:

Webinars report in a Kolkata browser with Show Times In Asia/Calcutta and the Your Local Time column marked with red boxes
The report in Kolkata.

The browser sent the older name Asia/Calcutta, not Asia/Kolkata. Both names are valid in the database and give the same times.

Click Create and enter a webinar Release Planning Workshop by Sophie Martin, starting 20-NOV-2026 18:30, for 60 minutes. The form shows that the time is entered in Asia/Calcutta:

Webinar dialog with Starts At 20-NOV-2026 18:30 and Your Time Zone Asia/Calcutta marked with a red box
The user types the time in their own time zone.

Click Create. The database stored the moment in UTC, 18:30 minus 5 hours 30 minutes:

Example:

set lines 200 pages 100 feedback off
alter session set nls_timestamp_format = 'DD-MON-YYYY HH24:MI';
column title format a28
select webinar_id,
       title,
       sys_extract_utc(starts_at) as stored_utc
from webinars
where title in ('Release Planning Workshop', 'Faceted Search Tips')
order by webinar_id;

Output:

   WEBINAR_ID TITLE                        STORED_UTC
_____________ ____________________________ ____________________
            3 Faceted Search Tips          05-NOV-2026 14:00
           21 Release Planning Workshop    20-NOV-2026 13:00

Now the New York browser. The same rows show New York times, with 11:00 for the October webinar and 10:00 for the December webinar, and the new workshop at 08:00:

Webinars report in a New York browser with Show Times In America/New_York and the Your Local Time column marked with red boxes
The report in New York.

Presenter's Time and UTC are the same in both browsers, because they do not depend on the session.

Open Faceted Search Tips in the New York browser. The form shows 05-NOV-2026 09:00 and America/New_York. Change only Duration (Minutes) to 75 and click Apply Changes:

Webinar edit dialog with Starts At 05-NOV-2026 09:00 and Your Time Zone America/New_York marked with a red box
An existing webinar opened in New York.

Saving the form in another time zone did not move the start time. It is still 14:00 UTC:

Example:

set lines 200 pages 100 feedback off
alter session set nls_timestamp_format = 'DD-MON-YYYY HH24:MI';
column title format a28
select webinar_id,
       title,
       sys_extract_utc(starts_at) as stored_utc,
       duration_min
from webinars
where title = 'Faceted Search Tips';

Output:

   WEBINAR_ID TITLE                  STORED_UTC              DURATION_MIN
_____________ ______________________ ____________________ _______________
            3 Faceted Search Tips    05-NOV-2026 14:00                 75

Next, select Asia/Tokyo in Show Times In and click Apply. The report switches to Tokyo times. The REST webinar now starts at midnight on 30 October:

Webinars report with Show Times In Asia/Tokyo and the Your Local Time column in Tokyo time marked with red boxes
The user views the times of another office.

The choice stayed when the user went to the home page and back. After signing in again, the session started with the browser time zone America/New_York.

Now the negative test. Force the value Mars/Olympus into the select list with the browser developer tools and click Apply. The validation stops it, and the times stay in New York time:

Webinars page with the error Choose a time zone from the list. under Show Times In marked with a red box
An unknown time zone name is rejected.

Things to Know

  • Only TIMESTAMP WITH LOCAL TIME ZONE values are converted. A DATE value is shown exactly as stored, in every time zone, as the query below shows.
  • SYSDATE returns the time of the database server, not of the user. CURRENT_DATE follows the session time zone.
  • Automatic Time Zone uses the time zone the browser reports. If a user wants another one, the picker in Step 8 lets them change it.
  • APEX_UTIL.SET_SESSION_TIME_ZONE lasts for the APEX session only. A new sign-in starts again with the browser time zone.
  • Store region names such as Europe/London, not fixed offsets such as +01:00. Only a region name follows daylight saving time.
  • Automatic Time Zone is an application setting, so it changes how every page of the application shows TIMESTAMP WITH LOCAL TIME ZONE columns.

Example:

set lines 200 pages 100 feedback off
alter session set nls_date_format = 'DD-MON-YYYY HH24:MI';
alter session set nls_timestamp_format = 'DD-MON-YYYY HH24:MI';

alter session set time_zone = 'America/New_York';
select sessiontimezone,
       sysdate,
       current_date,
       to_date('05-NOV-2026 14:00', 'DD-MON-YYYY HH24:MI') as date_value,
       w.starts_at
from webinars w
where w.title = 'Faceted Search Tips';

alter session set time_zone = 'Asia/Kolkata';
select sessiontimezone,
       sysdate,
       current_date,
       to_date('05-NOV-2026 14:00', 'DD-MON-YYYY HH24:MI') as date_value,
       w.starts_at
from webinars w
where w.title = 'Faceted Search Tips';

Output:

SESSIONTIMEZONE     SYSDATE              CURRENT_DATE         DATE_VALUE           STARTS_AT
___________________ ____________________ ____________________ ____________________ ____________________
America/New_York    18-OCT-2026 01:22    17-OCT-2026 21:22    05-NOV-2026 14:00    05-NOV-2026 09:00

SESSIONTIMEZONE    SYSDATE              CURRENT_DATE         DATE_VALUE           STARTS_AT
__________________ ____________________ ____________________ ____________________ ____________________
Asia/Kolkata       18-OCT-2026 01:22    18-OCT-2026 06:52    05-NOV-2026 14:00    05-NOV-2026 19:30

The database server runs in UTC, so SYSDATE shows 01:22 in both sessions. CURRENT_DATE and STARTS_AT change with the session time zone, while the DATE value stays 14:00.

Related Guides

Conclusion

Showing times in each user's time zone in Oracle APEX takes two settings: a TIMESTAMP WITH LOCAL TIME ZONE column and Automatic Time Zone in the Globalization Attributes. APEX then converts every value for display and converts typed values back when it saves them.

Add a display item so users know which zone they type in, a picker with APEX_UTIL.SET_SESSION_TIME_ZONE for viewing other zones, and a validation for the picker. Test with browsers in different time zones and check the stored UTC value in SQL, including dates on both sides of a daylight saving change.

Vinish Kapoor
Vinish Kapoor

An Oracle ACE, author of four books on Oracle APEX, SQL and PL/SQL, and Oracle Forms, and a software developer building Oracle database applications since 2001.

guest

0 Comments
Oldest
Newest Most Voted