La mayoria de los problemas en el desarrollo de software suelen surgir de un conocimiento pobre e inconsistente del dominio en cuestion. Un tema aparentemente tan simple como la representacion, serializacion y gestion del tiempo puede causar facilmente una serie de problemas tanto al programador novato como al experimentado.

En este post, veremos que no hace falta ser un Senor del Tiempo para comprender los poquisimos conceptos simples necesarios para no caer en el infierno de la gestion del tiempo.

El tiempo no existe. Los relojes existen.

Representacion

Una pregunta tan simple como "Que hora es?" presupone una serie de sutilezas contextuales que son obvias para el cerebro humano, pero se convierten en un completo sinsentido para un ordenador.

Por ejemplo, si me estuvieras preguntando que hora es ahora mismo, podria decir: "Son las 15:39" y, si fueras un colega en mi oficina, esa seria informacion suficiente para deducir que son las 15:39 CEST. Esto se debe a que ya estarias en posesion de algunos bits de informacion contextual importantes como

  • es por la tarde porque ya hemos almorzado
  • estamos en Roma, por lo tanto nuestra zona horaria es la Hora Central Europea (CET) o la Hora de Verano de Europa Central (CEST)
  • cambiamos al horario de verano hace unas semanas, asi que la zona horaria actual debe ser la Hora de Verano de Europa Central

15:39 es una representacion conveniente del tiempo siempre y cuando estemos en posesion de los bits contextuales. Para representar el tiempo de manera universal, deberian tener una idea de que son UTC y las zonas horarias.

Por favor, no confundan UTC con GMT: aunque su hora coincide, son dos cosas diferentes: uno es un estandar universal mientras que el otro es una zona horaria. Cuando alguien dice que usa GMT, a menos que esa persona tenga un divertido acento escoces, lo que realmente quiere decir es UTC.

Como radioaficionado, tengo contactos con personas de todo el mundo. Cada operador esta obligado a mantener un registro de sus contactos, y normalmente intercambiamos las llamadas tarjetas QSL, que son una confirmacion escrita del contacto. Por supuesto, una tarjeta QSL debe reportar la hora exacta del contacto de radio y por convencion es en UTC. Se que cuando recibo una tarjeta QSL de cualquier colega radioaficionado, sin importar donde este ubicado en todo el mundo, puedo buscar el contacto en mi registro y la informacion de fecha y hora va a coincidir, ya que ambos nos adherimos al mismo estandar: UTC.

Ahora, supongamos que tengo que programar un chat de Skype con un colega desarrollador en los Estados Unidos. Podria escribirle un correo y decir algo como "nos vemos el 2/3". En Italia, eso seria el segundo dia del mes de marzo, pero para una persona estadounidense, eso seria el tercer dia del mes de febrero. Como pueden ver, nuestro chat nunca va a ocurrir.

Estos son solo algunos ejemplos del tipo de problemas que podrian surgir al representar informacion de fecha y hora. Afortunadamente, hay una solucion a los enigmas de la representacion, a saber, el estandar ISO 8601 o, mejor aun, RFC 3339.

Solo para darles un ejemplo, en RFC 3339, 1994-11-05T08:15:30-05:00 corresponde al 5 de noviembre de 1994, 8:15:30 am, Hora Estandar del Este de los Estados Unidos. 1994-11-05T13:15:30Z corresponde al mismo instante (la Z significa UTC). Mismo instante, diferentes representaciones.

RFC 3339 tambien tiene el bonito efecto secundario de proporcionar ordenamiento natural en sistemas que utilizan el orden lexicografico (como los sistemas de archivos) porque la informacion esta organizada de la mas a la menos significativa, es decir, ano, mes, dia, hora, minuto, segundo, fraccion de segundo1.

Incluso si solo estan manejando horas locales en su software, deben saber que, a menos que tambien muestren la zona horaria, nunca pueden estar seguros de la hora. No puedo recordar cuantas veces un desarrollador me ha pedido que arregle la hora en el servidor, solo para descubrir que su software estaba imprimiendo la hora en UTC.

En el momento de la visualizacion, esta bien manejar representaciones parciales del tiempo porque la experiencia de usuario asi lo requiere. Solo asegurense, al depurar, de imprimir el conjunto completo de informacion, incluyendo la zona horaria, de lo contrario nunca pueden estar seguros de que lo que estan viendo es lo que realmente creen que es.

Aunque un momento dado en el tiempo es inmutable, hay un numero arbitrario de formas de expresarlo. Y ni siquiera hemos hablado de los calendarios juliano o indio o de cosas como expresar duraciones.

Permitanme resumir algunos puntos clave hasta ahora:

Reloj de Volver al Futuro

Serializacion

Hablando de software, la serializacion es un proceso donde se toma el estado de un objeto y se detalla de tal manera que pueda ser posteriormente reconstruido por completo, exactamente como el original, utilizando la informacion detallada (serializada). Piensen en un archivo xml o json:

