SQL自定義函數,計算日期間隔,不包含(除去)週末和下班時間

上班時間:8:30 ~ 17:30,不包含週六週日。有點複雜,僅供參考。

-- 格式化結束時間,去掉下班(17:30)和週末。(開始時間類似做法)
create or replace function formatEndDateOnWorkDays(end_time date)
  return date
is end_date date;
  begin
    -- 轉換下班時間,轉換到有效時間段
    if end_time > to_date(to_char(end_time, 'yyyy-mm-dd') || ' 17:30:00', 'yyyy-mm-dd hh24:mi:ss')
    then end_date := to_date(to_char(end_time+1, 'yyyy-mm-dd') || ' 8:30:00', 'yyyy-mm-dd hh24:mi:ss');
    elsif end_time < to_date(to_char(end_time, 'yyyy-mm-dd') || ' 8:30:00', 'yyyy-mm-dd hh24:mi:ss')
      then end_date := to_date(to_char(end_time, 'yyyy-mm-dd') || ' 8:30:00', 'yyyy-mm-dd hh24:mi:ss');
    else
      end_date := end_time;
    end if;
    -- 轉換週末時間
    if to_char(end_date, 'd') = '1'
    then
      end_date := end_date + 1;
    elsif to_char(end_date, 'd') = '7'
      then
        end_date := end_date + 2;
    end if;
    return end_date;
  end formatEndDateOnWorkDays;
  
-- 計算相隔時間(秒),去掉非工作時間
 create or replace function getDelayTimeBySecond(start_time in date,
                                     end_time   in date,
                                     date_type  in varchar2)
  return number
is second_   number;
  holidays   number(10);
  temp_days  number(10);
  end_date   date;
  start_date date;
  temp       date;
  symbol     varchar2(1);
  begin
    if start_time is null or end_time is null
    then
      return 0;
    end if;
    end_date := formatEndDateOnWorkDays(end_time);
    -- 比較精度,秒 分 時 天
    if date_type = 'ss'
    then
      start_date := to_date(to_char(start_time, 'yyyy-mm-dd hh24:mi:ss'), 'yyyy-mm-dd hh24:mi:ss');
      end_date := to_date(to_char(end_date, 'yyyy-mm-dd hh24:mi:ss'), 'yyyy-mm-dd hh24:mi:ss');
    elsif date_type = 'mi'
      then
        start_date := to_date(to_char(start_time, 'yyyy-mm-dd hh24:mi'), 'yyyy-mm-dd hh24:mi');
        end_date := to_date(to_char(end_date, 'yyyy-mm-dd hh24:mi'), 'yyyy-mm-dd hh24:mi');
    elsif date_type = 'hh24'
      then
        start_date := to_date(to_char(start_time, 'yyyy-mm-dd hh24'), 'yyyy-mm-dd hh24');
        end_date := to_date(to_char(end_date, 'yyyy-mm-dd hh24'), 'yyyy-mm-dd hh24');
    elsif date_type = 'dd'
      then
        start_date := to_date(to_char(start_time, 'yyyy-mm-dd'), 'yyyy-mm-dd');
        end_date := to_date(to_char(end_date, 'yyyy-mm-dd'), 'yyyy-mm-dd');
    else
      start_date := start_time;
      end_date := end_date;
    end if;
    -- 沒有超時,轉換位置,防止後邊進行負數計算
    if (end_date - start_date) < 0
    then
      temp := end_date;
      end_date := start_date;
      start_date := temp;
      symbol := '-';
    else
      symbol := '';
    end if;
    -- 如果小於9小時(一天)
    if (end_date - start_date) * 24 <= 9
    then
      second_ := round((end_date - start_date) * 24 * 60 * 60);
    else
      -- 大於9小時,即過夜的,其實也就是大於15小時
      -- 得到週末天數
      select count(1) into holidays
      from (select to_char(end_date - level, 'd') DOW from dual connect by level <= (end_date - start_date))
      where DOW in (7, 1);
      -- 去掉週末時間
      end_date := (end_date - holidays);
      -- 得到整天數
      temp_days := trunc(end_date - start_date);
      -- 至少大於一天又過夜(15)的
      if (end_date - start_date - temp_days) > 0.625
      then
        -- 減去相隔天數使之小於一天,在減去過夜的15小時 + 相隔的時間,得到秒
        second_ :=
        round((((end_date - temp_days - (15 / 24)) - start_date) * 24 * 60 * 60) + (temp_days * 9 * 60 * 60));
      else
        second_ := round((((end_date - temp_days) - start_date) * 24 * 60 * 60) + (temp_days * 9 * 60 * 60));
      end if;
    end if;
    return round(symbol||second_);
  end getDelayTimeBySecond;
  
-- 格式化輸出
create or replace function getDelayTime(start_time in date,
                             end_time   in date,
                             date_type  in varchar2)
  return varchar2
is hour_  number(10);
  minute_ number(10);
  second_ number;
  result  varchar2(100);
  symbol  varchar2(1);
  begin
    second_ := getDelayTimeBySecond(start_time, end_time, date_type);
    if second_ < 0
    then
      second_ := -second_;
      symbol := '-';
    else
      symbol := '';
    end if;
    hour_ := trunc(second_ / 3600);
    minute_ := trunc(second_ / 60) - (hour_ * 60);
    second_ := second_ - (hour_ * 60 * 60) - (minute_ * 60);
    result := symbol || to_char(hour_) || '小時' || to_char(minute_) || '分' ||
              to_char(second_) || '秒';
    return result;
  end getDelayTime;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章