Percursos
/
Java
Java
/
Exercícios
/
Clientes Habituais do Salão de Beleza
Clientes Habituais do Salão de Beleza

Clientes Habituais do Salão de Beleza

Exercício de aprendizagem

Introdução

Temporal Adjusters

Quando trabalhas com objetos java.time como LocalDate, muitas vezes precisas de fazer ajustes de datas complexos, como encontrar a "segunda terça-feira do mês". Em vez de calculares isto manualmente, podes usar TemporalAdjusters.

A classe utilitária java.time.temporal.TemporalAdjusters (atenção ao "s" do plural) disponibiliza métodos estáticos para muitos ajustes de datas comuns. Aplicas estes ajustes com o método .with() num objeto temporal (como LocalDate).

Note

Usar with(TemporalAdjuster) devolve uma nova instância e não atualiza a instância existente, porque 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

Eis alguns ajustadores essenciais para cálculos de agendamento:

Encontrar a enésima ocorrência de um dia da semana

Usa 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

Encontrar o primeiro/último dia da semana no mês

Usa 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

Ajustes relativos (next/previous)

Ajustadores como next(), previous(), nextOrSame() e previousOrSame() encontram a ocorrência seguinte ou anterior de um determinado dia da semana, em relação à data atual.

  • next()/previous(): excluem a data atual se coincidir.
  • nextOrSame()/previousOrSame(): incluem a data atual se coincidir.
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

Ajustadores personalizados

TemporalAdjuster é uma interface funcional, por isso podes escrever um ajustador teu como uma lambda que recebe o objeto temporal a ajustar e devolve o objeto já ajustado. Aplica-se com .with() tal 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 estes ajustadores da classe TemporalAdjusters pode simplificar bastante as tarefas de manipulação de datas.

Instruções

O salão de beleza de Nova Iorque que ajudaste no exercício Marcações de Beleza está a correr bem, e muitos dos seus clientes já são habituais. Em vez de marcar cada visita em separado, um cliente habitual tem um acordo fixo: «todas as segundas terças-feiras», «a última sexta-feira do mês», e assim por diante. A dona do salão pediu-te um auxiliar que descubra as datas por trás desses acordos, bem como as datas da rotina mensal do próprio salão.

1. Encontrar a marcação fixa de um cliente habitual

Implementa 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. Devolve a data dessa 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)

2. Encontrar a marcação de fim de mês de um cliente habitual

Alguns clientes habituais preferem a última ocorrência do seu dia no mês. Implementa o método SalonCalendar.lastAppointment() que recebe um ano, um mês e um dia da semana, e devolve 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)

3. Encontrar o próximo dia disponível para um cliente sem marcação

Um cliente no balcão pede «o próximo sábado». Se o dia em que faz o pedido já for um sábado, quer dizer esse mesmo dia. Implementa o método SalonCalendar.nextAvailable() que recebe uma data e um dia da semana, e devolve a primeira data igual ou posterior à data indicada que calhe 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)

4. Encontrar o dia de fecho de contas

O salão fecha as contas no último dia de cada mês. No entanto, ao fim de semana o salão está fechado, por isso, quando o último dia do mês calha a um sábado ou a um domingo, as contas fecham-se na sexta-feira anterior. Implementa o método SalonCalendar.bookkeepingDay() que recebe um ano e um mês, e devolve o dia de fecho de contas desse 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)

5. Reutilizar a regra do fim de semana para o dia de pagamento

Os funcionários recebem a 15 de cada mês, com a mesma regra de fim de semana: quando o dia 15 calha a um sábado ou a um domingo, o pagamento é na sexta-feira anterior. Em vez de escreveres a regra uma segunda vez, transforma-a num ajustador reutilizável.

Implementa o método SalonCalendar.precedingWorkday() que devolve um TemporalAdjuster que deixa um dia útil inalterado e move um sábado ou um domingo para a sexta-feira anterior. Depois implementa 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)

Fica à vontade para voltar atrás e usar também o ajustador no bookkeepingDay().

Editar via GitHub A ligação abre numa nova janela ou separador
Java Exercism

Estás pronto para começar Clientes Habituais do Salão de Beleza?

Inscreve-te no Exercism para aprenderes e dominares Java com 27 conceitos159 exercícios, e mentoria humana real, tudo grátis.