一个简短的问题:
我正在使用此代码
SELECT trunc (SYSDATE) - TO_DATE('04/10/2015','mm/dd/yyyy') Differenz FROM DUAL;
计算两个日期之间的差异。
我想补充一点,它忽略了周六,周日和节假日(不是学校假期,我的意思是圣诞节,东部假期。)我的当地时区是德国(每个国家都有自己的假期)。
感谢帮助!
获得排除周六和周日的天数并不是那么困难,您可以在SO上找到针对此问题的几种解决方案。
考虑假期更具挑战性。一种解决方案是使用Oracle SCHEDULER。默认情况下,它用于SCHEDULER JOBS,但是我不认为没有任何理由不将其用于其他目的。
SCHEDULER
SCHEDULER JOBS
最大的问题是复活节,请参阅此处:Computus。我认为最有效的方法是对日期进行硬编码并手动进行维护。
BEGIN DBMS_SCHEDULER.CREATE_SCHEDULE('New_Year', repeat_interval => 'FREQ=YEARLY;BYDATE=0101'); DBMS_SCHEDULER.CREATE_SCHEDULE('Easter_Sunday', repeat_interval => 'FREQ=YEARLY;BYDATE=20150405, 20160327, 20170416, 20170416, 20180401, 20190421, 20200412', comments => 'Hard coded till 2020'); DBMS_SCHEDULER.CREATE_SCHEDULE('Good_Friday', repeat_interval => 'FREQ=YEARLY;BYDATE=20150405-2D, 20160327-2D, 20170416-2D, 20170416-2D, 20180401-2D, 20190421-2D, 20200412-2D'); DBMS_SCHEDULER.CREATE_SCHEDULE('Easter_Monday', repeat_interval => 'FREQ=YEARLY;BYDATE=20150405+1D, 20160327+1D, 20170416+1D, 20170416+1D, 20180401+1D, 20190421+1D, 20200412+1D'); DBMS_SCHEDULER.CREATE_SCHEDULE('Ascension_Day', repeat_interval => 'FREQ=YEARLY;BYDATE=20150405+39D,20160327+39D,20170416+39D,20170416+39D,20180401+39D,20190421+39D,20200412+39D'); DBMS_SCHEDULER.CREATE_SCHEDULE('Pentecost_Monday', repeat_interval => 'FREQ=YEARLY;BYDATE=20150405+50D,20160327+50D,20170416+50D,20170416+50D,20180401+50D,20190421+50D,20200412+50D'); DBMS_SCHEDULER.CREATE_SCHEDULE('Repentance_and_Prayer', repeat_interval => 'FREQ=DAILY;BYDATE=1122-SPAN:7D;BYDAY=WED', comments => 'Wednesday before November 23th, Buss- und Bettag'); -- alternative solution: --DBMS_SCHEDULER.CREATE_SCHEDULE('Repentance_and_Prayer', repeat_interval => 'FREQ=MONTHLY;BYMONTH=NOV;BYDAY=3 WED', -- comments => '3rd Wednesday in November'); DBMS_SCHEDULER.CREATE_SCHEDULE('Labor_Day', repeat_interval => 'FREQ=YEARLY;BYDATE=0501'); DBMS_SCHEDULER.CREATE_SCHEDULE('German_Unity_Day', repeat_interval => 'FREQ=YEARLY;BYDATE=1003'); DBMS_SCHEDULER.CREATE_SCHEDULE('Christmas', repeat_interval => 'FREQ=YEARLY;BYDATE=1225+SPAN:2D'); DBMS_SCHEDULER.CREATE_SCHEDULE('Christian_Celebration_Days', repeat_interval => 'FREQ=DAILY;INTERSECT=Easter_Sunday,Good_Friday,Easter_Monday,Ascension_Day,Pentecost_Monday,Repentance_and_Prayer,Christmas'); -- alternative solution: -- DBMS_SCHEDULER.CREATE_SCHEDULE('Christian_Celebration_Days', repeat_interval => 'FREQ=Good_Friday;BYDAY=1 MON, 6 THU,8 MON'); DBMS_SCHEDULER.CREATE_SCHEDULE('Political_Holidays', repeat_interval => 'FREQ=DAILY;INTERSECT=New_Year,Labor_Day,German_Unity_Day'); END; /
在此处查看日历的语法:日历语法
然后,您可以使用以下时间表:
CREATE OR REPLACE FUNCTION DateDiff(end_date IN TIMESTAMP) RETURN INTEGER AS next_run_date TIMESTAMP := TRUNC(SYSTIMESTAMP); res INTEGER := 0; BEGIN IF end_date > SYSTIMESTAMP THEN LOOP DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING('FREQ=DAILY;INTERVAL=1;BYDAY=MON,TUE,WED,THU,FRI; EXCLUDE=Christian_Celebration_Days,Political_Holidays', NULL, next_run_date, next_run_date); EXIT WHEN next_run_date >= end_date; res := res + 1; END LOOP; RETURN res; ELSE RAISE VALUE_ERROR; END IF; END; SELECT DateDiff(TO_DATE('04/10/2015','mm/dd/yyyy')) AS Differenz FROM DUAL;
输出接下来的20个测试文件:
DECLARE next_run_date TIMESTAMP; BEGIN FOR i IN 1..20 LOOP DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING('FREQ=DAILY;INTERSECT='Christian_Celebration_Days,Political_Holidays', NULL, next_run_date, next_run_date); DBMS_OUTPUT.PUT_LINE(next_run_date); END LOOP; END;
更新
我什至找到了一个更紧凑的版本:
BEGIN -- Start with first celebration day (good Friday), all dependent celebration days have to be after this day for proper calculation of schedule DBMS_SCHEDULER.CREATE_SCHEDULE('GOOD_FRIDAY', repeat_interval => 'FREQ=YEARLY;BYDATE=20100402,20110422,20120406,20130329,20140418,20150403,20160325,20170414,20180330,20190419,20200410,20210402,20220410,20230407,20240329,20250418,20260403,20270326,20280414,20290330,20300419', comments => 'Hard coded 2010 to 2030'); -- Easter Sunday can be skipped for list of holidays, otherwise 'FREQ=Good_Friday;BYDAY=1 SUN+SPAN:2D' DBMS_SCHEDULER.CREATE_SCHEDULE('EASTER_MONDAY', repeat_interval => 'FREQ=Good_Friday;BYDAY=1 MON', comments => '1st Monday after Good Friday' DBMS_SCHEDULER.CREATE_SCHEDULE('ASCENSION_DAY', repeat_interval => 'FREQ=Good_Friday;BYDAY=6 THU', comments => '6th Thursday after Good Friday (40 days after Easter)'); -- Pentecost Sunday can be skipped for list of holidays, otherwise 'FREQ=Good_Friday;BYDAY=8 SUN+SPAN:2D' DBMS_SCHEDULER.CREATE_SCHEDULE('PENTECOST_MONDAY', repeat_interval => 'FREQ=Good_Friday;BYDAY=8 MON', comments => '8th Monday after Good Friday (50 days after Easter)'); DBMS_SCHEDULER.CREATE_SCHEDULE('EASTER_RELATED_DAYS', repeat_interval => 'FREQ=Good_Friday;BYDAY=1 MON, 6 THU,8 MON' END;