LocalDate fecha sin hora
flowchart TB
LD[LocalDate] --> Fact[Constructores]
Fact --> Now[now]
Fact --> Of[of año mes día]
Fact --> Parse[parse ISO]
LD --> Get[getYear getMonth]
LD --> Op[plusDays minusMonths]
LD --> Cmp[isBefore isAfter compareTo]
LD --> With[withDayOfMonth]
LD --> Period[until LocalDate]
Period --> P[Period días meses años]
LocalDate
La clase LocalDate es una parte fundamental de la API java.time introducida en Java 8, diseñada específicamente para representar una fecha sin hora en el calendario ISO-8601. Esta clase inmutable ofrece una alternativa moderna y más robusta a las antiguas clases Date y Calendar, resolviendo muchas de sus limitaciones y problemas de diseño.
LocalDate almacena únicamente información de año, mes y día, sin incluir componentes de hora ni zona horaria. Esto la hace ideal para representar fechas como cumpleaños, aniversarios, fechas de vencimiento o cualquier otro concepto donde solo importa el día y no la hora exacta.
Creación de objetos LocalDate
Existen varias formas de crear instancias de LocalDate:
- Fecha actual:
// Obtiene la fecha actual del sistema
LocalDate hoy = LocalDate.now();
System.out.println("Fecha actual: " + hoy); // Ejemplo: 2023-11-15
- Fecha específica:
// Crear una fecha específica (año, mes, día)
LocalDate fechaEspecifica = LocalDate.of(2023, 12, 31);
System.out.println("Fecha específica: " + fechaEspecifica); // 2023-12-31
// Alternativa usando Month (enum)
LocalDate navidad = LocalDate.of(2023, Month.DECEMBER, 25);
System.out.println("Navidad: " + navidad); // 2023-12-25
- Desde texto (parsing):
// Convertir texto a LocalDate
LocalDate fechaParseada = LocalDate.parse("2023-07-15");
System.out.println("Fecha parseada: " + fechaParseada); // 2023-07-15
// Con formato personalizado
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate fechaConFormato = LocalDate.parse("15/07/2023", formatter);
System.out.println("Fecha con formato: " + fechaConFormato); // 2023-07-15
Obtención de componentes de la fecha
LocalDate proporciona métodos para extraer los diferentes componentes de una fecha:
LocalDate fecha = LocalDate.of(2023, 11, 15);
// Obtener componentes individuales
int anno = fecha.getYear(); // 2023
int mes = fecha.getMonthValue(); // 11
Month mesEnum = fecha.getMonth(); // NOVEMBER
int dia = fecha.getDayOfMonth(); // 15
DayOfWeek diaSemana = fecha.getDayOfWeek(); // WEDNESDAY
int diaDeLaSemana = fecha.getDayOfWeek().getValue(); // 3 (lunes=1, domingo=7)
int diaDelAnno = fecha.getDayOfYear(); // 319
Manipulación de fechas
Una característica clave de LocalDate es su inmutabilidad, lo que significa que cada operación que modifica una fecha devuelve una nueva instancia. Esto evita efectos secundarios no deseados y facilita el trabajo con fechas en entornos concurrentes:
- Sumar o restar períodos:
LocalDate fecha = LocalDate.of(2023, 11, 15);
// Sumar días, semanas, meses o años
LocalDate manana = fecha.plusDays(1);
LocalDate siguienteSemana = fecha.plusWeeks(1);
LocalDate siguienteMes = fecha.plusMonths(1);
LocalDate siguienteAnno = fecha.plusYears(1);
// Restar períodos
LocalDate ayer = fecha.minusDays(1);
LocalDate semanaAnterior = fecha.minusWeeks(1);
LocalDate mesAnterior = fecha.minusMonths(1);
LocalDate annoAnterior = fecha.minusYears(1);
- Método plus/minus genérico:
// Usando Period para representar un intervalo de tiempo
Period periodo = Period.of(1, 2, 3); // 1 año, 2 meses, 3 días
LocalDate nuevaFecha = fecha.plus(periodo); // 2025-01-18
// Usando ChronoUnit para unidades específicas
LocalDate tresMesesDespues = fecha.plus(3, ChronoUnit.MONTHS); // 2024-02-15
Ajustadores de fechas
Los ajustadores temporales (TemporalAdjusters) permiten realizar operaciones más complejas sobre fechas:
LocalDate fecha = LocalDate.of(2023, 11, 15);
// Primer día del mes
LocalDate primerDia = fecha.with(TemporalAdjusters.firstDayOfMonth()); // 2023-11-01
// Último día del mes
LocalDate ultimoDia = fecha.with(TemporalAdjusters.lastDayOfMonth()); // 2023-11-30
// Próximo lunes
LocalDate proximoLunes = fecha.with(TemporalAdjusters.next(DayOfWeek.MONDAY)); // 2023-11-20
// Día específico del mes
LocalDate dia15 = fecha.withDayOfMonth(15); // 2023-11-15
// Cambiar mes y año
LocalDate navidad = fecha.withMonth(12).withDayOfMonth(25); // 2023-12-25
Comparación de fechas
LocalDate implementa la interfaz Comparable, lo que permite comparar fechas fácilmente:
LocalDate fecha1 = LocalDate.of(2023, 11, 15);
LocalDate fecha2 = LocalDate.of(2023, 12, 25);
// Comparación con métodos específicos
boolean esAntes = fecha1.isBefore(fecha2); // true
boolean esDespues = fecha1.isAfter(fecha2); // false
boolean esIgual = fecha1.isEqual(fecha2); // false
// Comparación con compareTo
int resultado = fecha1.compareTo(fecha2); // negativo (-1) porque fecha1 es anterior
Cálculo de períodos entre fechas
Para calcular la diferencia entre dos fechas, podemos usar Period o ChronoUnit:
LocalDate inicio = LocalDate.of(2023, 1, 1);
LocalDate fin = LocalDate.of(2023, 12, 31);
// Usando Period
Period periodo = Period.between(inicio, fin);
System.out.println("Período: " + periodo.getYears() + " años, " +
periodo.getMonths() + " meses, " +
periodo.getDays() + " días"); // 0 años, 11 meses, 30 días
// Usando ChronoUnit para unidades específicas
long dias = ChronoUnit.DAYS.between(inicio, fin); // 364
long meses = ChronoUnit.MONTHS.between(inicio, fin); // 11
long annos = ChronoUnit.YEARS.between(inicio, fin); // 0
Verificación de fechas especiales
LocalDate proporciona métodos útiles para verificar características de una fecha:
LocalDate fecha = LocalDate.of(2024, 2, 29);
// Verificar si el año es bisiesto
boolean esBisiesto = fecha.isLeapYear(); // true para 2024
// Verificar longitud del mes
int diasEnMes = fecha.lengthOfMonth(); // 29 para febrero en año bisiesto
int diasEnAnno = fecha.lengthOfYear(); // 366 para año bisiesto
Ejemplo práctico: Calculadora de edad
Un uso común de LocalDate es calcular la edad de una persona:
public static int calcularEdad(LocalDate fechaNacimiento) {
LocalDate hoy = LocalDate.now();
return (int) ChronoUnit.YEARS.between(fechaNacimiento, hoy);
}
// Uso
LocalDate fechaNacimiento = LocalDate.of(1990, 5, 15);
int edad = calcularEdad(fechaNacimiento);
System.out.println("Edad: " + edad + " años");
Ejemplo práctico: Verificar días laborables
Podemos usar LocalDate para determinar si una fecha es un día laborable:
public static boolean esDiaLaborable(LocalDate fecha) {
// Asumimos que los días laborables son de lunes a viernes
DayOfWeek diaSemana = fecha.getDayOfWeek();
return diaSemana != DayOfWeek.SATURDAY && diaSemana != DayOfWeek.SUNDAY;
}
// Uso
LocalDate fecha = LocalDate.of(2023, 11, 15); // Miércoles
System.out.println("¿Es día laborable? " + esDiaLaborable(fecha)); // true
Caso B2B: cálculo de vencimientos en banca y seguros
En entornos bancarios, el cálculo de fechas de vencimiento de préstamos, recibos SEPA y planes de amortización es uno de los pocos lugares donde un error de un día tiene impacto directo en pérdidas y en reclamaciones a Atención al Cliente. La inmutabilidad de LocalDate evita una clase entera de errores comunes con java.util.Calendar, donde una mutación accidental en una rutina compartida provocaba reclamaciones por intereses mal calculados. En aseguradoras que generan miles de pólizas diarias, LocalDate.plusYears(1).minusDays(1) reemplaza la antigua aritmética con Calendar.add(Calendar.YEAR, 1): el nuevo código es trivial de revisar y no se ve afectado por zonas horarias.
En administraciones públicas, los plazos legales (recursos contencioso-administrativos a 60 días hábiles, notificaciones tributarias) se modelan combinando LocalDate con TemporalAdjusters y un calendario de festivos. La separación clara entre fecha (LocalDate) y fecha-hora (LocalDateTime) reduce el riesgo de que un cambio de horario de verano provoque un cómputo incorrecto del último día de plazo, escenario habitual en los sistemas heredados con Date y SimpleDateFormat.
Versiones y disponibilidad
LocalDate está en java.time desde Java 8 (marzo 2014) y forma parte de la API estándar. La especificación se mantiene estable en Java 11 LTS, Java 17 LTS, Java 21 LTS y Java 25 LTS. Vuestro equipo puede asumir disponibilidad universal en cualquier despliegue moderno. Internamente está basada en JSR-310, diseñada por Stephen Colebourne (autor de Joda-Time) tras consensuar las lecciones aprendidas de la librería externa.
Anti-patrones y pitfalls
Mezclar LocalDate con java.util.Date en la misma clase. La conversión Date → LocalDate arrastra zona horaria del sistema y genera resultados distintos en producción y desarrollo. Si vuestro código recibe datos legados, convertid en el límite con instant.atZone(ZoneId.of("Europe/Madrid")).toLocalDate() y trabajad solo con LocalDate internamente.
Usar LocalDate para timestamps. Si la operación necesita hora (transacciones, logs, eventos de auditoría), LocalDate es la elección incorrecta. Usad LocalDateTime o Instant. Errar este criterio provoca pérdidas de información cuando se serializa a JSON.
Persistir como cadena. Algunos equipos guardan fechas como String "yyyy-MM-dd" en bases de datos. PostgreSQL 17, MySQL 8.4 y Oracle 23ai ofrecen tipo DATE nativo: usad el mapeo JPA @Column con LocalDate para evitar reformateos costosos y errores de orden lexicográfico en consultas con ORDER BY.
Asumir formato ISO al parsear. LocalDate.parse("15/07/2023") lanza DateTimeParseException porque el método sin formatter espera ISO. Cuando recibís fechas de un sistema externo (CSV, Excel, integraciones B2B), pasad siempre un DateTimeFormatter explícito.
Comparativa con alternativas
Frente a Joda-Time (org.joda.time.LocalDate), java.time.LocalDate es la API recomendada desde Java 8. Joda-Time sigue mantenida pero su autor recomienda migrar al estándar. Para proyectos en mantenimiento, una migración gradual con un Adapter deja ambos modelos coexistiendo durante semanas.
Frente a java.util.Date y Calendar, la API moderna es inmutable, thread-safe y semánticamente correcta (separa fecha, hora, fecha-hora, instante e intervalo). En revisiones de código de banca y AAPP, sustituir Date por LocalDate reduce las observaciones de seguridad estática (SonarQube, SpotBugs) en lo relativo a fechas mutables y formatos no thread-safe.
Frente a Kotlin kotlinx-datetime, LocalDate es interoperable: si vuestro proyecto Java consume servicios escritos en Kotlin, ambas APIs serializan al mismo formato ISO-8601 sin pérdida.
Documentación oficial
La referencia canónica es la Javadoc de Oracle del paquete java.time para Java 21+ y la JSR-310. Para conversiones a SQL, la Javadoc de java.sql.Date.valueOf(LocalDate) y la guía de drivers JDBC del proveedor concreto.
Ejemplo práctico: Calendario mensual
Podemos generar un calendario mensual simple utilizando LocalDate:
public static void imprimirCalendarioMensual(int anno, int mes) {
LocalDate fecha = LocalDate.of(anno, mes, 1);
int diasEnMes = fecha.lengthOfMonth();
System.out.println("Calendario para " + fecha.getMonth() + " " + anno);
System.out.println("Lu Ma Mi Ju Vi Sa Do");
// Ajustar el primer día de la semana (1 = lunes, ... 7 = domingo)
int diaSemana = fecha.getDayOfWeek().getValue();
// Imprimir espacios para alinear el primer día
for (int i = 1; i < diaSemana; i++) {
System.out.print(" ");
}
// Imprimir los días del mes
for (int dia = 1; dia <= diasEnMes; dia++) {
System.out.printf("%2d ", dia);
// Salto de línea después del domingo
if ((dia + diaSemana - 1) % 7 == 0 || dia == diasEnMes) {
System.out.println();
}
}
}
// Uso
imprimirCalendarioMensual(2023, 11);
Fuentes y referencias
Documentación oficial y recursos externos para profundizar en Java
Documentación oficial de Java
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Java es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Java
Explora más contenido relacionado con Java y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Comprender qué es LocalDate y su propósito en la API java.time. Crear instancias de LocalDate mediante diferentes métodos. Obtener y manipular componentes de fechas de forma inmutable. Realizar comparaciones y cálculos entre fechas. Aplicar LocalDate en ejemplos prácticos como cálculo de edad y generación de calendarios.