Ir para o conteúdo principal

Procedure - Retorna tempo (dias/horas/minutos/segundos) entre datas (dias uteis)

SQL: 

CREATE OR ALTER PROCEDURE RETORNA_TEMPO_DIAS_UTEIS (DATA_INICIO TIMESTAMP, DATA_FIM TIMESTAMP, HR_INICIO_EXPEDIENTE VARCHAR(8), HR_FIM_EXPEDIENTE VARCHAR(8))
RETURNS (
DIAS INTEGER,
HORAS INTEGER,
MINUTOS NUMERIC(15,2),
SEGUNDOS NUMERIC(15,2)
)
AS
declare variable DIFERENCA integer;
    declare variable I integer;
    declare variable dif integer;
    declare variable temp_dt_ini TIMESTAMP;
    declare variable temp_dt_fim TIMESTAMP;    
begin
    i = 0;
    DIFERENCA = 0;
    DIAS = 0;
    HORAS = 0;
    MINUTOS = 0;
    SEGUNDOS = 0;    
    dif = 0;

    if (:DATA_INICIO is not null and :DATA_FIM is not null and :DATA_INICIO <= :DATA_FIM) THEN
    BEGIN
    dif = datediff(SECOND from :DATA_INICIO to :DATA_FIM); 
    if (Extract(Weekday from :DATA_INICIO) IN (1,2,3,4,5) and Extract(Weekday from :DATA_FIM) IN (1,2,3,4,5) AND dif <= 86400) THEN
    begin                 
            SEGUNDOS = :SEGUNDOS + :dif;
            MINUTOS = :SEGUNDOS / 60;
            HORAS = cast(:SEGUNDOS / 3600 as integer);    
            DIAS = cast(:SEGUNDOS / 86400 as integer);                     
            suspend;
        END    
        ELSE
        BEGIN 
        DIFERENCA = cast(:DATA_FIM as date) - cast(:DATA_INICIO as date);         
       
        if (:DIFERENCA > 0) then 
        BEGIN
            WHILE (i <= :DIFERENCA) DO
            BEGIN                                
                if (i = 0) then 
                begin
                    temp_dt_ini = :DATA_INICIO;
                end
                ELSE
                begin
                temp_dt_ini = cast(cast(:DATA_INICIO + i as date) || ' ' || :hr_inicio_expediente as TIMESTAMP);
                end
              
               if (Extract(Weekday from :temp_dt_ini) not in (0,6)) THEN
               begin 
                   if (cast(cast(:temp_dt_ini as date) || ' ' || :hr_fim_expediente as TIMESTAMP) > :DATA_FIM) then 
                       temp_dt_fim = :DATA_FIM;                       
                   else
                       temp_dt_fim = cast(cast(:temp_dt_ini as date) || ' ' || :hr_fim_expediente as TIMESTAMP);
                   
                   if (Extract(Weekday from :temp_dt_fim) not in (0,6)) then
                   begin
                       dif = datediff(SECOND from :temp_dt_ini to :temp_dt_fim);
                       :SEGUNDOS = :SEGUNDOS + :dif;                       
                   END
               END                
               i = i +1;               
            END
            MINUTOS = :SEGUNDOS / 60;
            HORAS = cast(:SEGUNDOS / 3600 as integer);    
            DIAS = cast(:SEGUNDOS / 86400 as integer);                  
            Suspend; 
        END 
end        
    END 
    ELSE
    BEGIN   
    SEGUNDOS = :SEGUNDOS;
        MINUTOS = :SEGUNDOS;
        HORAS = :SEGUNDOS;    
        DIAS = :SEGUNDOS;                 
        suspend;
    END 
END;

Parâmetros da procedure:

Entrada:

  • DATA_INICIO (timestamp) = Data inicial para cálculo do tempo. Exemplo: 2019-08-13 18:04:20
  • DATA_FIM (timestamp) = Data final para cálculo do tempo. Exemplo: 2019-08-14 23:04:20
  • HR_INICIO_EXPEDIENTE (varchar(5)) = Horário de início do expediente em formato texto. Exemplo: '08:00'
  • HR_FIM_EXPEDIENTE (varchar(5)) = Horário de fim do expediente em formato texto. Exemplo: '18:30'

***OBSERVAÇÕES: Lembrar de passar os campos esse HR_INICIO_EXPEDIENTE e HR_FIM_EXPEDIENTE campo entre aspas simples. Caso queria calcular as horas totais do dia, sem considerar o expediente, bastas digitar passar '00:00' e '23:59' nos campos HR_INICIO_EXPEDIENTE e HR_FIM_EXPEDIENTE .

Saída

  • DIAS (integer)= Tempo em dias do período informado entre DATA_INICIO e DATA_FIM.
  • HORAS (integer) = Tempo em horas do período informado entre DATA_INICIO e DATA_FIM.
  • MINUTOS (numeric) = Tempo em minutos do período informado entre DATA_INICIO e DATA_FIM.
  • SEGUNDOS (numeric) = Tempo em segundos do período informado entre DATA_INICIO e DATA_FIM.

***OBSERVAÇÕES: Os campos de saída DIAS e HORAS são arredondados (inteiros) para facilitar o uso em Painéis, relatórios, cubos, entre outros. Caso queira um valor em formato numérico. Mas usar o campo MINUTOS ou SEGUNDOS convertendo para horas e ou dias.

Chamada da procedure: 

SELECT dias, 
       horas, 
       minutos, 
       segundos 
  FROM RETORNA_TEMPO_DIAS_UTEIS ('2019-08-09 08:21:16', '2019-08-14 13:21:16','00:00:00','23:59:59')

Retorno: 

DIAS|HORAS|MINUTOS|SEGUNDOS | ----|-----|-------|---------| 3| 77|4619.95|277197.00|

Explicação do retorno: 

O tempo entre 09/08/2019 08:21:16 até 14/08/2019 13:21:16 é de 277.197 de segundos que é igual a 4619,95 de minutos que é igual a 77 de horas que é igual a 3 dias.

Detalhamento do cálculo:

  • 09/08/2019 08:21:16 até 09/08/2019 23:59:59 = 56.323 segundos
  • 10/08/2019 00:00:00 até 10/08/2019 23:59:59 = 0 segundos (não tem calculo pois é sábado)
  • 11/08/2019 00:00:00 até 11/08/2019 23:59:59 = 0 segundos (não tem calculo pois é domingo)
  • 12/08/2019 00:00:00 até 12/08/2019 23:59:59 = 86.399 segundos
  • 13/08/2019 00:00:00 até 13/08/2019 23:59:59 = 86.399? segundos
  • 14/08/2019 00:00:00 até 14/08/2019 13:21:16 = 48.076? segundos
  • Totalizando 277.197 segundos