Oracle 計算兩個日期間時間排除非工作日及非工作時間精確至分鍾


客戶要求計算兩個時間點之間工作了多少時間,時間上要排除非工作日、節假日、非工作時間。非工作時間設定為除9點到18點之外的。

在網上搜索一通終於到相類似的情況了,鏈接為:http://bbs.csdn.net/topics/360105287

網上計算時間點時是自己建了一張節假日的表,好在客戶這里另外的系統上有全部的節假日信息,不需要在建表進行維護。用戶這邊的節假日表是將全年每一天的信息都維護進去,工作日日期標示為1,非工作日日期標示為2.相對簡單點。

以下為具體代碼:

create or replace function wk_hours_between(
  i_startTime varchar2, -- 起始時間:( 格式:'YYYY-MM-DD HH24:MI:SS' )
  i_endTime varchar2    -- 結束時間:( 格式:'YYYY-MM-DD HH24:MI:SS' )
)
return number
is
  v_real_startTime date;--開始時間 變量
  v_real_endTime date;--結束時間 變量
  v_hours number(18,0);--計算結果
  v_number number(18,4);
begin
  --格式轉換
  v_real_startTime := to_date(i_startTime,'YYYY-MM-DD HH24:MI:SS');
  v_real_endTime := to_date(i_endTime,'YYYY-MM-DD HH24:MI:SS');
  --開始時間及結束時間轉換 
  if v_real_startTime > v_real_endTime -- 如果起始時間大於結束時間,將起始時間與結束時間替換
  then
    select v_real_startTime, v_real_endTime into v_real_endTime, v_real_startTime from dual;
  end if;

  if v_real_startTime<trunc(v_real_startTime,'dd')+9/24 -- 如果小於當天9點,將其置為當天9點(因為你是9點上班)
  then
    v_real_startTime:=trunc(v_real_startTime,'dd')+9/24;
  /*-- 如果大於當天12點,且小於當天14點,將其置為當天14點(因為你下午是14點上班)
  elsif v_real_startTime>trunc(v_real_startTime,'dd')+12/24 and v_real_startTime<trunc(v_real_startTime,'dd')+14/24
  then
    v_real_startTime:=trunc(v_real_startTime,'dd')+14/24;*/
  -- 如果大於當天18點,將其置為第二天9點
  elsif v_real_startTime>trunc(v_real_startTime,'dd')+18/24
  then
    v_real_startTime:=trunc(v_real_startTime+1,'dd')+9/24;
  end if;

  -- 如果小於當天9點,將其置為昨天18點
  if v_real_endTime<trunc(v_real_endTime,'dd')+9/24
  then
    v_real_endTime:=trunc(v_real_endTime-1,'dd')+18/24;
 /* -- 如果大於當天12點,且小於當天14點,將其置為當天12點(因為你上午是12點下班)
  elsif v_real_endTime>trunc(v_real_endTime,'dd')+12/24 and v_real_endTime<trunc(v_real_endTime,'dd')+14/24
  then
    v_real_endTime:=trunc(v_real_endTime,'dd')+12/24;*/
  elsif v_real_endTime>trunc(v_real_endTime,'dd')+18/24 -- 如果大於當天18點,將其置為當天18天(因為你是18點下班)
  then
    v_real_endTime:=trunc(v_real_endTime,'dd')+18/24;
  end if;
  --with tem as 語句 將后面語句查詢結果賦值於tem  使用於多次查詢的語句,可簡化
  with a as( select v_real_startTime+(level-1)/24 as cdate,
                    trunc(v_real_startTime+(level-1)/24,'hh') as tr_cdate
               from dual
            connect by level <= (v_real_endTime-v_real_startTime)*24+2 ),--connect by level 遞歸查詢 按小時對時間進行拆分
            --對a中內容進行篩選,排除非工作日的    to_char 'D' 得到所在所在周內的第幾天
       b as( select cdate, tr_cdate cdate2 from a where trunc(a.cdate) not in (select hdate from holidays) and to_char(a.cdate,'D') not in ('1','7') ), -- 排除周六、日 和 節假日
       --對b中時間點進行轉換  主要以工作時間09——18來進行
       c as( select (case when to_char(t1.cdate,'hh24') in ('18') then trunc(t1.cdate,'hh') else t1.cdate end) as cdate1, --超過18點的按18點
                     (case when to_char(t1.cdate2,'hh24') in ('09') then trunc(t2.cdate+1/24,'hh24') else t2.cdate end) as cdate2 --超過9點的按9點
             from b t1
             left join b t2 on trunc(t1.cdate,'hh24')=trunc(t2.cdate+1/24,'hh24')
            order by (case when to_char(t1.cdate,'hh') in ('09','18') then trunc(t1.cdate,'hh') else t1.cdate end) ),
            --上一步將超過9點和18點的轉換成了整點,此步將整點的字段值信息進行調整
       --對c表中信息進行篩選,選取在工作時間點上的來計算
       d as ( select (case when c.cdate1>v_real_endTime then v_real_endTime
                           else c.cdate1 end) as cdate1,
                      c.cdate2
                from c
               where to_char(c.cdate1,'hh24') in ('10','11','12','13','14','15','16','17','18') )
      --*1440為時間進度計算到分鍾
      select nvl(sum(d.cdate1-nvl(d.cdate2,d.cdate1))*1440,0) into v_number from d;

  return v_number;
end;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM