Calcular horas úteis

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…

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

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

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

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

Se conseguir, posto aqui o resultado…

valew jks1903.

[quote=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

[/quote]

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!

[quote=aureliolima][quote=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

[/quote]

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![/quote]

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.

é não precisa banco de dados necessariamente.

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

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.

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.

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

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

[quote=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[/quote]

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.

[quote=sergiotaborda][quote=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[/quote]

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.[/quote]

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.