Desafio de lógica em JAVA

Fala pessoal, tudo bem? Eu estou enfrentando um problema com um trabalho que me foi passado. No trabalho é necessário fazer a subtração entre duas tabelas, Marcação e Horário de Trabalho.
Nas duas tabelas temos os campos de entrada e saída. O resultado esperado é o seguinte:

/**
  * Exemplo de entrada 
  *  Horários:
  *  	- 08:00	12:00
  *  	- 13:30	17:30
  *  Marcações:
  *  	- 06:00 20:00
  *  
  *  Exemplo de saída:
  *  
  *  Atraso:
  *  	-
  *  Hora Extra:
  *  	- 06:00 08:00
  *  	- 12:00 13:30
  *  	- 17:30 20:00
  *  
  */

Ou

/**
  * Exemplo de entrada
  *  Horários:
  *  	- 08:00	12:00
  *  Marcações:
  *  	- 07:00 11:00
  *  
  *  Exemplo de saída:
  *  
  *  Atraso:
  *  	- 11:00 12:00
  *  Hora Extra:
  *  	- 07:00 08:00
  *  
  */

Bom, eu montei um código que já está funcionando pra maioria dos casos, mas, estou tendo problemas quando é de madrugada, pois da forma que eu fiz, ele entende errado a comparação e acaba não identificando atrasos.

/**
  * Exemplo de entrada para madrugada
  *  Horários:
  *  	- 22:00 05:00
  *  Marcações:
  *  	- 03:00 07:00
  *  
  *  Exemplo de saída:
  *  
  *  Atraso:
  *  	- 22:00 03:00
  *  Hora Extra:
  *  	- 05:00 07:00
  *  
  */

Se alguém puder me dar umas dicas do que melhorar, ou no que eu estou errando…

Segue meu código JAVA (PS: os campos de entrada e saída são do tipo String)

