Calcular horas úteis

14 respostas
aureliolima

Olá, estou com um problema para localizar X horas úteis a partir de um determinado horário.
Por exemplo, informo o horário 15:30 e queria contar 8 horas úteis a partir desse horário, tendo em vista que vale apenas horário comercial: 8:00 até 12:00 e 13:30 até 18:00.

Fiz o seguinte código porém não funcionou:

public static Calendar adicionaHorasUteis2(int qtdeHoras, Calendar dataHoraAtual) {

        final String HORA_INICIO_MANHA = "08:00";
        final String HORA_FINAL_MANHA = "12:00";
        final String HORA_INICIO_TARDE = "13:30";
        final String HORA_FINAL_TARDE = "18:00";

        final String DATA_HORA_INICIO_MANHA = Datas.formataDataHoraMin(Datas.getDataHora(Datas.formataData(dataHoraAtual), HORA_INICIO_MANHA + ":00")) + ":00";
        final String DATA_HORA_FINAL_MANHA = Datas.formataDataHoraMin(Datas.getDataHora(Datas.formataData(dataHoraAtual), HORA_FINAL_MANHA + ":00")) + ":00";
        final String DATA_HORA_INICIO_TARDE = Datas.formataDataHoraMin(Datas.getDataHora(Datas.formataData(dataHoraAtual), HORA_INICIO_TARDE + ":00")) + ":00";
        final String DATA_HORA_FINAL_TARDE = Datas.formataDataHoraMin(Datas.getDataHora(Datas.formataData(dataHoraAtual), HORA_FINAL_TARDE + ":00")) + ":00";

        int horasUteis = 1;

        do {
            if ((dataHoraAtual.compareTo(Datas.getDataHoraMinSegCalendar(DATA_HORA_INICIO_MANHA)) >= 0 && dataHoraAtual.compareTo(Datas.getDataHoraMinSegCalendar(DATA_HORA_FINAL_MANHA)) < 0)
                    || (dataHoraAtual.compareTo(Datas.getDataHoraMinSegCalendar(DATA_HORA_INICIO_TARDE)) >= 0 && dataHoraAtual.compareTo(Datas.getDataHoraMinSegCalendar(DATA_HORA_FINAL_TARDE)) < 0)) {
                dataHoraAtual.add(dataHoraAtual.HOUR, 1);
                horasUteis++;
            } else if (isDiaUtil(dataHoraAtual)) {
                dataHoraAtual.add(dataHoraAtual.HOUR, 1);
            } else {
                dataHoraAtual.add(dataHoraAtual.DATE, 1);
            }
        } while (horasUteis <= qtdeHoras);
        return dataHoraAtual;
    }

Se alguém puder me ajudar, agradesceria muito…

14 Respostas

charleston10

Ta usando banco de dados?
Se estiver é mais facil fazer isso via SQL…

aureliolima

to usando isso pra pegar o prazo final para um determinado atendimento, e gravar no bd esse prazo final.

jks1903

Acho que a classe GregorianCalendar é a mais indicada para esse caso.

Eu já precisei fazer algo semelhante e fiz mais ou menos assim.

Calculei a diferença em milisegundos(que é o retornado pelo metodo da classe) da data/hora atual até a hora desejada.
Também calculei a quantidade de milisegundos existentes nos turnos, por exemplo, das 13:30 até as 18:00.

Aí caso a quantidade de milisegundos do primeiro cálculo for maior que a quantidade de milisegundos do turno, adiciona um dia e prossegue com o calculo do proximo turno.

A lógica é semelhante a qu você está usando, mas com o GregorianCalendar acredito que seja mais simples.

Aqui voce encontra a documentação da Oracle sobre a classe: http://docs.oracle.com/javase/7/docs/api/java/util/GregorianCalendar.html

aureliolima

Blz…
vou tentar usar a classe GregorianCalendar e calculando os milissegundos de cada turno…

Se conseguir, posto aqui o resultado…

valew jks1903.

aureliolima

jks1903:
Acho que a classe GregorianCalendar é a mais indicada para esse caso.

Eu já precisei fazer algo semelhante e fiz mais ou menos assim.

Calculei a diferença em milisegundos(que é o retornado pelo metodo da classe) da data/hora atual até a hora desejada.
Também calculei a quantidade de milisegundos existentes nos turnos, por exemplo, das 13:30 até as 18:00.

Aí caso a quantidade de milisegundos do primeiro cálculo for maior que a quantidade de milisegundos do turno, adiciona um dia e prossegue com o calculo do proximo turno.

