Clase Calendar de Java
La clase heredada java.util.Calendar y GregorianCalendar: cómo se relacionan con java.time.
java.util.Calendar es la segunda mitad del par de fecha/hora heredado. Mientras que java.util.Date es simplemente un envoltorio alrededor de un long de milisegundos epoch, Calendar es lo que sabe en qué año, mes y día cae ese milisegundo. Se añadió en Java 1.1 para extraer la aritmética de calendario de Date — por eso la mayoría de los accesores de campo de Date están obsoletos.
En código moderno deberías usar java.time. Pero Calendar sigue apareciendo: en rutas de código antiguo, en bibliotecas escritas antes de Java 8 y en controladores JDBC que no han sido actualizados. Necesitas leerlo, hacer el puente y seguir adelante. Esta página explica cómo crear un Calendar, leer sus campos de forma segura (cuidado con el mes basado en cero), hacer aritmética de calendario y — lo más importante — hacer el puente hacia la API moderna.
Calendar es abstracta
Calendar en sí es una clase abstracta. Nunca llamas new Calendar(...). Obtienes una instancia a través del factory:
Calendar cal = Calendar.getInstance();Esto devuelve una subclase concreta — casi siempre GregorianCalendar — precargada con la hora actual, la zona horaria predeterminada y el locale predeterminado. Puedes ser explícito si lo necesitas:
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.US);GregorianCalendar también tiene constructores públicos, pero el factory es la convención.
Constantes de campo
Calendar expone todo a través de constantes de campo enteras y un get(int field) genérico:
| Campo | Significado | Rango |
|---|---|---|
Calendar.YEAR | Año | año completo, p. ej. 2026 |
Calendar.MONTH | Mes | 0–11 (enero = 0) |
Calendar.DAY_OF_MONTH | Día del mes | 1–31 |
Calendar.DAY_OF_WEEK | Día de la semana | 1–7 (domingo = 1) |
Calendar.HOUR_OF_DAY | Hora | 0–23 |
Calendar.MINUTE | Minuto | 0–59 |
Calendar.SECOND | Segundo | 0–59 |
Calendar.MILLISECOND | Milisegundo | 0–999 |
Dos trampas se esconden en esta tabla: MONTH es basado en cero (Calendar.JANUARY == 0) y DAY_OF_WEEK empieza en domingo. Los errores de desfase por uno en código heredado casi siempre tienen su origen aquí.
Asignación y aritmética
set toma un campo y un valor; add hace aritmética consciente del calendario que desborda en campos mayores:
Calendar cal = Calendar.getInstance();
cal.set(2026, Calendar.MAY, 29); // Y, M, D — month is zero-based
cal.add(Calendar.DAY_OF_MONTH, 5); // → 2026-06-03roll hace lo mismo campo por campo pero no se lleva a campos mayores — roll(DAY_OF_MONTH, 5) en el 30 de mayo da el 4 de mayo, no el 4 de junio. Raramente es lo que quieres.
Las instancias de Calendar son mutables, por lo que pasar una a un método significa entregar un identificador activo. Clona antes de exponer, de la misma forma que harías con un Date.
El puente hacia java.time
El único propósito de trabajar con Calendar hoy en día es salir de él. Dos métodos lo hacen posible:
Instant when = cal.toInstant();
ZoneId zone = cal.getTimeZone().toZoneId();
ZonedDateTime zdt = when.atZone(zone);Desde ZonedDateTime tienes toda la API moderna. En sentido inverso:
Calendar cal = GregorianCalendar.from(zdt); // Java 8+GregorianCalendar.from(ZonedDateTime) es la conversión compatible. Evita el ida y vuelta a través de Date.
Ejemplo práctico
El ejemplo siguiente muestra las dos cosas que harás con Calendar en código real: leer campos de una instancia heredada y hacer el puente a java.time para cualquier cosa no trivial.
Lo que hay que extraer de la ejecución:
Calendar.MONTHpara mayo imprime4. Los meses basados en cero son la mayor fuente de errores en el código heredado de fechas.getInstance(TimeZone)vincula la instancia a una zona — diferente deDate, que no tiene ninguna.add(MONTH, 1)entiende la duración de los meses; obtienes el día correcto del mes siguiente aunque los meses no sean todos de 30 días.cal.toInstant().atZone(cal.getTimeZone().toZoneId())es el puente de una línea haciajava.time.GregorianCalendar.from(ZonedDateTime)te permite devolver unCalendara una API antigua sin perder la zona.
Qué sigue
Esto cierra la Parte 14 — Fecha y Hora. A continuación viene la Parte 15, Multihilo y Concurrencia, comenzando con Multithreading en Java — el modelo que ha dado forma a todas las demás APIs de este libro.