public static void main(String[] args) {
		List<WorkSchedule> schedules = new ArrayList<>();
		//Exemplo 1
		
		//schedules.add(new WorkSchedule("08:00","12:00"));
		//HourMarker hm = new HourMarker("07:00", "11:00");
		
		//Exemplo 2
		//schedules.add(new WorkSchedule("08:00","12:00"));
		//HourMarker hm = new HourMarker("07:00", "11:00");
		
		//Exemplo 3
		//schedules.add(new WorkSchedule("08:00","12:00"));
		//schedules.add(new WorkSchedule("13:30","17:30"));
		
		//HourMarker hm = new HourMarker("06:00", "20:00");
		
		//Exemplo 3
		schedules.add(new WorkSchedule("22:00","05:00"));
		//schedules.add(new WorkSchedule("13:30","17:30"));
		
		HourMarker hm = new HourMarker("03:00", "07:00");
		
		int markerEntryHour = Integer.parseInt(hm.getEntryHour().split(":")[0]);
		int markerEntryMinute = Integer.parseInt(hm.getEntryHour().split(":")[1]);
		
		int markerDepartureHour = Integer.parseInt(hm.getDepartureTime().split(":")[0]);
		int markerDepartureMinute = Integer.parseInt(hm.getDepartureTime().split(":")[1]);

		//Guarda a hora extra
		String horaExtra = "";
		
		//Guarda a hora de atraso
		String horaAtraso = "";
		
		//Início da jornada
		int hour = 0;
		
		//Guarda o último minuto processado
		int lastMinute = 0;
		
		//Percorro os horários de trabalho
		for (WorkSchedule schedule : schedules) {
			boolean horaExtraEncontrada = false;
			boolean horaDeAtrasoEncontrada = false;
			
			int horarioDeEntrada = Integer.parseInt(schedule.getEntryHour().split(":")[0]);
			int horarioDeSaida = Integer.parseInt(schedule.getDepartureTime().split(":")[0]);
			lastMinute = Integer.parseInt(schedule.getDepartureTime().split(":")[1]);
			
			/**
			 * Jornada de Trabalho do funcionário
			 * 
			 * Se hour for == 0, então quer dizer que ele iniciou o cálculo agora, mas caso já exista um valor, continua de onde parou
			 * Casos como uma jornada que passa de um turno para o outro por exemplo....
			 */
			for(hour = hour == 0 ? markerEntryHour : hour; hour <= markerDepartureHour; hour++) {
				if(hour == horarioDeSaida) {
					/**
					 * Isto marca a chegada ao final do expediente, então passa para o próximo horário, caso exista
					 */
					break;
				}
				if(hour < horarioDeEntrada && hour < horarioDeSaida) {
					/**
					 * Entrou antes
					 */
					if(!horaExtraEncontrada) {
						horaExtraEncontrada = true;
						horaExtra = hour + ":" + markerEntryMinute;
					}
				} else if(hour == horarioDeEntrada && hour < horarioDeSaida) {
					/**
					 * Entrou no horário
					 */	
					if(horaExtraEncontrada) {
						/**
						 * Se tiver encontrado hora extra antesm quer dizer que tinha algo para colocar na variável
						 */
						horaExtra += " " + schedule.getEntryHour();
						System.out.println("Hora extra antes do expediente: " + horaExtra);
					} else {
						System.out.println("Entrou no horário");
					}
				
				} else if(hour > horarioDeEntrada && hour < horarioDeSaida) {
					//Se ele encontrou hora extra antes, quer dizer que o funcionário chegou antes do horário...
					/**
					 * Entrou Atrasado
					 */
					
					if(hour > horarioDeEntrada && !horaExtraEncontrada) {
						/**
						 * Se não, ele entrou atrasado
						 */
						if(!horaDeAtrasoEncontrada) {
							horaDeAtrasoEncontrada = true;
							horaAtraso = hour + ":" + markerEntryMinute;
						} else {
							horaAtraso += " " + schedule.getDepartureTime();
							System.out.println("Entrou atrasado: " + horaAtraso);
						}
					} else if(hour == markerDepartureHour) {
						/* Se a hora for igual ao horário de saída marcado, então quer dizer que 
						 * o funcionário saiu, mas ainda deveria estar no expediente dele e será considerado 
						 * um atraso
						 */
						horaDeAtrasoEncontrada = true;
						horaAtraso =  hour + ":" + markerDepartureMinute + " " + schedule.getDepartureTime();
						System.out.println("Saiu mais cedo: " + horaAtraso);
					}
				
				} else if(hour > horarioDeEntrada && hour > horarioDeSaida) {
					/**
					 * Saiu depois do horário
					 */	
					if(!horaExtraEncontrada) {
						/**
						 * Pega o início da hora extra
						 */
						horaExtraEncontrada = true;
						horaExtra = schedule.getDepartureTime();
					} else {
						/**
						 * Fica atualizando até achar o final da hora extra
						 */
						horaExtra += " " + hour + ":" + markerDepartureMinute;
						System.out.println("Hora extra após o expediente: " + horaExtra);
					}
				}
			}
		}
		
		if(hour < markerDepartureHour) {
			/**
			 * Caso ainda tenham sobrado horas, serão horas extras após o expediente
			 */
			String horasExtrasRestantes = hour + ":" + lastMinute + " " +  markerDepartureHour + ":" + markerDepartureMinute;  
			System.out.println("Hora extra após expediente: " + horasExtrasRestantes);
		}
		
	}

Desde já agradeço a atenção de todos!

Fala amigo, obrigado por disponibilizar o código, tem me ajudado bastante em um projeto que estou desenvolvendo. Fiquei curioso em saber se voce termonou ele. Pelo o que vi e testei falta implementar a logica seguinte:

    Exemplo 4:

            Tabela-Horario De Trabalho:

                    22:00 05:00

            Exemplos em marcações e o resultado esperado:

            - Marcações:
                    21:00 04:00

                            Atraso
                               04:00 05:00

                            Hora extra
                               21:00 22:00


            - Marcações
                    03:00 07:00

                            Atrasos:
                               22:00 03:00

                            Hora extra:
                               05:00 07:00

Estou estudando ele bastante, e estou vendo como faço pra implementar quando a marcação é 21:00 04:00. O resto do codigo esta funcionando pelo o que testei, no momento estou vendo como faço pra continuar a terminar ele. Mas se já resolveu serei grato em ver e testalo. Parabéns!

Calcule a diferença, 04:00 menos 21:00.
Se for negativo, é só somar 24 horas.