A lógica é semelhante a qu você está usando, mas com o GregorianCalendar acredito que seja mais simples.

Aqui voce encontra a documentação da Oracle sobre a classe: http://docs.oracle.com/javase/7/docs/api/java/util/GregorianCalendar.html

Cara, poderia exemplificar melhor o que vc disse em relação á " Aí caso a quantidade de milisegundos do primeiro cálculo for maior que a quantidade de milisegundos do turno, adiciona um dia e prossegue com o calculo do proximo turno. "

Não entendi muito bem.

Obrigado!

jks1903

aureliolima:
jks1903:
Acho que a classe GregorianCalendar é a mais indicada para esse caso.

Eu já precisei fazer algo semelhante e fiz mais ou menos assim.

Calculei a diferença em milisegundos(que é o retornado pelo metodo da classe) da data/hora atual até a hora desejada.
Também calculei a quantidade de milisegundos existentes nos turnos, por exemplo, das 13:30 até as 18:00.

Aí caso a quantidade de milisegundos do primeiro cálculo for maior que a quantidade de milisegundos do turno, adiciona um dia e prossegue com o calculo do proximo turno.

A lógica é semelhante a qu você está usando, mas com o GregorianCalendar acredito que seja mais simples.

Aqui voce encontra a documentação da Oracle sobre a classe: http://docs.oracle.com/javase/7/docs/api/java/util/GregorianCalendar.html

Cara, poderia exemplificar melhor o que vc disse em relação á " Aí caso a quantidade de milisegundos do primeiro cálculo for maior que a quantidade de milisegundos do turno, adiciona um dia e prossegue com o calculo do proximo turno. "

Não entendi muito bem.

Obrigado!

O que eu quis dizer ali foi o seguinte, digamos que vocÊ queria adicionar 8 horas a seguinte data/hora: 21/01/2013 09:00. O resultado será 21/01/2013 17:00. E a diferença em milisegundos entre elas é X.

Porém, como o “turno” vaí apenas até as 12:00, você deve subtrair desse X a quantidade de milisegundos entre 21/01/2013 09:00 e 21/01/2013 12:00. O que sobrar, você incrementa a data 21/01/2013 13:30, que é o início do próximo turno.
Como no meu caso eu trabalhava com um turno unico por dia, se estourasse iria para o outro dia, o que acontecerá caso o valor ultrapasse 21/01/2013 18:00. Você terá que passar para o dia 22/01/2013.

De repente possam haver formas mais simples de fazer, apenas passei uma idéia, pois fiz mais ou menos assim quando precisei.

R

é não precisa banco de dados necessariamente.

aureliolima

Pessoal, publiquei no blog aureliolima.wordpress.com a solução para esse problema…

sergiotaborda

Na boa, você está orgulhoso desse código ?

Ha uma coisa que as pessoas têm que entender de umas vez por todas, não se faz contas com datas, se usa um calendário!
Além disso, “Aqui é OO!!!”, não é Esparta…

Se vc tem um enunciado de “intervalo”, crie a classe intervalo.

Eis como eu faria

HorarioComercial horario = new HorarioComercial();
horario.add(Intervalo.valueOf(8, 12)); 
horario.add(Intervalo.valueOf(13.5, 18)); 

Calendar calendar = Calendar.getInstance(); // hoje
calendar.set(Calendar.HOUR_OF_DAY, 15); // hoje às 15 horas
calendar.set(Calendar.MINUTE, 30); // hoje ás 15 horas e 30 minutos.

int horasUteis = 8; // horas que eu quero achar

while (horasUteis > 0){
       calendar.add(Calendar.HOUR_OF_DAY, 1); 

       int horAtual = calendar.get(Calendar.HOUR_OF_DAY);
       int minutoAtual = calendar.get(Calendar.MINUTE);

      double agora = horaAtual + (minutoAtual / 60d); // isto é porque estou usando double no Horario, mas poderia criar um objeto para isso.

       if (horario.contains(agora)){
               horasUteis--;
       } 

}

Date dataFinal = calendar.geTtime();

Ou seja, começo na data atual e na hora que foi ditada.
Vou andando 1 hora no tempo. Se essa hora passou fora do intervalo, não conta. Se passou dentro, então conta e diminui um do numero de horas uteis que tenho que procurar.
(estou fazendo de hora em hora, mas na realidade teria que ser minuto em minuto porque o intervalo pode ser dado com qualquer numero de minutos)