{
  "person": {
    "name": "Mirko",
    "surname": "Caserta",
    "class": "nerd"
  }
}

Esta es la forma serializada de una peculiar instancia imaginaria de la clase persona.

En el mundo binario de los ordenadores, el tiempo se serializa y almacena normalmente utilizando la convencion del tiempo Unix. Mientras escribo esto, mi tiempo Unix es 1366191727. Es decir: 1366191727 segundos han pasado desde el 1 de enero de 1970 a las 00:00 UTC. No es una forma bastante inteligente, consistente y compacta de representar una pletora de informacion, como 17 de abril de 2013 @ 11:42:07am CEST?

El tiempo Unix es solo otra representacion arbitraria de un momento dado en el tiempo, aunque no muy legible para los humanos. Pero pueden tomar ese numero, escribirlo en un trozo de papel, pegarlo a una paloma mensajera, y su destinatario seria capaz de descifrar su mensaje vital simplemente recurriendo a Internet y visitando un sitio como unixtimestamp.com o currentmillis.com.

Si son adictos a la linea de comandos como yo, en sistemas Linux pueden usar:

$ date -d @1366191727
Wed Apr 17 11:42:07 CEST 2013

Sin embargo, en sistemas derivados de BSD como Mac OS X, date -d no funciona asi que tienen que usar en su lugar:

$ date -r 1366191727
Wed Apr 17 11:42:07 CEST 2013

Igual que pueden escribir ese numero en un trozo de papel y despues devolver el instante completo a la vida, pueden almacenarlo en un archivo o en una fila de su RDBMS favorito. Aunque puede que quieran comunicarse con su RDBMS usando un driver apropiado y pasandole una instancia de fecha simple; su driver se encargara entonces de la conversion al formato de serializacion subyacente de la base de datos para instancias de tiempo nativas.

Al almacenar el tiempo usando un formato nativo, obtienen gratuitamente las excelentes funcionalidades de formateo, ordenamiento, consulta, etc. de su RDBMS para el tiempo, asi que puede que quieran pensarlo dos veces antes de almacenar timestamps Unix simples en, digamos, Oracle.

Solo asegurense de saber a que zona horaria se refiere su timestamp Unix, o podrian confundirse despues en el momento de la deserializacion. Por defecto, un timestamp Unix esta en UTC. Si usan las librerias de su sistema, deberian estar bien.

Cuando trabajen con bases de datos, usen los tipos de datos mas apropiados. Por ejemplo en Oracle, hay cuatro tipos de datos diferentes: DATE, TIMESTAMP, TIMESTAMP WITH TIME ZONE y TIMESTAMP WITH LOCAL TIME ZONE. Ademas, las bases de datos normalmente tienen un concepto de zona horaria de la base de datos y zona horaria de la sesion, asi que asegurense de entender como su base de datos especifica los esta usando. Un usuario que abre una sesion con zona horaria A va a ver valores diferentes que un usuario que se conecta con zona horaria B.

ISO 8601 tambien es un favorito para la serializacion. De hecho, se usa en el estandar XML Schema. La mayoria de los frameworks xml son nativamente capaces de serializar y deserializar de ida y vuelta entre xs:date, xs:time y xs:dateTime y el formato nativo de su lenguaje de programacion (y viceversa). Lo mismo aplica para json. Solo tengan cuidado al manejar representaciones parciales: por ejemplo, si omiten la zona horaria, asegurense de acordar previamente una por defecto con su contraparte comunicante (normalmente UTC o su zona horaria local si ambos estan en la misma).

Gestion

Antes que nada, si creen que pueden escribir su propia libreria de software para la gestion del tiempo, o incluso escribir una pequena rutina que sume o reste valores arbitrarios de la hora del dia, permitanme mostrarles el codigo fuente de las clases java.util.Date y java.util.GregorianCalendar del JDK 7, que pesan respectivamente 1331 y 3179 lineas de codigo.

De acuerdo, estos probablemente no son los mejores ejemplos de rutinas de software que manejan el tiempo, lo admito. Por eso se escribieron librerias Java como Joda Time. De hecho, Joda Time se hizo tan popular que dio origen a JSR-310 y ahora es parte del JDK 8.

El uso de frameworks de tiempo populares, bien disenados e implementados les salvara la vida. En serio. Tomense el tiempo para familiarizarse con la API de su eleccion.

Tareas Comunes de Tiempo en Java

Veamos como todo esto se traduce en codigo java. Cualquier lenguaje sera por supuesto diferente pero todo lo que estoy haciendo aqui deberia ser posible en el lenguaje de su eleccion.

Por favor, no usen java.util.Date ni java.util.Calendar. Ya no usamos esas clases. La nueva API de tiempo esta en el paquete java.time.

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;

