Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to schedule an Oracle dbms_scheduler Job timezone and DST safely

I am trying to setup a DBMS_SCHEDULER Job to run exactly at 1 AM on 1st of January every year on Oracle 11g. How to setup its attributes to be absolutely sure it wont get executed in wrong hour, because of timezone differences nor Daylight Savings Time.

I have spent plenty of time going through Oracle documentation, but I have still not reached the level of certainity.

Just btw, here are the rules which I found and consider relevant to the subject:

Job attributes

start_date This attribute specifies the first date on which this job is scheduled to start. If start_date and repeat_interval are left null, then the job is scheduled to run as soon as the job is enabled. For repeating jobs that use a calendaring expression to specify the repeat interval, start_date is used as a reference date. The first time the job will be scheduled to run is the first match of the calendaring expression that is on or after the current date. The Scheduler cannot guarantee that a job will execute on an exact time because the system may be overloaded and thus resources unavailable.

repeat_interval This attribute specifies how often the job should repeat. You can specify the repeat interval by using calendaring or PL/SQL expressions. The expression specified is evaluated to determine the next time the job should run. If repeat_interval is not specified, the job will run only once at the specified start date. See "Calendaring Syntax" for further information.

Rules in Calendaring syntax

  • The calendaring syntax does not allow you to specify a time zone. Instead the Scheduler retrieves the time zone from the start_date argument. If jobs must follow daylight savings adjustments you must make sure that you specify a region name for the time zone of the start_date. For example specifying the start_date time zone as 'US/Eastern' in New York will make sure that daylight saving adjustments are automatically applied. If instead the time zone of the start_date is set to an absolute offset, such as '-5:00', daylight savings adjustments are not followed and your job execution will be off by an hour half of the year.
  • When start_date is NULL, the Scheduler will determine the time zone for the repeat interval as follows:
  • It will check whether the session time zone is a region name. The session time zone can be set by either: Issuing an ALTER SESSION statement, for example: SQL> ALTER SESSION SET time_zone = 'Asia/Shanghai'; Setting the ORA_SDTZ environment variable.
  • If the session time zone is an absolute offset instead of a region name, the Scheduler will use the value of the DEFAULT_TIMEZONE Scheduler attribute. For more information, see the SET_SCHEDULER_ATTRIBUTE Procedure.
  • If the DEFAULT_TIMEZONE attribute is NULL, the Scheduler will use the time zone of systimestamp when the job or window is enabled.
like image 616
Javo Avatar asked Jan 15 '14 15:01

Javo


2 Answers

You may use this to make sure you pass a timestamp with time zone and that the start date will have a timezone name (US/Eastern) instead of an offset (ex: +5:00). This way, as the above fragments from the oracle docs mention, the Scheduler will keep track of DST.

-- Create a SCHEDULE

declare 
 v_start_date timestamp with time zone;
BEGIN 

select localtimestamp at time zone 'US/Eastern' into v_start_date from dual; --US/Eastern

DBMS_SCHEDULER.CREATE_SCHEDULE(
      schedule_name => 'SAMPLE_SCHEDULE',
      start_date => v_start_date,
      repeat_interval => 'FREQ=DAILY; BYHOUR=10; BYMINUTE= 15',
      comments => 'Runs daily at the specified hour.'); 
END;

To make sure you have set it properly you can run this: ALTER SESSION SET nls_timestamp_tz_format = 'MM-DD-YYYY HH24:MI:SS tzr tzd';

Now, create two schedules, one as above and one using sysdate as the start_date parameter and execute the query below.

-- Check the TIMEZONE 
select * from USER_SCHEDULER_SCHEDULES;


v1:
27-MAR-14 11.44.24.929282 AM **US/EASTERN**

v2:

27-MAR-14 05.44.54.000000 PM **+05:00**
like image 117
Catalin Filip Avatar answered Nov 03 '22 06:11

Catalin Filip


I am unsure if this answer truly passes the rules of an answer on this site, but after spending a lot of time googling I came up with the following solution:

start_date      => CAST(trunc(sysdate, 'YEAR')+2/24 AS TIMESTAMP) at time zone 'Europe/Berlin'

I believe this is closest to safest solution because:

  • It uses timestamp instead of date - i believe it forces the job to be truly executed on given time in given timezone, while ignoring DMBS_SCHEDULER default_timezone. I found also some suggestions that say that it is also unsafe to use directly timestamp, that only this cast is safe
  • I selected manually the timezone I need, with the hope, that it would not come to conflict with local settings. Altough it is unclear to me, whether it is now truly unrelated to SESSIONTIMEZONE, or DBTIMEZONE and whether it affects the proper time of run.
  • I have used a little hack, even though the request is that the job should start after midnight, I have set it to 2AM, with the hope that even in case of bad time zone and bad daylight savings it would get moved max +-2 hours.

I would be happier with the solution, if I would be absolutely clear on when the job actually gets executed with the respect of local time of a server, SESSIONTIMEZONE, DBTIMEZONE, start_date Time Zone and a DBMS_SCHEDULER time zone.

I am also unhappy with the Time Zone specification, since its has 4 abbreviations linked with it - LMT, CET, CEST, CEMT, where CEST seems to me like being completely wrong. My target is to use CET with Daylight savings(winter!=summer).

like image 35
Javo Avatar answered Nov 03 '22 05:11

Javo