Quando o numero de horasUteis chegar a zero, o tempo que passou no calendário é a que eu quero. Compare esse código com o seu.

É preciso entender que o calendário é um contador de tempo e o ha conceitos de intervalo sendo usados. Isso tem que ser explicito. Senão, não é OO, é apenas um programa…

P.S. Deveríamos usar um passo de 1 minuto. Isso poderia fazer o algoritmo demorar muito, mas o algoritmo seria correto. Depois que se entende que o algoritmo correto é utilizar o calendário como contador, podemos acelerar isso um pouco mais assim:
Calcule-se o tempo passado entre a data de input e a primeira data no intervalo do horario comercial. Diminui o horasUteis desse valor e acelera o relógio até o fim do intervalo, depois soma o numero de horas que faltaram cumprir.
Este mecanismo só funciona se só existe um intervalo ou se os vários intervalos são conectados (que não é o caso, mas poderia ser se o intervalo fosse das 8 às 18). É claro que poderemos usar recursividade e usar esta logica para cada intervalo.

R

fui lá ver. tem pouca coisa ainda. bacana.

fiz um trabalho que parecia tudo certo. mas no final ‘vi problema’ . aí ficou meio parado.
Exige um certo estudo e cuidado esta modalidade.

Fiz com a questão dos milisegundos, mas na hora de trabalhar o somar e diminuir os milisegundos
houve algum problema.

x111

Bah cara, não querer falar mal, trollar nem nada mas isso não e nada OO e a solução é beeemmm mais simples!

aureliolima

sergiotaborda fiz aquele código pois até aquele momento não tinha encontrado alguma solução rápida, e tinha uma certa pressa… Por enquando está funcionando para o que eu preciso, mas assim que possível, vou procurar utilizar a solução que vc descreveu…

valew

sergiotaborda

aureliolima:
sergiotaborda fiz aquele código pois até aquele momento não tinha encontrado alguma solução rápida, e tinha uma certa pressa… Por enquanto está funcionando para o que eu preciso, mas assim que possível, vou procurar utilizar a solução que vc descreveu…

valew

Meu ponto nem era o algoritmo. Meu ponto é a qualidade do que vc publicou. Se algum desavisado usar aquilo vc não está fazendo um favor à comunidade. E vc mesmo sabe que foi uma coisa feita à pressa e tal… então , é problema seu se vc usa aquele código porque não tem tempo para fazer um melhor (embora escrever o codigo correto demora menos que escrever aquilo) , mas é problema nosso se alguém acreditar e usar aquilo.

Use como quiser, mas faça um favor a todos e remova do blog. Ou pelo menos explique com muito cuidado que aquele não é código ideal e que não deve ser usado em sistemas reais.

aureliolima

sergiotaborda:
aureliolima:
sergiotaborda fiz aquele código pois até aquele momento não tinha encontrado alguma solução rápida, e tinha uma certa pressa… Por enquanto está funcionando para o que eu preciso, mas assim que possível, vou procurar utilizar a solução que vc descreveu…

valew

Meu ponto nem era o algoritmo. Meu ponto é a qualidade do que vc publicou. Se algum desavisado usar aquilo vc não está fazendo um favor à comunidade. E vc mesmo sabe que foi uma coisa feita à pressa e tal… então , é problema seu se vc usa aquele código porque não tem tempo para fazer um melhor (embora escrever o codigo correto demora menos que escrever aquilo) , mas é problema nosso se alguém acreditar e usar aquilo.

Use como quiser, mas faça um favor a todos e remova do blog. Ou pelo menos explique com muito cuidado que aquele não é código ideal e que não deve ser usado em sistemas reais.

O que eu quis dizer, é que o código que fiz foi pra suprir as minhas necessidades, e está funcionando muito bem para o sistema que desenvolvi. Porém pode haver uma solução melhor, como a citada aqui mesmo neste fórum. Solução essa que não havia encontrado no momento que desenvolvi o algoritmo.

Apenas publiquei no meu blog, pois pensei em ajudar pessoas, que assim como eu, não possuía uma solução para o problema, pois outras soluções somente foram postadas aqui no guj depois que publiquei a minha no meuu blog e sinalizei este post no guj como “resolvido”.

Em suma irei despublicar do meu blog o algoritmo e, se por ventura, alguém quiser detalhes sobre como desenvolvi, que entre em contato comigo.

Criado 25 de janeiro de 2013
Ultima resposta 14 de mar. de 2013
Respostas 14
Participantes 6