Trabalho de algum curso ou do emprego mesmo? Se for de curso, até entendo que tenha que fazer na mão (muitos não deixam usar libs prontas para que vc tenha que exercitar). Mas se for algo referente ao seu emprego, sugiro não fazer tudo manualmente.

A partir do Java 8 vc pode usar o java.time para manipular datas e horários. Dito isso, vamos a alguns pontos.

Se a data tem que ser levada em conta, então é melhor já considerá-la nos cálculos. Assim, vc simplifica esse monte de if. Por exemplo, por padrão vc considera que todos os horários são da data de hoje, mas se o horário de saída for menor que a entrada, quer dizer que a saída foi no dia seguinte.

Outro detalhe é que vc também precisa determinar se somente o horário de saída é no dia seguinte, ou se ambos são. Por exemplo, no último caso: se o horário é das 22:00 às 05:00 e a marcação foi das 03:00 às 07:00. Aqui temos duas possibilidades:

  • 22:00 é referente a um dia, e todos os outros horários (05:00, 03:00 e 07:00) são do dia seguinte.
  • 22:00, 03:00 e 07:00 são referentes a hoje, e 05:00 é do dia seguinte.

Ambas as interpretações são válidas, e cada uma vai dar um resultado diferente. No seu caso, creio que é o primeiro caso.


Então se vc criar os horários juntamente com a data, fica bem mais simples do que ficar testando várias condições.

Claro que sem todos os requisitos e sem saber o caso real, não tem como determinar o que é “melhor”, mas enfim. Para este caso, vamos considerar que por padrão um horário é do dia de hoje, a menos que caia em alguma das condições abaixo:

  • implícito: horário de saída é anterior ao de entrada, então a saída é no dia seguinte
  • explícito: é o caso do último exemplo, não tem como determinar se a marcação é de hoje ou amanhã. Então vc tem que especificar a data explicitamente

Enfim, uma sugestão seria usar java.time.LocalDateTime para os horários de entrada e saída (assim temos a data e a hora juntas). E na hora de construir os objetos vc determina a data de acordo com as regras que forem mais adequadas ao seu caso.

Primeiro eu criei uma classe que representa um horário de entrada e saída. Ela pode ser usada tanto para o horário de trabalho quanto para as marcações:

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

public class EntradaSaida {
    private LocalDateTime entrada, saida;
    public EntradaSaida(LocalDateTime entrada, LocalDateTime saida) {
        this.entrada = entrada;
        this.saida = saida;
    }

    public static EntradaSaida build(LocalTime horaEntrada, LocalTime horaSaida) {
        // por padrão, os horários são do mesmo dia
        LocalDate dt = LocalDate.now();
        // mas se o horário de saída é "antes", quer dizer que é no dia seguinte
        // mas a entrada continua sendo no dia atual
        return new EntradaSaida(dt.atTime(horaEntrada), (horaEntrada.isAfter(horaSaida) ? dt.plusDays(1) : dt).atTime(horaSaida));
    }
    // constrói a partir de 2 strings contendo o horário (formato HH:MM)
    public static EntradaSaida build(String horaEntrada, String horaSaida) {
        return build(horaEntrada, horaSaida, false); // por padrão, não é dia seguinte
    }
    // deixa explícito se os horários referem-se à data atual ou ao dia seguinte
    public static EntradaSaida build(String horaEntrada, String horaSaida, boolean diaSeguinte) {
        LocalDate dt = LocalDate.now();
        if (diaSeguinte) { // os dois horários são do dia seguinte
            dt = dt.plusDays(1);
        }
        return new EntradaSaida(dt.atTime(LocalTime.parse(horaEntrada)), dt.atTime(LocalTime.parse(horaSaida)));
    }

    public LocalDateTime getEntrada() {
        return entrada;
    }
    public LocalDateTime getSaida() {
        return saida;
    }
    private static DateTimeFormatter FMT = DateTimeFormatter.ofPattern("HH:mm");
    @Override
    public String toString() {
        return FMT.format(this.entrada) + " " + FMT.format(this.saida);
    }
}

