DEV Community

El tercer intento de API de fechas de Java

Java es un lenguaje de pogramación con algunos años ya. El problema de los calendarios y las fechas es conocido: cada cuatro años se añade un día más, pero incluso en ciertos escenarios se añade un segundo a un año arbitrariamente. Como esto no se podía representar con la clase Date tal cual estaba planteada, decidieron desactualizar varios de sus métodos, de manera que fuera solo parcialmente funcional, y promocionaron el uso de Calendar en su lugar.

Calendar es una clase mucho más capaz, y solo se le pueden achacar problemas de edad, como el hecho de que no utilice enumerados (enum).

Fueron dos intentos ímprobos, y el segundo de muy buen resultado y ejecución, sobre una materia que ya de por sí es difícil. Por si fuera poco, la información mundial de time zones (zonas horarias), puede cambiar en cualquier momento, por lo que es una API que debe ser actualizada frecuentemente.

Así que los ingenieros de Java han creado LocalDate en el paquete java.time, el tercer intento de plantear una solución a la fecha y hora (spoiler: seguirá teniendo defectos, y se planteará una nueva alternativa).

El caso es que la nueva clase LocalDate proporciona el método getMonth(), que devuelve el mes que está representado en la fecha en sí. Este método devuelve un enumerado Month.

var fecha = LocalDate.now();
final var MESES = new String[]{ "Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre" };

System.out.println( "Fecha: " + fecha.toString() );
System.out.println( "Mes (val): " + MESES[ fecha.getMonth().getValue() ] );
System.out.println( "Mes (ord): " + MESES[ fecha.getMonth().ordinal() ] );
Enter fullscreen mode Exit fullscreen mode

La salida de este código es:

Fecha: 2026-04-07
Mes (val): Mayo
Mes (ord): Abril
Enter fullscreen mode Exit fullscreen mode

¿Hemos creado una fecha de marzo o de abril? A ver, el código se ejecutó el 7 de abril de 2026, fecha de escritura de este texto. Por lo tanto, la fecha (que, correctamente, se ofrece en formato ISO): 2026-04-07 se corresponde perfectamente. El problema viene cuando accedemos al mes con LocalDate.getMonth(). Se devuelve una de las constantes JANUARY, FEBRUARY, MARCH... Y aquí es cuando comienzan los problemas. Si accedemos al valor del enumerado Month, nos encontramos con que JANUARY es... 1. Porque sí, se nos ofrece el valor del enumerado a través de getValue().

Por eso, fecha.getMonth() devuelve 4, y entonces MESES[ fecha.getMonth().getValue() ] devuelve la 5ª posición en el vector, y así el código retorna, incorrectamente, Mayo. Nos han estropeado la forma acostumbrada de obtener el nombre del método.

Al menos, no han tocado el valor del propio enumerado. Es decir, el valor Month.ordinal() sigue siendo el correcto: 3. Para obtener el mismo resultado mediante LocalDate.getValue(), tendremos que restar 1, de ahí que el código anterior deba ser corregido a:

System.out.println( "Mes (val): " + MESES[ fecha.getMonth().getValue() - 1 ] );
System.out.println( "Mes (ord): " + MESES[ fecha.getMonth().ordinal() ] );
Enter fullscreen mode Exit fullscreen mode

A partir de ahora, será necesario escribir MESES[ fecha.getMonth().getValue() -1] si utilizamos getValue(), y continuar escribiendo el mismo código si utilizamos ordinal().

Y no es que getValue() esté mal. Veámos la siguiente tabla:

Método Basado en Ejemplo Concepción
getValue() 1 Enero == 1 Intuitiva
ordinal() 0 Enero == 0 Costumbre

Y es que, efectivamente, la costumbre entre programadores es que los meses empiecen en 0, de manera que se pueda indexar directamente en un vector de meses, como en el ejemplo. Sin embargo, reconoceremos que lo normal, lo que sería intuitivo para un no programador sería que los meses empezasen en 1, es decir, que el índice de la posición de Enero fuese 1.

¿Y por qué me quejo? Pues porque a veces es mejor dejar las cosas como están. Cuando a tu alrededor todo funciona de una manera, cambiar a otra, aunque sea la más correcta, no necesariamente es la mejor idea. Especialmente cuando esa costumbre se ha extendido durante años.

Estamos hablando, en definitiva, de un estándar de facto (que los meses se indexen desde 0), frente a lo que podría ser un estándar por lo intuitivo (o un estándar concreto, el ISO 8601). Lo único cierto, es que desde C las colecciones comienzan a contar en 0, no en 1 (solo BASIC y Pascal (cuando se especifican los límites), lo permiten así).

En C, la forma de obtener el mes es dejando que el sistema rellene una estructura tm.

#include <stdio.h>
#include <time.h>

int main()
{
    time_t t = time( NULL );
    struct tm tm = *localtime(&t);

    printf( "now: %4d-%02d-%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday );
}
Enter fullscreen mode Exit fullscreen mode

La función localtime() es una llamada al kernel del sistema operativo.

Lenguaje Método o función Ejemplo Basado en
C localtime() tm->tm_mon 0
Java Calendar.get() cal.get( Calendar.MONTH ) 0
JavaScript Date.getMonth() new Date().getMonth() + 1 0
Perl localtime() $d, $m, $y = localtime() 0
PHP date(fmt) $current_month = date('n'); 1
Python datetime.month datetime.now().month 1
C# DateTime.month DateTime.now().Month 1
Nim Month int int(month(now())) 1
Gleam month_int() month_int(datetime.now()) 1
V Time.month time.now().month 1
C3 datetime.month datetime::now().month 1
Rust chrono::Datelike chrono::now().month 1
Go Month() time.Now().Month() 1
Zig localtime() tm->tm_mon 0

¿Qué puedo decir? Me quedo con que tengo razón, solo porque para eso es mi columna, la del viejo programador malhumorado.

Pero no se puede ignorar que, a medida que surgen lenguajes de programación más recientes (modernos), cada vez más se fijan en estándares como el RFC 3339, o el ISO 8601, y estos estándares identifican unívocamente enero con el índice 1, lo que no es sorprendente. Al fin y al cabo, que enero se identifique con 0 solo depende de una optimización del lenguaje de programación C, que se basa en que el nombre de un array en C es un puntero al primer elemento, es decir *v, *(v + 0) y v[ 0 ] son todas distintas formas de referirse al primer elemento.

Top comments (0)