Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
Boa tarde pessoal,
basicamente eu preciso do retorno de horas entre duas datas, porém tenho condições a tratar .
basicamente tenho 2 colunas ( dt_fim e dt_ini ) que representam data final e data inicial. Preciso da diferença entre as duas retornada em uma outra coluna (hr_ausencias) , porém a cada dia posso computar no máximo 9 horas, e desconsiderar finais de semana e feriados( esses cadastrados em uma tabela) . Seria melhor tratar isso com uma Trigger , um Script PL/SQL , como me sugerem? Agradeço quem puder ajudar.
Bom dia Motta, analisei e ambas soluções são inviáveis para mim , eu precisaria de algo mais específico , como por exemplo : ( suponha que tenho minha data inicial no dia 31/05/2019 08:00 e a data final no dia 03/06/2019 12:00 . Nesse caso seria computado no máximo 9 horas por dia entre as datas, e desconsiderado os finais de semana e feriados , assim ao final eu teria como resultado as horas que necessito ).
Existem diversas function que tratam dias úteis me parece fácil de adaptar.
http://glufke.net/oracle/viewtopic.php?f=3&t=904
https://forum.imasters.com.br/topic/406953-resolvido feriados-móveis/
Assim, eu já vi essas várias functions , blocos e etc que tem por aí, inclusive eu tenho uma que adaptei pra me retornar a quantidade de horas estimadas a trabalhar em um mês. Meu problema esta mais em fixar um valor máximo por período entre as 2 variáveis, por exemplo DT_INI (28/05/2019 08:00 ) DT_FIM ( 30/05/2019 09:00) . num calculo direto isso daria 49horas. eu preciso que volte 19 horas ( 9horas do dia 28, 9horas do dia 29 e 1 hora do dia 30), mas to me embaralhando pra montar o script nessa parte. Se puder me dar um exemplo parecido ou um caminho pra começar isso eu agradeço .
Eu faria um FUNCTION somente para isto , retornando as "horas úteis" entre duas datas levando em conta todas as regras.
Não é simples mas também não é um biche de sete cabeças tendo conhecimento de plsql
Tendo dúvidas vai postando aí.
eu tentei uma trigger mas deu meio errado . Vou tentar pela function , só preciso pensar um pouco mais nas regras internas ( por exemplo a de contar as horas no dia e travar após passar e contar o restante dos dias) . Não sei se aqui no forum tem algum específico pra Apex, é que eu utilizo ele e seria pra aplicar dentro de um item de formulario que eu estou montando o script.
Obrigado pela ajuda, qualquer coisa eu volto aqui .
Montei um script que está funcionando , mas notei que a conta das horas internamente não está como eu preciso, não estou conseguindo finalizar essa parte, poderia verificar e me auxiliar nesse pedaço? Tipo ele calcula as horas mas sempre um valor grande, eu preciso que dê as horas exatas , por exemplo se a DT_FIM = '30/05/2019 16:00' e DT_INI = '30/05/2019 18:00' o retorno de horas ao final na variável precisa ser 2 .
segue o scritp: (deixei os output pra validar os resultados , e os comentarios nos ifs pra facilitar compreensao)
DECLARE
VA_CONTA NUMBER := 0;
VA_DT_INI DATE;
VA_DT_FIM DATE;
VA_QT_DIAS NUMBER := 0;
VA_DT_ATUAL DATE;
VA_FERIADO_SN NUMBER := 0;
VA_TOT_HR_AUS NUMBER:= 0;
VA_HR_AUS_SEXTA NUMBER:= 0;
VA_TOT_DIAS NUMBER :=0;
VA_TOT_SEXTA NUMBER := 0;
BEGIN
select TRUNC(TO_DATE('31/05/2019 08:00', 'DD/MM/RRRR HH24:MI')) -
TRUNC(TO_DATE('14/05/2019 09:00', 'DD/MM/RRRR HH24:MI')),
TO_DATE('14/05/2019 09:00', 'DD/MM/RRRR HH24:MI'),
TO_DATE('31/05/2019 08:00', 'DD/MM/RRRR HH24:MI')
INTO VA_QT_DIAS, VA_DT_INI, VA_DT_FIM
from DUAL;
/* select DISTINCT (TRUNC(A.DT_FIM) - TRUNC(A.DT_INI)),
A.DT_INI,
A.DT_FIM
INTO VA_QT_DIAS,
VA_DT_INI,
VA_DT_FIM
FROM AUSENCIAS A
WHERE CD_AUSENCIA = 174;*/
DBMS_OUTPUT.put_line('Dia inicial: ' || VA_DT_INI);
DBMS_OUTPUT.put_line('Dia final: ' || VA_DT_FIM);
DBMS_OUTPUT.put_line('total dias: ' || VA_QT_DIAS);
DBMS_OUTPUT.put_line('------------------');
WHILE VA_CONTA <= VA_QT_DIAS LOOP -- VERIFICA CONTADOR MENOR QUE TOTAL DE DIAS ENTRE AS DATAS (INI E FIM)
VA_DT_ATUAL := VA_DT_INI + VA_CONTA;
IF VA_DT_ATUAL <= VA_DT_FIM THEN --1 VERIFICA SE DATA DO LAÇO MENOR QUE DATA FINAL
SELECT COUNT(*) INTO VA_FERIADO_SN FROM FERIADOS WHERE TRUNC(DT_FERIADO) = TRUNC(VA_DT_ATUAL);
-- DBMS_OUTPUT.PUT_LINE('ATUAL:' || VA_DT_ATUAL ||'FERIADO:' ||VA_FERIADO_SN);--
IF VA_FERIADO_SN = 0 THEN -- RETORNO 0 NÃO É FERIADO
IF TO_CHAR(VA_DT_ATUAL, 'D') NOT IN (1, 7) THEN --2 VERIFICA DIA DIFERENTE DE SABADO E DOMINGO
VA_TOT_HR_AUS := (VA_DT_FIM - VA_DT_ATUAL) * 24;
IF (TO_CHAR(VA_DT_FIM, 'HH24:MI') BETWEEN '08' AND '18') AND (TO_CHAR(VA_DT_INI, 'HH24:MI') BETWEEN '08' AND '18')THEN --3 VERIFICA SE HORA ESTÁ ENTRE 08 E 18
IF (TO_CHAR(VA_DT_ATUAL, 'D') <> (6) AND VA_TOT_HR_AUS >9) THEN --4 VERIFICA DIA DIFERENTE DE SEXTA E SE HORA MAIOR QUE 9
VA_TOT_DIAS := VA_TOT_DIAS +1;
VA_TOT_HR_AUS := 9;
DBMS_OUTPUT.put_line('VALOR ATUAL:' || VA_TOT_HR_AUS);
DBMS_OUTPUT.put_line('--------------');
ELSIF (TO_CHAR(VA_DT_ATUAL, 'D') = 6 AND VA_TOT_HR_AUS > 8) THEN -- VERIFICA DIA IGUAL SEXTA E SE HORA MAIOR QUE 8
-- VA_TOT_HR_AUS := 8;
VA_HR_AUS_SEXTA := 8;
DBMS_OUTPUT.put_line('VALOR ATUAL:' || VA_TOT_HR_AUS);
DBMS_OUTPUT.put_line('--------------');
VA_TOT_SEXTA := VA_TOT_SEXTA +1;
END IF; --4
END IF; --3
END IF; --2
VA_TOT_HR_AUS := (VA_TOT_HR_AUS*VA_TOT_DIAS) + (VA_HR_AUS_SEXTA * VA_TOT_SEXTA);
END IF; --1
END IF;
VA_DT_ATUAL := VA_DT_ATUAL +1;
DBMS_OUTPUT.PUT_LINE('CALCULO: ' || VA_CONTA);
VA_CONTA := VA_CONTA + 1;
DBMS_OUTPUT.put_line('DATA ATUAL:' || VA_DT_ATUAL);
END LOOP;
DBMS_OUTPUT.PUT_LINE('DIA SEMANA:' || VA_TOT_DIAS);
DBMS_OUTPUT.PUT_LINE('DIA SEXTA:' || VA_TOT_SEXTA);
DBMS_OUTPUT.put_line('VALOR FINAL :' || ROUND(VA_TOT_HR_AUS, 2)); -- resultado retornado que me interessa
END;Alguém consegue dar uma ajuda ?agradeço.
Fiz pára
Dia inicial: 30/05/19
Dia final: 31/05/19
total dias: 1
------------------
VALOR ATUAL:9
--------------
CALCULO: 0
DATA ATUAL:31/05/19
CALCULO: 1
DATA ATUAL:01/06/19
DIA SEMANA:1
DIA SEXTA:0
VALOR FINAL :9
o que está errado ?
por exemplo , se testar DT_INI = 30/05/2019 08:00 e DT_FIM = 30/05/2019 10:00 eu preciso que me retorne 2 ao final ,pois são 2 horas de diferença . As horas são o que importam pra mim entre as datas
Seriam horas "úteis" ?
você diz a coluna em questão ou o sentido do que eu quero no script ?
Se for a coluna, preciso desse valor retornado dentro da coluna hr_ausencia ( da tabela ausencias)
Se for o sentido do script, seriam na vdd as horas "inutilizaveis por ausencia"
Por exemplo entre
30/5/19 00:00
e
31/5/19 23:59
temos 48 horas "cheias" e 16 horas "úteis"
Seriam 17 horas úteis certo( 9 horas da quinta e 8 horas da sexta) ? . no caso sim, são 17 horas "úteis". pra esses casos que ultrapassam um dia, a contagem retorna correto, porém pra contagens menores que um dia o resultado não está correto, por exemplo como citei DT_INI = 30/05/2019 08:00 e DT_FIM = 30/05/2019 10:00 , deve retornar 2 horas "úteis"
indo um pouco mais além se não for abusar também, como posso fazer para setar esse resultado na coluna da tabela de ausencias ? por exemplo, abaixo tenho um update ao final do loop que carrega o resultado pra coluna de hr_ausencia referente a ultima linha inserida, porém meu resultado é sempre 0 , nao consegui encontrar o erro
DECLARE
VA_CONTA NUMBER := 0;
VA_DT_INI DATE;
VA_DT_FIM DATE;
VA_QT_DIAS NUMBER := 0;
VA_DT_ATUAL DATE;
VA_FERIADO_SN NUMBER := 0;
VA_TOT_HR_AUS NUMBER:= 0;
VA_HR_AUS_SEXTA NUMBER:= 0;
VA_TOT_DIAS NUMBER :=0;
VA_TOT_SEXTA NUMBER := 0;
BEGIN
/*SELECT trunc(to_date('03/06/2019 13:00','dd/mm/yyyy hh24:mi')) - trunc(to_date('03/06/2019 18:00','dd/mm/yyyy hh24:mi')),
trunc(to_date('03/06/2019 13:00','dd/mm/yyyy hh24:mi')),
trunc(to_date('03/06/2019 18:00','dd/mm/yyyy hh24:mi'))
into VA_QT_DIAS,
VA_DT_INI,
VA_DT_FIM
from dual;*/
select DISTINCT (TRUNC(A.DT_FIM) - TRUNC(A.DT_INI)),
A.DT_INI,
A.DT_FIM
INTO VA_QT_DIAS,
VA_DT_INI,
VA_DT_FIM
FROM AUSENCIAS A
WHERE A.DT_INC = (SELECT MAX(DT_INC) FROM AUSENCIAS);
WHILE VA_CONTA <= VA_QT_DIAS LOOP -- VERIFICA CONTADOR(VA_CONTA) EQUANTO MENOR QUE TOTAL DE DIAS ENTRE AS DATAS (INI E FIM)
VA_DT_ATUAL := VA_DT_INI + VA_CONTA;
IF VA_DT_ATUAL <= VA_DT_FIM THEN --1 VERIFICA SE DATA DO LAÇO MENOR QUE DATA FINAL
SELECT COUNT(*) INTO VA_FERIADO_SN FROM FERIADOS WHERE TRUNC(DT_FERIADO) = TRUNC(VA_DT_ATUAL);
IF VA_FERIADO_SN = 0 THEN --2 RETORNO 0 NÃO É FERIADO
IF TO_CHAR(VA_DT_ATUAL, 'D') NOT IN (1, 7) THEN --3 VERIFICA DIA DIFERENTE DE SABADO E DOMINGO
VA_TOT_HR_AUS := (VA_DT_FIM - VA_DT_ATUAL) * 24;
IF (TO_CHAR(VA_DT_FIM, 'HH24:MI') BETWEEN '08' AND '18') AND (TO_CHAR(VA_DT_INI, 'HH24:MI') BETWEEN '08' AND '18')THEN --4 VERIFICA SE HORA ESTÁ ENTRE 08 E 18
IF (TO_CHAR(VA_DT_ATUAL, 'D') <> (6) AND VA_TOT_HR_AUS >9) THEN --5 VERIFICA DIA DIFERENTE DE SEXTA E SE HORA MAIOR QUE 9
VA_TOT_DIAS := VA_TOT_DIAS +1;
VA_TOT_HR_AUS := 9;
ELSIF (TO_CHAR(VA_DT_ATUAL, 'D') = 6 AND VA_TOT_HR_AUS > 8) THEN -- VERIFICA DIA IGUAL SEXTA E SE HORA MAIOR QUE 8
VA_HR_AUS_SEXTA := 8;
VA_TOT_SEXTA := VA_TOT_SEXTA +1;
END IF; --5
END IF; --4
END IF; --3
END IF; --2
VA_TOT_HR_AUS := (VA_TOT_HR_AUS*VA_TOT_DIAS) + (VA_HR_AUS_SEXTA * VA_TOT_SEXTA);
END IF; --1
VA_DT_ATUAL := VA_DT_ATUAL +1;
VA_CONTA := VA_CONTA + 1;
END LOOP;
UPDATE AUSENCIAS SET HR_AUSENCIA = VA_TOT_HR_AUS WHERE DT_INC = (SELECT MAX(A.DT_INC) FROM AUSENCIAS A);
END;Eu faria o while somando horas a data.hora 1/24 e testando se é hora útil ou não, segunda se tiver tempobtento montar.
Veja se istoajuda.
Ou isto., também