/****************************************************************************/
/* SYSTEM_ID : GE                                                           */
/* TMDIFF ( FUNCTION )                                                      */
/* Created : 2014/12/13 13:30:48                                            */
/****************************************************************************/
CREATE FUNCTION TMDIFF 
FUNCTION TMDIFF(
    IN_TIME1 IN VARCHAR2  ,
    IN_TIME2 IN VARCHAR2  ,
    IN_CDKT  IN VARCHAR2  ,
    PLUS     IN NUMBER   := 0 ,
    DAYSPAN  IN VARCHAR2 := '0700-2000' )
RETURN NUMBER
IS
    RTN     NUMBER(8);      -- 計算結果を保存します。
  --DT      NUMBER(8);      -- 引数の日の時間換算の計算値を保存します。
    DTSPN   NUMBER(8);      -- DAYSPAN の差分、一日の時間換算の計算値
    DTDEL   NUMBER(8);      -- 指定の期間内の休日カレンダの休日日数
  --DTTMP   NUMBER(2);      -- 開始、終了日の休日かどうかの判定用ワーク
    DTTMP1  NUMBER(2);      -- 開始日の休日かどうかの判定用ワーク
    DTTMP2  NUMBER(2);      -- 終了日の休日かどうかの判定用ワーク
    DT1     DATE;           -- 開始日の年月日の日付型
    DT2     DATE;           -- 終了日の年月日の日付型
    TM1     NUMBER(4)  := 0;
    TM2     NUMBER(4)  := 0;
    VTM1    NUMBER(4)  := 0;        -- DAYSPAN の 前半部
    VTM2    NUMBER(4)  := 0;        -- DAYSPAN の 後半部
    TIME1   VARCHAR2(14) ;
    TIME2   VARCHAR2(14) ;

    FUNCTION TO_TIME( TIM IN VARCHAR2 ) RETURN NUMBER IS
    BEGIN
        RETURN TO_NUMBER( SUBSTRB( TIM,1,2 ) )*100 + TO_NUMBER( SUBSTRB( TIM,3,2 ) )*100/60 ;
    END TO_TIME;

