Quando lavori con oggetti java.time come LocalDate, spesso devi eseguire regolazioni di date complesse, come trovare il «secondo martedì del mese».
Invece di calcolarle a mano, puoi usare TemporalAdjusters.
La classe di utilità java.time.temporal.TemporalAdjusters (nota la «s» finale) fornisce metodi statici per molte regolazioni di date comuni.
Applichi queste regolazioni con il metodo .with() su un oggetto temporale (come LocalDate).
Usare with(TemporalAdjuster) restituisce una nuova istanza e non aggiorna quella esistente, perché gli oggetti java.time sono immutabili.
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
Ecco alcuni regolatori fondamentali per i calcoli di pianificazione:
Usa dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek) per trovare l'ennesima occorrenza di un giorno della settimana.
// Find the second Tuesday of August 2024
LocalDate secondTuesday = date.with(TemporalAdjusters.dayOfWeekInMonth(2, DayOfWeek.TUESDAY));
// => 2024-08-13
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
Regolatori come next(), previous(), nextOrSame() e previousOrSame() trovano l'occorrenza successiva o precedente di un dato giorno della settimana, rispetto alla data corrente.
next()/previous(): escludono la data corrente se corrisponde.nextOrSame()/previousOrSame(): includono la data corrente se corrisponde.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 è un'interfaccia funzionale, quindi puoi scrivere un regolatore tutto tuo come lambda che prende l'oggetto temporale da regolare e restituisce quello regolato.
Si applica con .with() esattamente come i regolatori predefiniti, e al suo interno può usarli.
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
Usare questi regolatori della classe TemporalAdjusters può semplificare molto le operazioni sulle date.
Il salone di bellezza di New York che hai aiutato nell'esercizio Prenotazioni per la bellezza sta andando bene, e molti dei suoi clienti sono diventati abituali. Invece di prenotare ogni visita separatamente, un cliente abituale ha un accordo fisso: «ogni secondo martedì», «l'ultimo venerdì del mese» e così via. Il proprietario ti ha chiesto una funzione ausiliaria che calcoli le date di questi accordi, oltre alle date della routine mensile del salone.
Implementa il metodo SalonCalendar.standingAppointment() che accetta un anno, un mese, un'occorrenza (1 per la prima, 2 per la seconda e così via) ed un giorno della settimana.
Restituisce la data di quell'occorrenza del giorno della settimana nel mese:
SalonCalendar calendar = new SalonCalendar();
calendar.standingAppointment(2024, Month.AUGUST, 2, DayOfWeek.TUESDAY);
// => LocalDate.of(2024, 8, 13)
Alcuni clienti abituali preferiscono l'ultima occorrenza del loro giorno nel mese.
Implementa il metodo SalonCalendar.lastAppointment() che accetta un anno, un mese ed un giorno della settimana e restituisce la data dell'ultima occorrenza di quel giorno della settimana nel mese:
SalonCalendar calendar = new SalonCalendar();
calendar.lastAppointment(2024, Month.AUGUST, DayOfWeek.FRIDAY);
// => LocalDate.of(2024, 8, 30)
Un cliente alla reception chiede «il prossimo sabato».
Se il giorno in cui lo chiede è già un sabato, intende proprio quel giorno.
Implementa il metodo SalonCalendar.nextAvailable() che accetta una data ed un giorno della settimana e restituisce la prima data uguale o successiva alla data indicata che cade in quel giorno della settimana:
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)
Il salone chiude i conti l'ultimo giorno di ogni mese.
Il salone, però, è chiuso nel fine settimana: quando l'ultimo giorno del mese cade di sabato o di domenica, i conti si chiudono il venerdì precedente.
Implementa il metodo SalonCalendar.bookkeepingDay() che accetta un anno ed un mese e restituisce il giorno di chiusura dei conti di quel mese:
SalonCalendar calendar = new SalonCalendar();
calendar.bookkeepingDay(2024, Month.SEPTEMBER);
// => LocalDate.of(2024, 9, 30)
calendar.bookkeepingDay(2024, Month.AUGUST);
// => LocalDate.of(2024, 8, 30)
Il personale viene pagato il 15 di ogni mese, con la stessa regola del fine settimana: quando il 15 cade di sabato o di domenica, il giorno di paga è il venerdì precedente. Invece di scrivere la regola una seconda volta, trasformala in un regolatore temporale riutilizzabile.
Implementa il metodo SalonCalendar.precedingWorkday() che restituisce un TemporalAdjuster che lascia invariato un giorno feriale e sposta un sabato o una domenica al venerdì precedente.
Poi implementa il metodo SalonCalendar.payday() che accetta un anno ed un mese, usando quel regolatore:
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)
Sentiti libero di tornare indietro ed usare il regolatore anche in bookkeepingDay().
Iscriviti a Exercism per imparare e padroneggiare Java con 27 concetti159 esercizi e il mentoring di persone reali, tutto gratis.