Depois, criei uma classe que representa o horário de trabalho. Ela tem uma lista de entradas e saídas, e um método para calcular os atrasos e horas extras a partir de uma marcação:

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

// horário de trabalho contém uma lista de entrada/saída
public class HorarioTrabalho {
    private List<EntradaSaida> horarios;
    public HorarioTrabalho(EntradaSaida... horarios) {
        this.horarios = Arrays.asList(horarios);
    }

    public void calcularAtrasosHoraExtra(EntradaSaida marcacao) {
        System.out.println("\nHorários: " + this.horarios + "\nMarcacao " + marcacao);
        List<EntradaSaida> atrasos = new ArrayList<>(), horasExtras = new ArrayList<>();
        EntradaSaida primeiraEntrada = this.horarios.get(0);
        LocalDateTime primeiroHorario = primeiraEntrada.getEntrada();
        LocalDateTime entrada = marcacao.getEntrada();
        for (int i = 0; i < this.horarios.size(); i++) {
            EntradaSaida es = this.horarios.get(i);
            if (entrada.isBefore(primeiroHorario)) {
                horasExtras.add(new EntradaSaida(entrada, es.getEntrada()));
            } else if (entrada.isAfter(es.getEntrada())) {
                atrasos.add(new EntradaSaida(es.getEntrada(), entrada));
            }
            entrada = es.getSaida();

            LocalDateTime saida;
            // não é o último horário, calcula a "saída" até o início do próximo horário
            if (i < this.horarios.size() - 1) {
                saida = this.horarios.get(i + 1).getEntrada();
            } else { // último horário
                saida = marcacao.getSaida();
            }
            if (saida.isBefore(es.getSaida())) {
                atrasos.add(new EntradaSaida(saida, es.getSaida()));
            } else if (saida.isAfter(es.getSaida())) {
                horasExtras.add(new EntradaSaida(es.getSaida(), saida));
            }
        }

        showList(atrasos, "Atraso:");
        showList(horasExtras, "Hora Extra: ");
    }

    private void showList(List<EntradaSaida> lista, String titulo) {
        System.out.println(titulo);
        if (lista.isEmpty()) {
            System.out.println("  -");
        } else {
            for (EntradaSaida es : lista) {
                System.out.println("  - " + es);
            }
        }
    }
}

Pronto, agora é só testar:

public static void main(String[] args) {
    HorarioTrabalho schedule = new HorarioTrabalho(EntradaSaida.build("08:00", "12:00"), EntradaSaida.build("13:30", "17:30"));
    schedule.calcularAtrasosHoraExtra(EntradaSaida.build("06:00", "20:00"));

    schedule = new HorarioTrabalho(EntradaSaida.build("22:00", "05:00"));
    schedule.calcularAtrasosHoraExtra(EntradaSaida.build("21:00", "04:00"));

    schedule = new HorarioTrabalho(EntradaSaida.build("22:00", "05:00"));
    // ambos os horários da marcação estão no dia seguinte, passar "true" explicitamente
    schedule.calcularAtrasosHoraExtra(EntradaSaida.build("03:00", "07:00", true));
}

Saída:

Horários: [08:00 12:00, 13:30 17:30]
Marcacao 06:00 20:00
Atraso:
  -
Hora Extra: 
  - 06:00 08:00
  - 12:00 13:30
  - 17:30 20:00

Horários: [22:00 05:00]
Marcacao 21:00 04:00
Atraso:
  - 04:00 05:00
Hora Extra: 
  - 21:00 22:00

Horários: [22:00 05:00]
Marcacao 03:00 07:00
Atraso:
  - 22:00 03:00
Hora Extra: 
  - 05:00 07:00

E claro que dá para melhorar bastante. Mas como eu já disse, sem conhecer todos os requisitos, no fim vira apenas especulação. Mas só pra citar algumas coisas, poderia ter a escala semanal/mensal (pois no mundo real vc não tem o mesmo horário em todos os 7 dias da semana, varia bastante). E vc poderia usar java.time.ZonedDateTime, se quiser levar em conta o fuso horário e outros fatores como o horário de verão (que LocalDateTime não considera). Entre outros detalhes que acho que não vem ao caso, mas é só pra deixar claro que a solução acima é simplista e provavelmente incompleta.

2 curtidas