In Belgium we are switching to wintertime this Sunday, which is good opportunity for me to write this post.
I normally intended to write it when we switched to summer time, so everything will be from the point of view of changing from winter time to summer time (confused yet? ).
The reason that I wanted to write about it, where some alerts we got back then from our monitoring considering scheduler jobs which where no longer running on time.
Quickly it became clear that these jobs did not follow the change to summer time, but instead ran an hour later.
The key is to look at the dba_scheduler_jobs table in the correct format. You see, the *_run_date columns are of the datatype “timestamp(6) with timezone”, so to get all the information you need to use the right format model. Using the TZR and TZD models you can respectively see the timezone and the daylight saving information:
sys@WPS50> select job_name, to_char(last_start_date, 'DD/MM/YYYY HH24:MI:SS "TZ:" TZR "DS:" TZD ') last_start_date, to_char(next_run_date, 'DD/MM/YYYY HH24:MI:SS "TS:" TZR "DS:" TZD ') next_run_date from dba_scheduler_jobs;
JOB_NAME LAST_START_DATE NEXT_RUN_DATE
------------------------------ -------------------------------------------------- --------------------------------------------------
AUTO_SPACE_ADVISOR_JOB 28/03/2009 06:00:04 TZ: +01:00 DS:
GATHER_STATS_JOB 02/02/2009 22:00:00 TZ: +01:00 DS:
FGR$AUTOPURGE_JOB
PURGE_LOG 29/03/2009 03:00:00 TZ: MET DS: MEST 30/03/2009 03:00:00 TS: MET DS: MEST
ANALYZETHIS_PURGEHISTORY 29/03/2009 17:00:00 TZ: +01:00 DS: 30/03/2009 17:00:00 TS: +01:00 DS:
GATHER_WK_TEST_STATS 29/03/2009 18:00:00 TZ: +01:00 DS: 30/03/2009 18:00:00 TS: +01:00 DS:
GATHER_SESSIONUSR_STATS 29/03/2009 18:00:00 TZ: +01:00 DS: 30/03/2009 18:00:00 TS: +01:00 DS:
GATHER_RELEASEUSR_STATS 29/03/2009 18:00:00 TZ: +01:00 DS: 30/03/2009 18:00:00 TS: +01:00 DS:
GATHER_LMDBUSR_STATS 29/03/2009 18:00:00 TZ: +01:00 DS: 30/03/2009 18:00:00 TS: +01:00 DS:
GATHER_ICMADMIN_STATS 29/03/2009 18:00:00 TZ: +01:00 DS: 30/03/2009 18:00:00 TS: +01:00 DS:
GATHER_COMMUNITYUSR_STATS 29/03/2009 18:00:00 TZ: +01:00 DS: 30/03/2009 18:00:00 TS: +01:00 DS:
GATHER_CUSTOMIZATIONUSR_STATS 29/03/2009 18:00:00 TZ: +01:00 DS: 30/03/2009 18:00:00 TS: +01:00 DS:
MGMT_STATS_CONFIG_JOB 01/03/2009 01:01:01 TZ: +01:00 DS: 01/04/2009 01:01:01 TS: +01:00 DS:
MGMT_CONFIG_JOB 28/03/2009 06:00:04 TZ: +01:00 DS:
14 rows selected.
(note the additional space after the TZD format, I needed to add this to actually show the information if I used the "DS:" litteral in front (probably this is a bug) )
As you can see, each job has its own timezone offset and some have also daylight saving information.
So, what happened with our jobs? Well, when a job gets created, Oracle stores the timezone information of the start_date parameters. If this timezone is specified in an absolute offset then no daylight saving changes are applied.
When the server switches to summer time (GMT +2 in Belgium), the scheduler job stays in its own little world and remains in the timezone GMT +1.
So, when for the rest of the database the time is 07:00, the job thinks it is still 06:00 and does not start. As the monitoring check did not take the timezone of the job in account, it reported the job as being late.
To avoid this situation, you need to use a named timezone, in which case oracle will apply automatically the correct daylight saving settings.
How do you do this? Well either you use the to_timestamp_tz to convert a text string to a timestamp with timezone information or Oracle retrieves the timezone from your session.
The timezone information in your session can be set with alter session, or by using the ORA_SDTZ variable in your client environment.
But there is a catch. In the following example I have set my timezone to Europe/Brussels, and then verified the timezone information in systimestamp:
sys@WPS50> select sessiontimezone from dual;
SESSIONTIMEZONE
---------------------------------------------------------------------------
Europe/Brussels
sys@WPS50> select to_char(systimestamp, 'DD/MM/YYYY HH24:MI:SS "TZ:" TZR "DS:" TZD ') from dual;
TO_CHAR(SYSTIMESTAMP,'DD/MM/YYYYHH24:MI:SS"TZ:"TZR"DS:"TZD')
--------------------------------------------------------------------
30/03/2009 01:57:15 TZ: +02:00 DS:
As you can see, the timezone part an absolute notation, not a named timezone.
Systimestamp will never use the named timezone notation, so whenever you use systimestamp as value for the next_date parameter in dbms_scheduler, you will use an absolute offset and thus not follow daylight saving switches.
The current_timestamp variable, will however use the correct notation:
sys@WPS50> select to_char(current_timestamp, 'DD/MM/YYYY HH24:MI:SS "TZ:" TZR "DS:" TZD ') from dual;
TO_CHAR(CURRENT_TIMESTAMP,'DD/MM/YYYYHH24:MI:SS"TZ:"TZR"DS:"TZD')
--------------------------------------------------------------------
30/03/2009 01:57:18 TZ: EUROPE/BRUSSELS DS: CEST
So, when you want to specify the current date as value for the next_date parameter, use current_timestamp and not systimestamp.
This timezone stuff is only applicable when you have an interval that is at least 1 day. With smaller intervals, Oracle will make sure that the period between 2 runs remain the same.
If a job runs every 3 hours and last ran on midnight and the clock is then moved forward from 02:00 to 03:00, then the next run date of the job becomes 04:00, so that the 3 hour period between two job runs is retained.
More information on this, including how Oracle behaves when no start_date parameter is given can be found here:
The database version on which the tests where done is 10.2.0.4