Tracks
/
Java
Java
/
Ejercicios
/
Clientes habituales del salón de belleza
Clientes habituales del salón de belleza

Clientes habituales del salón de belleza

Ejercicio de aprendizaje

Introducción

Ajustadores temporales

Cuando trabajas con objetos de java.time como LocalDate, muchas veces necesitas hacer ajustes de fechas más complejos, como encontrar «el segundo martes del mes». En lugar de calcularlo a mano, puedes usar TemporalAdjusters.

La clase de utilidades java.time.temporal.TemporalAdjusters (fíjate en la «s» del plural) ofrece métodos estáticos para muchos ajustes de fechas habituales. Estos ajustes los aplicas con el método .with() sobre un objeto temporal (como LocalDate).

Note

Usar with(TemporalAdjuster) devuelve una instancia nueva y no actualiza la instancia existente, ya que los objetos de java.time son inmutables.

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

Estos son algunos ajustadores clave que resultan útiles para calcular programaciones:

Encontrar el enésimo día de la semana

Usa dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek) para encontrar la enésima aparición de un día de la semana.

// Find the second Tuesday of August 2024
LocalDate secondTuesday = date.with(TemporalAdjusters.dayOfWeekInMonth(2, DayOfWeek.TUESDAY));
// => 2024-08-13

Encontrar el primer o el último día de la semana en el mes

Usa firstInMonth(DayOfWeek dayOfWeek) o lastInMonth(DayOfWeek dayOfWeek).

// Find the last Monday of August 2024
LocalDate lastMonday = date.with(TemporalAdjusters.lastInMonth(DayOfWeek.MONDAY));
// => 2024-08-26

Ajustes relativos (siguiente/anterior)

Ajustadores como next(), previous(), nextOrSame() y previousOrSame() encuentran la aparición siguiente o anterior de un día de la semana dado, tomando como referencia la fecha actual.

  • next()/previous(): excluyen la fecha actual si coincide.
  • nextOrSame()/previousOrSame(): incluyen la fecha actual si coincide.
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 es una interfaz funcional, así que puedes escribir tu propio ajustador como una lambda que recibe el objeto temporal que hay que ajustar y devuelve el ya ajustado. Se aplica con .with() igual que los ajustadores predefinidos, y puede usarlos 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 estos ajustadores de la clase TemporalAdjusters puede simplificar muchísimo las tareas de manipulación de fechas.

Instrucciones

El salón de belleza de Nueva York en el que ayudaste en el ejercicio Booking Up For Beauty ha ido muy bien, y muchas de sus clientas se han vuelto habituales. En lugar de reservar cada visita por separado, una clienta habitual tiene un arreglo fijo: «cada segundo martes», «el último viernes del mes», y así sucesivamente. La dueña te ha pedido un ayudante que calcule las fechas que hay detrás de estos arreglos, además de las fechas de la rutina mensual del propio salón.

1. Encuentra la cita fija de una clienta habitual

Implementa el método SalonCalendar.standingAppointment() que toma un año, un mes, una ocurrencia (1 para la primera, 2 para la segunda, y así sucesivamente) y un día de la semana. Devuelve la fecha de esa ocurrencia del día de la semana dentro del mes:

SalonCalendar calendar = new SalonCalendar();
calendar.standingAppointment(2024, Month.AUGUST, 2, DayOfWeek.TUESDAY);
// => LocalDate.of(2024, 8, 13)

2. Encuentra la cita de fin de mes de una clienta habitual

Algunas clientas habituales prefieren la última ocurrencia de su día en el mes. Implementa el método SalonCalendar.lastAppointment() que toma un año, un mes y un día de la semana, y devuelve la fecha de la última ocurrencia de ese día de la semana dentro del mes:

SalonCalendar calendar = new SalonCalendar();
calendar.lastAppointment(2024, Month.AUGUST, DayOfWeek.FRIDAY);
// => LocalDate.of(2024, 8, 30)

3. Encuentra el próximo día disponible para un cliente sin cita

Un cliente en el mostrador pide «el próximo sábado». Si el día en que lo pide ya es sábado, se refiere a ese mismo día. Implementa el método SalonCalendar.nextAvailable() que toma una fecha y un día de la semana, y devuelve la primera fecha igual o posterior a la fecha dada que caiga en ese día de la 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. Encuentra el día de cierre contable

El salón hace el cierre de cuentas el último día de cada mes. Sin embargo, el salón está cerrado los fines de semana, así que cuando el último día del mes cae en sábado o en domingo, las cuentas se cierran el viernes anterior. Implementa el método SalonCalendar.bookkeepingDay() que toma un año y un mes, y devuelve el día de cierre contable de ese mes:

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. Reutiliza la regla del fin de semana para el día de pago

El personal cobra el día 15 de cada mes, con la misma regla del fin de semana: cuando el 15 cae en sábado o en domingo, el día de pago es el viernes anterior. En lugar de escribir la regla por segunda vez, conviértela en un ajustador reutilizable.

Implementa el método SalonCalendar.precedingWorkday() que devuelve un TemporalAdjuster que deja sin cambios un día entre semana y mueve un sábado o un domingo al viernes anterior. Después implementa el método SalonCalendar.payday(), que toma un año y un mes, usando ese 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)

Si quieres, vuelve atrás y usa también el ajustador en bookkeepingDay().

Editar en GitHub El enlace se abre en una ventana o una pestaña nuevas
Java Exercism

¿Todo listo para empezar Clientes habituales del salón de belleza?

Regístrate en Exercism para aprender y dominar Java con 27 conceptos159 ejercicios y mentoría humana real, todo gratis.