class Main {
  public static void main(String[] args) {
    ZoneId systemDefault = ZoneId.systemDefault();
    System.out.println("systemDefault = " + systemDefault);

    long now = System.currentTimeMillis();
    System.out.println("now = " + now);

    LocalDate localDate = LocalDate.now();
    System.out.println("localDate = " + localDate);

    LocalDateTime localDateTime = LocalDateTime.now();
    System.out.println("localDateTime = " + localDateTime);

    LocalDateTime utc = LocalDateTime.now(ZoneId.of("UTC"));
    System.out.println("utc = " + utc);

    ZonedDateTime zonedDateTime1 = ZonedDateTime.now();
    System.out.println("zonedDateTime1 = " + zonedDateTime1);

    ZonedDateTime zonedDateTime2 = ZonedDateTime.now(ZoneId.of("UTC"));
    System.out.println("zonedDateTime2 = " + zonedDateTime2);

    String iso8601 = zonedDateTime2.format(DateTimeFormatter.ISO_INSTANT);
    System.out.println("iso8601 = " + iso8601);

    ZonedDateTime zonedDateTime3 = zonedDateTime2.plus(Duration.ofDays(7));
    System.out.println("zonedDateTime3 = " + zonedDateTime3);

    Instant nowAsInstant = Instant.ofEpochMilli(now);
    System.out.println("nowAsInstant = " + nowAsInstant);

    ZonedDateTime nowAsInstantInRome = nowAsInstant.atZone(ZoneId.of("Europe/Rome"));
    System.out.println("nowAsInstantInRome = " + nowAsInstantInRome);

    LocalDateTime romeLocalTime = nowAsInstantInRome.toLocalDateTime();
    System.out.println("romeLocalTime = " + romeLocalTime);

    LocalDate localDateInRome = nowAsInstantInRome.toLocalDate();
    System.out.println("localDateInRome = " + localDateInRome);

    LocalTime localTimeInRome = nowAsInstantInRome.toLocalTime();
    System.out.println("localTimeInRome = " + localTimeInRome);

    String shortTimeInRome = nowAsInstantInRome.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT));
    System.out.println("shortTimeInRome = " + shortTimeInRome);

    String evenShorterTimeInRome = nowAsInstantInRome.format(DateTimeFormatter.ofPattern("HH:mm"));
    System.out.println("evenShorterTimeInRome = " + evenShorterTimeInRome);
  }
}

Si ejecutan este codigo con java Main.java, deberian ver algo como esto:

systemDefault = Europe/Rome
now = 1753718631998
localDate = 2025-07-28
localDateTime = 2025-07-28T10:03:51.999991
utc = 2025-07-28T16:03:52.000089
zonedDateTime1 = 2025-07-28T10:03:52.000532-06:00[Europe/Rome]
zonedDateTime2 = 2025-07-28T16:03:52.000620Z[UTC]
iso8601 = 2025-07-28T16:03:52.000620Z
zonedDateTime3 = 2025-08-04T16:03:52.000620Z[UTC]
nowAsInstant = 2025-07-28T16:03:51.998Z
nowAsInstantInRome = 2025-07-28T18:03:51.998+02:00[Europe/Rome]
romeLocalTime = 2025-07-28T18:03:51.998
localDateInRome = 2025-07-28
localTimeInRome = 18:03:51.998
shortTimeInRome = 6:03PM
evenShorterTimeInRome = 18:03

Pruebas

En Java hay una clase Clock que permite conectar una implementacion de reloj arbitrariamente configurable para su uso en la API de tiempo. Esto es especialmente util en pruebas unitarias y depuracion. Otros lenguajes deberian tener una funcionalidad equivalente.

https://docs.oracle.com/javase/8/docs/api/java/time/Clock.html

Recursos Adicionales

Aqui hay algunos enlaces utiles que he acumulado con el tiempo:

Memes

Elon Musk: "Estoy llevando gente a Marte!", Desarrolladores: "Fantastico, mas zonas horarias que soportar".

Perro del horario de verano

Horario de verano? Que tal inversiones diurnas?

Amigo: "Que paso?", Yo: "Tuve que trabajar con zonas horarias hoy".

Meme del 1 de enero de 1970

Meme Boeing 787

xkcd: Consensus Time

Desarrolladores teniendo que implementar otro cambio de zona horaria mas

Desarrolladores teniendo que explicar...

Dias desde el ultimo problema de zona horaria: 0

Relojes atrasados

Picard DST

Meme del 1 de enero

Un mapa completo de todos los paises que usan el formato de fecha MMDDYYYY

Comic de Shen sobre el formato de EE.UU.

Cita perfecta

DST

Trabajadores nocturnos

XKCD: DateTime

Paises Bajos

1

asumiendo que se usa la misma zona horaria en todas partes