W3docs

Java TemporalAdjusters

Calcula fechas relativas en Java con TemporalAdjusters: firstDayOfMonth, next, previous y ajustadores personalizados.

plusDays(7) añade siete días. withDayOfMonth(15) salta al día 15. Esos dos cubren los casos simples. Un TemporalAdjuster es un objeto con forma de función que se pasa a Temporal.with(adjuster) para manejar los casos en que la nueva fecha depende de la actual de forma más compleja: "el primer lunes del mes siguiente", "el último día de este trimestre", "el Día de Acción de Gracias del año próximo en Estados Unidos".

La interfaz tiene un solo método:

@FunctionalInterface
interface TemporalAdjuster {
  Temporal adjustInto(Temporal temporal);
}

Normalmente no la implementas tú. La clase java.time.temporal.TemporalAdjusters (nota la sAdjusters, plural) es un conjunto de aproximadamente una docena de ajustadores integrados que cubren los casos comunes, y LocalDate.with(adjuster) es la forma de aplicarlos.

El catálogo integrado

Importa de forma estática; el código se lee mejor así:

import static java.time.temporal.TemporalAdjusters.*;

Luego:

AjustadorEfecto
firstDayOfMonth()día → primer día del mismo mes
lastDayOfMonth()día → último día del mismo mes
firstDayOfNextMonth()día → primer día del mes siguiente
firstDayOfYear()día → 1 de enero del mismo año
lastDayOfYear()día → 31 de diciembre del mismo año
firstDayOfNextYear()día → 1 de enero del año siguiente
next(DayOfWeek dow)el próximo dow estrictamente después de la fecha
nextOrSame(DayOfWeek dow)hoy si es dow, de lo contrario el próximo dow
previous(DayOfWeek dow)el dow más reciente estrictamente antes de la fecha
previousOrSame(DayOfWeek dow)hoy si es dow, de lo contrario el dow anterior
dayOfWeekInMonth(int ord, DayOfWeek dow)el n-ésimo día de la semana del mes: dayOfWeekInMonth(3, MONDAY) → 3er lunes
firstInMonth(DayOfWeek dow)primer dow del mes (equivalente a dayOfWeekInMonth(1, dow))
lastInMonth(DayOfWeek dow)último dow del mes

Las dos mitades responden a "¿cuál es el primero/último X?" (mitad superior) y "¿cuál es el siguiente/anterior X?" (mitad inferior). Encadénalos cuando la pregunta sea más compleja:

LocalDate firstMondayNextMonth = today.with(firstDayOfNextMonth()).with(nextOrSame(DayOfWeek.MONDAY));

Eso es "el primer lunes en o después del primero del mes siguiente". Léelo de izquierda a derecha; cada with es un paso.

Aplicar 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 existe en cada Temporal: LocalDate, LocalDateTime, ZonedDateTime, incluso OffsetDateTime. El ajustador solo toca los componentes de fecha — aplicar lastDayOfMonth() a un LocalDateTime deja la hora sin cambios.

Los ajustadores son totales: siempre devuelven un resultado. No existe un camino de excepción "¿qué pasa si hoy no está en el mes correcto?" — lastDayOfMonth() desde el 31 de enero sigue siendo el 31 de enero (el último día es él mismo).

Ajustadores lambda

Como TemporalAdjuster es una @FunctionalInterface, puedes escribir el tuyo propio 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-10

El patrón: convierte el Temporal entrante al tipo de fecha que quieras (LocalDate.from(t)), calcula la nueva fecha, devuélvela. El tipo de retorno es Temporal (la interfaz), pero el JDK acepta el retorno concreto.

Para un uso puntual esto es excesivo — simplemente inserta la lógica junto al punto de llamada. Para un cálculo que usarás en múltiples lugares (día hábil siguiente, último día del trimestre, festivo observado), empaquetarlo como ajustador mantiene los puntos de llamada legibles.

ofDateAdjuster para el caso simple de lambda

Si tu ajustador trabaja solo con LocalDate (el caso común), TemporalAdjusters.ofDateAdjuster(UnaryOperator<LocalDate>) es la fábrica más limpia:

TemporalAdjuster nextWorkingDay = TemporalAdjusters.ofDateAdjuster(d -> {
  LocalDate result = d.plusDays(1);
  while (result.getDayOfWeek().getValue() > 5) {
    result = result.plusDays(1);
  }
  return result;
});

La lambda toma y devuelve un LocalDate. La fábrica lo envuelve como TemporalAdjuster. Esto es lo que usarás el 90% del tiempo al escribir ajustadores personalizados.

Un ejemplo completo: fechas hábiles, festivos y fin de período

El programa siguiente usa ajustadores para el calendario contable: fin de mes para facturación, último día hábil del mes para el cierre de caja, el primer lunes del mes siguiente para una reunión de planificación periódica, un ajustador "siguiente día hábil" con festivos, y cálculo del fin de trimestre.

java— editable, runs on the server

Lo que se puede extraer de la ejecución:

  • Los ajustadores integrados cubren los casos límite (firstDayOfMonth, lastDayOfMonth, firstDayOfYear, etc.) sin ninguna aritmética de tu parte. Para "esta fecha ajustada al inicio/fin de su mes o año", son la herramienta correcta — más claros que el cálculo manual con withDayOfMonth(1), e inmunes a las sorpresas por la longitud del mes.
  • next(MONDAY) y previous(FRIDAY) devolvieron fechas estrictamente distintas; nextOrSame(TUESDAY) en un martes devolvió hoy. Memoriza la distinción estricto-versus-o-igual; es la fuente de la mayoría de los bugs de "desfase de una semana" cuando la fecha de inicio cae en el día de la semana objetivo.
  • El encadenado firstDayOfNextMonth().nextOrSame(MONDAY) expresó "el primer lunes en o después del primero del mes siguiente" en dos lecturas. La cadena es una línea; el equivalente manual son seis. Encadenar ajustadores es la forma idiomática de componerlos.
  • NEXT_BUSINESS_DAY saltó el fin de semana y un festivo de una vez. El conjunto HOLIDAYS fue un ejemplo sintético de dos elementos; una implementación real lo cargaría desde un servicio. La forma del ajustador es la misma — envuelve el bucle en TemporalAdjusters.ofDateAdjuster(...) y puedes usarlo en llamadas today.with(NEXT_BUSINESS_DAY) en todas partes.
  • END_OF_QUARTER fue un ajustador personalizado de una línea que seleccionó el tercer mes del trimestre de la fecha y aplicó lastDayOfMonth(). El punto: las operaciones de dominio complejas pertenecen a constantes TemporalAdjuster con nombre, donde el punto de llamada se lee someDate.with(END_OF_QUARTER) en lugar de insertar un bloque aritmético de seis líneas. Mantén la lógica de calendario en un solo lugar.

Qué sigue

TemporalAdjusters cierra la API moderna java.time. Los dos últimos capítulos de esta parte cubren los tipos heredados que encontrarás en código antiguo: Java Legacy Date Class para el java.util.Date original, y Java Calendar Class para java.util.Calendar. Ambos siguen presentes por compatibilidad; ambos tienen una ruta de conversión limpia a java.time.

Práctica

Práctica
Necesitas 'el primer lunes del mes siguiente' a partir de cualquier fecha dada. ¿Qué expresión devuelve ese valor de forma más idiomática?
Necesitas 'el primer lunes del mes siguiente' a partir de cualquier fecha dada. ¿Qué expresión devuelve ese valor de forma más idiomática?
Was this page helpful?