BEGIN
    -- IN_TIME1 と IN_TIME2 の大小比較：以降は、TIME2 の方が大きい値とする。
    IF IN_TIME1 > IN_TIME2 THEN
        TIME1 := IN_TIME2; TIME2 := IN_TIME1;
    ELSE
        TIME1 := IN_TIME1; TIME2 := IN_TIME2;
    END IF;

    -- 日付のみ指定の場合は、表示開始時刻を追加しておきます。
    IF LENGTH( TIME1 ) = 8 THEN TIME1 := TIME1 || SUBSTRB(DAYSPAN,1,4); END IF;
    IF LENGTH( TIME2 ) = 8 THEN TIME2 := TIME2 || SUBSTRB(DAYSPAN,1,4); END IF;

    -- １日分の表示時間間隔を求める。マイナスの場合は、２４時間分加算されます。
    -- 開始、終了時刻が同じ場合(0700-0700)は、１日まるごとと計算されます。
    VTM1 := TO_TIME( SUBSTRB(DAYSPAN,1,4) ) ;
    VTM2 := TO_TIME( SUBSTRB(DAYSPAN,6,4) ) ;

    -- 範囲内に存在する日数を求めます。
  --DT := TO_DATE( SUBSTRB(TIME2,1,8),'YYYYMMDD' ) - TO_DATE( SUBSTRB(TIME1,1,8),'YYYYMMDD' ) ;
    DT1 := TO_DATE( SUBSTRB(TIME1,1,8),'YYYYMMDD' ) ;
    DT2 := TO_DATE( SUBSTRB(TIME2,1,8),'YYYYMMDD' ) ;

    -- それぞれの時間を求めておきます。
    TM1 := TO_TIME( SUBSTRB( TIME1,9,4 ) ) ;
    TM2 := TO_TIME( SUBSTRB( TIME2,9,4 ) ) ;

    -- 開始時刻と終了時刻の２４時間変換を行います。
    -- 開始時刻と終了時刻がともに表示開始時刻と表示終了時刻の範囲内の場合で、開始時刻、または、
    -- 終了時刻が表示開始時刻よりも小さい場合は、2400を加算して、日付をマイナス１します。
    IF VTM1 >= VTM2 THEN
      --IF TM1 <  VTM2 THEN TM1 := TM1 + 2400; DT := DT +1; END IF;
        IF TM1 <  VTM2 THEN TM1 := TM1 + 2400; DT1 := DT1 -1; END IF;
      --IF TM2 <= VTM2 THEN TM2 := TM2 + 2400; DT := DT -1; END IF;
        IF TM2 <= VTM2 THEN TM2 := TM2 + 2400; DT2 := DT2 -1; END IF;
        VTM2 := VTM2 + 2400;
    END IF;
    DTSPN := VTM2 - VTM1 ;

    -- 開始時刻が表示開始時間より小さい場合は、表示開始時間に変換します。
    IF TM1 < VTM1 THEN TM1 := VTM1; END IF;

    -- 表示終了時刻よりも大きい場合は、表示終了時刻にあわせます。これを変換済み開始時刻とします。
    IF TM1 > VTM2 THEN TM1 := VTM2; END IF;

    -- 同様に、終了時刻も、表示開始時刻より小さい場合は、表示開始時刻に、
    IF TM2 < VTM1 THEN TM2 := VTM1; END IF;

    -- 表示終了時刻よりも大きい場合は、表示終了時刻にあわせます。これを変換済み終了時刻とします。
    IF TM2 > VTM2 THEN TM2 := VTM2; END IF;

  /* +++++++++++++++ 2006年03月25日 Comment Out +++++++++++++++++++++
    -- 休日カレンダ(WI008)より、範囲内の休日数を求める。
    -- 開始日が休日の場合は、時間を表示終了時刻に設定する。
    SELECT COUNT(*) INTO DTTMP FROM WI008
    WHERE CDKT = IN_CDKT
    AND YMD = SUBSTRB(TIME1,1,8)
    AND HOL = '1' ;

    IF DTTMP > 0 THEN
        DT := DT -1;
        TM1 := VTM1;
    END IF;

    -- 休日カレンダ(WI008)より、範囲内の休日数を求める。
    -- 終了日が休日の場合は、時間を表示開始時刻に設定する。
    SELECT COUNT(*) INTO DTTMP FROM WI008
    WHERE CDKT = IN_CDKT
    AND YMD = SUBSTRB(TIME2,1,8)
    AND HOL = '1' ;

    IF DTTMP > 0 THEN
        TM2 := VTM1;
    END IF;

    -- 休日カレンダ(WI008)より、範囲内の休日数を求める。
    -- ここでは、開始、終了日は、計算済みなので、日付範囲に = を含めません。
    SELECT COUNT(*) INTO DTDEL FROM WI008
    WHERE CDKT = IN_CDKT
    AND YMD > SUBSTRB(TIME1,1,8)
    AND YMD < SUBSTRB(TIME2,1,8)
    AND HOL = '1' ;
  ++++++++++++++++++ 2006年03月25日 Comment Out ++++++++++++++++++ */

    SELECT  COUNT(CASE WHEN A.YMD > B.DYSTR AND A.YMD < B.DYEND THEN 1 END)
        ,   NVL(MAX(DECODE(A.YMD, B.DYSTR, 0)), 1)
        ,   NVL(MAX(DECODE(A.YMD, B.DYEND, 0)), 1)
    INTO    DTDEL
        ,   DTTMP1
        ,   DTTMP2
    FROM WI008 A
    ,   (SELECT TO_CHAR(DT1, 'YYYYMMDD') DYSTR, TO_CHAR(DT2, 'YYYYMMDD') DYEND FROM DUAL) B
    WHERE A.CDKT = IN_CDKT
    AND   A.YMD >= B.DYSTR
    AND   A.YMD <= B.DYEND
    AND   A.HOL = '1' ;

    -- TM2 と TM1 の引き算は、そのまま行います。24時を回っている日付は、日数の加算済みです。
  --RTN := ( DT - DTDEL ) * DTSPN + ( TM2 - TM1 ) + PLUS*100/60 ;
    --GREATEST(DT2 - DT1 -1, 0)  開始日と終了日の間の日数(開始日と終了日を含まない)
    --SIGN(DT2 -DT1) * DTTMP1    開始日の開始時刻と終了日の開始時刻の日数
    RTN := ( GREATEST(DT2 - DT1 -1, 0) + SIGN(DT2 - DT1) * DTTMP1 - DTDEL) * DTSPN
         + ( (TM2 - VTM1) * DTTMP2 - (TM1 - VTM1) * DTTMP1 )
         + PLUS*100/60 ;

    RETURN RTN;

END TMDIFF;
/
