Java TemporalAdjusters
Calcola date relative in Java con TemporalAdjusters: firstDayOfMonth, next, previous e aggiustatori personalizzati.
plusDays(7) aggiunge sette giorni. withDayOfMonth(15) salta al 15. Questi due metodi coprono i casi semplici. Un TemporalAdjuster è un oggetto a forma di funzione che si passa a Temporal.with(adjuster) per gestire i casi in cui la nuova data dipende da quella corrente in modo più complesso: "il primo lunedì del mese prossimo," "l'ultimo giorno di questo trimestre," "il Ringraziamento americano dell'anno prossimo."
L'interfaccia ha un solo metodo:
@FunctionalInterface
interface TemporalAdjuster {
Temporal adjustInto(Temporal temporal);
}Di solito non la si implementa direttamente. La classe java.time.temporal.TemporalAdjusters (nota la s — Adjusters, plurale) è un kit di circa una dozzina di aggiustatori integrati che coprono i casi comuni, e LocalDate.with(adjuster) è il modo per applicarli.
Il catalogo integrato
Importa staticamente; il codice risulta più leggibile in questo modo:
import static java.time.temporal.TemporalAdjusters.*;Poi:
| Aggiustatore | Effetto |
|---|---|
firstDayOfMonth() | giorno → primo giorno dello stesso mese |
lastDayOfMonth() | giorno → ultimo giorno dello stesso mese |
firstDayOfNextMonth() | giorno → primo giorno del mese successivo |
firstDayOfYear() | giorno → 1° gennaio dello stesso anno |
lastDayOfYear() | giorno → 31 dicembre dello stesso anno |
firstDayOfNextYear() | giorno → 1° gennaio dell'anno successivo |
next(DayOfWeek dow) | il prossimo dow strettamente dopo la data |
nextOrSame(DayOfWeek dow) | oggi se è dow, altrimenti il prossimo dow |
previous(DayOfWeek dow) | il dow più recente strettamente prima della data |
previousOrSame(DayOfWeek dow) | oggi se è dow, altrimenti il precedente dow |
dayOfWeekInMonth(int ord, DayOfWeek dow) | l'ennesimo giorno della settimana nel mese: dayOfWeekInMonth(3, MONDAY) → 3° lunedì |
firstInMonth(DayOfWeek dow) | primo dow del mese (equivalente a dayOfWeekInMonth(1, dow)) |
lastInMonth(DayOfWeek dow) | ultimo dow del mese |
Le due metà rispondono a "qual è il primo/ultimo X" (metà superiore) e "qual è il prossimo/precedente X" (metà inferiore). Concatenali quando la domanda è più complessa:
LocalDate firstMondayNextMonth = today.with(firstDayOfNextMonth()).with(nextOrSame(DayOfWeek.MONDAY));Questo significa "il primo lunedì a partire dal primo del mese prossimo." Leggi da sinistra a destra; ogni with è un passaggio.
Applicare con with(adjuster)
LocalDate today = LocalDate.now();
LocalDate eom = today.with(lastDayOfMonth());
LocalDate nextMonday = today.with(next(DayOfWeek.MONDAY));
LocalDate thirdFriday = today.with(dayOfWeekInMonth(3, DayOfWeek.FRIDAY));with esiste su ogni Temporal: LocalDate, LocalDateTime, ZonedDateTime, anche OffsetDateTime. L'aggiustatore tocca solo le componenti della data — applicare lastDayOfMonth() a un LocalDateTime lascia invariata l'ora.
Gli aggiustatori sono totali: restituiscono sempre un risultato. Non esiste un percorso di eccezione "cosa succede se oggi non è nel mese giusto" — lastDayOfMonth() a partire dal 31 gennaio è ancora il 31 gennaio (l'ultimo giorno è se stesso).
Aggiustatori lambda
Poiché TemporalAdjuster è una @FunctionalInterface, puoi scriverne uno tuo con una lambda:
TemporalAdjuster nextWorkingDay = t -> {
LocalDate d = LocalDate.from(t).plusDays(1);
while (d.getDayOfWeek() == DayOfWeek.SATURDAY || d.getDayOfWeek() == DayOfWeek.SUNDAY) {
d = d.plusDays(1);
}
return d;
};
LocalDate friday = LocalDate.of(2025, 11, 7);
LocalDate monday = friday.with(nextWorkingDay); // 2025-11-10Il pattern: converti il Temporal in ingresso nel tipo di data che vuoi (LocalDate.from(t)), calcola la nuova data, restituiscila. Il tipo di ritorno è Temporal (l'interfaccia), ma il JDK accetta il tipo concreto restituito.
Per un uso occasionale questo è eccessivo — basta inserire la logica direttamente nel punto di chiamata. Per un calcolo che userai in più punti (giorno lavorativo successivo, ultimo giorno del trimestre, festività osservata), impacchettarlo come aggiustatore mantiene leggibili i punti di chiamata.
ofDateAdjuster per il caso lambda semplice
Se il tuo aggiustatore lavora solo con LocalDate (il caso comune), TemporalAdjusters.ofDateAdjuster(UnaryOperator<LocalDate>) è la factory più pulita:
TemporalAdjuster nextWorkingDay = TemporalAdjusters.ofDateAdjuster(d -> {
LocalDate result = d.plusDays(1);
while (result.getDayOfWeek().getValue() > 5) {
result = result.plusDays(1);
}
return result;
});La lambda prende e restituisce una LocalDate. La factory la incapsula come TemporalAdjuster. Questo è ciò che si usa nel 90% dei casi quando si scrivono aggiustatori personalizzati.
Un esempio pratico: date lavorative, festività, fine periodo
Il programma seguente usa gli aggiustatori per il calendario contabile: fine mese per la fatturazione, ultimo giorno lavorativo del mese per la chiusura di cassa, il primo lunedì del mese successivo per una riunione di pianificazione regolare, un aggiustatore "prossimo giorno lavorativo" che tiene conto delle festività, e il calcolo della fine del trimestre.
Cosa trarre dall'esecuzione:
- Gli aggiustatori integrati hanno coperto i casi limite (
firstDayOfMonth,lastDayOfMonth,firstDayOfYear, ecc.) senza alcuna aritmetica da parte tua. Per "questa data allineata all'inizio/fine del suo mese o anno," sono lo strumento giusto — più chiari del calcolo manuale conwithDayOfMonth(1), e immuni alle sorprese sulla lunghezza del mese. next(MONDAY)eprevious(FRIDAY)hanno restituito date strettamente diverse;nextOrSame(TUESDAY)su un martedì ha restituito oggi. Memorizza la distinzione strict-vs-or-same; è la fonte della maggior parte dei bug "sfasamento di una settimana" quando la data di partenza cade proprio nel giorno della settimana target.- La concatenazione
firstDayOfNextMonth().nextOrSame(MONDAY)ha espresso "il primo lunedì a partire dal primo del mese prossimo" in due letture. La catena è una riga; l'equivalente manuale è sei. Concatenare gli aggiustatori è il modo idiomatico per comporli. NEXT_BUSINESS_DAYha saltato il fine settimana e una festività in un colpo solo. Il setHOLIDAYSera un esempio sintetico con due elementi; un'implementazione reale li caricherebbe da un servizio. La forma dell'aggiustatore è la stessa — incapsula il ciclo inTemporalAdjusters.ofDateAdjuster(...)e puoi usarlo nelle chiamatetoday.with(NEXT_BUSINESS_DAY)ovunque.END_OF_QUARTERera un aggiustatore personalizzato di una riga che selezionava il terzo mese del trimestre della data e applicavalastDayOfMonth(). Il punto: le operazioni di dominio complesse appartengono a costantiTemporalAdjustercon nome, dove il punto di chiamata si legge comesomeDate.with(END_OF_QUARTER)invece di inserire un blocco aritmetico di sei righe. Tieni la logica del calendario in un unico posto.
Cosa fare dopo
TemporalAdjusters chiude la moderna API java.time. Gli ultimi due capitoli di questa parte coprono i tipi legacy che incontrerai nel codice più vecchio: Java Legacy Date Class per l'originale java.util.Date, e Java Calendar Class per java.util.Calendar. Entrambi sono ancora presenti per compatibilità; entrambi hanno un percorso di conversione pulito verso java.time.