Ao trabalhar com objetos java.time, como LocalDate, muitas vezes você precisa fazer ajustes de data mais elaborados, como encontrar a "segunda terça-feira do mês".
Em vez de calcular isso manualmente, você pode usar os TemporalAdjusters.
A classe utilitária java.time.temporal.TemporalAdjusters (repare no "s" do plural) oferece métodos estáticos para muitos ajustes de data comuns.
Você aplica esses ajustes com o método .with() em um objeto temporal (como LocalDate).
Usar with(TemporalAdjuster) retorna uma instância nova e não atualiza a instância existente, já que os objetos java.time são imutáveis.
import java.time.LocalDate;
import java.time.Month;
import java.time.DayOfWeek;
import java.time.temporal.TemporalAdjusters;
LocalDate date = LocalDate.of(2024, Month.AUGUST, 15); // 2024-08-15 (a Thursday)
// Use a predefined adjuster: find the last day of the month
LocalDate lastDay = date.with(TemporalAdjusters.lastDayOfMonth());
// => 2024-08-31
Veja alguns ajustadores importantes para cálculos de agendamento:
Use dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek) para encontrar a enésima ocorrência de um dia da semana.
// Find the second Tuesday of August 2024
LocalDate secondTuesday = date.with(TemporalAdjusters.dayOfWeekInMonth(2, DayOfWeek.TUESDAY));
// => 2024-08-13
Use firstInMonth(DayOfWeek dayOfWeek) ou lastInMonth(DayOfWeek dayOfWeek).
// Find the last Monday of August 2024
LocalDate lastMonday = date.with(TemporalAdjusters.lastInMonth(DayOfWeek.MONDAY));
// => 2024-08-26
Ajustadores como next(), previous(), nextOrSame() e previousOrSame() encontram a próxima ocorrência ou a ocorrência anterior de um determinado dia da semana, em relação à data atual.
next()/previous(): excluem a data atual, caso ela corresponda.nextOrSame()/previousOrSame(): incluem a data atual, caso ela corresponda.LocalDate thursday = LocalDate.of(2024, Month.AUGUST, 15); // A Thursday
// Find the next Thursday (exclusive)
LocalDate nextThursday = thursday.with(TemporalAdjusters.next(DayOfWeek.THURSDAY));
// => 2024-08-22
// Find the next Thursday (inclusive) - returns the same date
LocalDate nextOrSameThursday = thursday.with(TemporalAdjusters.nextOrSame(DayOfWeek.THURSDAY));
// => 2024-08-15
// Find the previous Monday (exclusive)
LocalDate previousMonday = thursday.with(TemporalAdjusters.previous(DayOfWeek.MONDAY));
// => 2024-08-12
TemporalAdjuster é uma interface funcional, então você pode escrever um ajustador seu como uma lambda que recebe o objeto temporal a ajustar e retorna o objeto ajustado.
Ele é aplicado com .with() assim como os ajustadores predefinidos, e pode usá-los internamente.
import java.time.temporal.TemporalAdjuster;
// An adjuster that moves a weekend date to the following Monday
TemporalAdjuster skipWeekend = temporal -> {
DayOfWeek day = DayOfWeek.from(temporal);
if (day == DayOfWeek.SATURDAY || day == DayOfWeek.SUNDAY) {
return temporal.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
}
return temporal;
};
LocalDate saturday = LocalDate.of(2024, Month.AUGUST, 17);
LocalDate monday = saturday.with(skipWeekend);
// => 2024-08-19
Usar esses ajustadores da classe TemporalAdjusters pode simplificar muito as tarefas de manipulação de datas.
O salão de beleza em Nova York que você ajudou no exercício Booking Up For Beauty vai muito bem, e muitos dos seus clientes se tornaram clientes fixos. Em vez de marcar cada visita separadamente, um cliente fixo tem um combinado permanente: "toda segunda terça-feira", "a última sexta-feira do mês" e assim por diante. A dona do salão pediu que você fizesse um auxiliar que calcula as datas por trás desses combinados, além das datas da rotina mensal do próprio salão.
Implemente o método SalonCalendar.standingAppointment() que recebe um ano, um mês, uma ocorrência (1 para a primeira, 2 para a segunda, e assim por diante) e um dia da semana.
Ele retorna a data daquela ocorrência do dia da semana dentro do mês:
SalonCalendar calendar = new SalonCalendar();
calendar.standingAppointment(2024, Month.AUGUST, 2, DayOfWeek.TUESDAY);
// => LocalDate.of(2024, 8, 13)
Alguns clientes fixos preferem a última ocorrência do dia deles no mês.
Implemente o método SalonCalendar.lastAppointment() que recebe um ano, um mês e um dia da semana, e retorna a data da última ocorrência desse dia da semana dentro do mês:
SalonCalendar calendar = new SalonCalendar();
calendar.lastAppointment(2024, Month.AUGUST, DayOfWeek.FRIDAY);
// => LocalDate.of(2024, 8, 30)
Um cliente na recepção pede "o próximo sábado".
Se o dia em que ele faz o pedido já for um sábado, ele quer dizer esse mesmo dia.
Implemente o método SalonCalendar.nextAvailable() que recebe uma data e um dia da semana, e retorna a primeira data, igual ou posterior à data informada, que caia nesse dia da semana:
SalonCalendar calendar = new SalonCalendar();
calendar.nextAvailable(LocalDate.of(2024, 8, 15), DayOfWeek.SATURDAY);
// => LocalDate.of(2024, 8, 17)
calendar.nextAvailable(LocalDate.of(2024, 8, 17), DayOfWeek.SATURDAY);
// => LocalDate.of(2024, 8, 17)
O salão fecha o caixa no último dia de cada mês.
Só que o salão não abre nos fins de semana, então, quando o último dia do mês cai em um sábado ou em um domingo, o caixa é fechado na sexta-feira anterior.
Implemente o método SalonCalendar.bookkeepingDay() que recebe um ano e um mês, e retorna o dia de fechamento do caixa daquele mês:
SalonCalendar calendar = new SalonCalendar();
calendar.bookkeepingDay(2024, Month.SEPTEMBER);
// => LocalDate.of(2024, 9, 30)
calendar.bookkeepingDay(2024, Month.AUGUST);
// => LocalDate.of(2024, 8, 30)
A equipe recebe no dia 15 de cada mês, com a mesma regra de fim de semana: quando o dia 15 cai em um sábado ou em um domingo, o pagamento sai na sexta-feira anterior. Em vez de escrever a regra uma segunda vez, transforme-a em um ajustador reutilizável.
Implemente o método SalonCalendar.precedingWorkday() que retorna um TemporalAdjuster que deixa um dia de semana inalterado e move um sábado ou um domingo para a sexta-feira anterior.
Depois implemente o método SalonCalendar.payday(), que recebe um ano e um mês, usando esse ajustador:
SalonCalendar calendar = new SalonCalendar();
LocalDate.of(2024, 6, 15).with(calendar.precedingWorkday());
// => LocalDate.of(2024, 6, 14)
calendar.payday(2024, Month.JUNE);
// => LocalDate.of(2024, 6, 14)
calendar.payday(2024, Month.AUGUST);
// => LocalDate.of(2024, 8, 15)
Fique à vontade para voltar e usar o ajustador em bookkeepingDay() também.
Crie sua conta no Exercism para aprender e dominar Java com 27 conceitos159 exercícios e mentoria humana de verdade, tudo de graça.