Los bugs de zonas horarias sobreviven a muchas pruebas porque la mayoría de días se comporta de forma normal. Luego llega un cambio de daylight saving, una reunión internacional, un país que modifica reglas o una hora local que no existe. El problema no es que el tiempo sea imposible; es que las aplicaciones simplifican demasiado pronto. Una zona horaria no es solo un offset como +02:00. Es una base de reglas históricas y futuras.

Offset y timezone no son lo mismo

Un offset dice la diferencia con UTC en un instante: por ejemplo, dos horas adelante. Una IANA timezone como Europe/Madrid contiene reglas para saber qué offset aplica en fechas distintas. El mismo lugar puede usar offset diferente en verano e invierno.

Guardar solo offset puede ser suficiente para registrar un instante pasado. Para programar eventos futuros, se necesita la zona. Si el usuario quiso “9:00 en Madrid”, el sistema debe conservar esa intención, no solo el offset del día de creación.

Algunas horas locales no existen

Cuando los relojes adelantan por daylight saving, una franja de tiempo puede saltarse. Una cita a las 02:30 quizá no exista en esa fecha y zona. La aplicación debe decidir qué hacer: rechazar, mover al siguiente instante válido o pedir confirmación.

Ignorar el caso produce resultados sorprendentes. Un scheduler puede ejecutar a otra hora sin avisar, o una interfaz puede mostrar una hora que luego no puede guardar. La política debe ser visible para el usuario.

Algunas horas locales ocurren dos veces

Cuando los relojes atrasan, una hora se repite. Las 01:30 pueden referirse a dos instantes distintos. Sin información adicional, convertir local time a instant es ambiguo. Sistemas de calendario deben permitir elegir o aplicar una regla documentada.

Para logs y eventos ya ocurridos, guardar UTC instant evita ambigüedad. Para entradas humanas futuras, conservar timezone y resolver ambigüedades con la biblioteca adecuada es más honesto.

Las reglas cambian por decisiones políticas

Países y regiones modifican daylight saving, offset y nombres de zonas. Las bibliotecas usan bases de datos actualizadas para reflejar esas decisiones. Un sistema que convierte eventos futuros demasiado pronto puede quedar obsoleto si la regla cambia antes de la fecha.

Actualizar tzdata es una tarea operativa, no un detalle menor. Aplicaciones globales deben incluirla en mantenimiento y pruebas. Un contenedor con datos de zona antiguos puede producir horarios incorrectos aunque el código sea correcto.

Fechas locales no son instantes

Un cumpleaños, una fecha de vencimiento o un día de reporte pueden ser local dates sin hora. Convertirlos a medianoche UTC puede moverlos al día anterior o siguiente para algunos usuarios. Si el dominio habla de día calendario, el dato debe conservarse como fecha local.

Del mismo modo, una duración de “24 horas” no siempre equivale a “un día calendario” en una zona con cambios de horario. Sumar días y sumar horas responden preguntas diferentes.

La interfaz debe explicar consecuencias

Cuando un usuario crea una reunión, conviene mostrar zona horaria y vista para participantes. Si edita una serie recurrente, el sistema debe decir si cambia la hora local, el instante o solo la presentación. Las sorpresas temporales son menos costosas cuando aparecen antes de guardar.

Enviar invitaciones actualizadas y registrar auditoría ayuda a resolver disputas. “El sistema movió la reunión” es difícil de investigar sin saber qué regla se aplicó.

APIs deben aceptar valores inequívocos

Un string como 2026-03-29 02:30 sin zona ni offset es incompleto para un instant. Una API debe pedir ISO 8601 con offset para instantes o campos separados para local date, local time y timezone cuando esa sea la intención. Mezclar modelos crea datos irrecuperables.

Los errores de validación deben indicar si la hora es inexistente, ambigua o falta timezone. Un mensaje genérico de “fecha inválida” obliga al cliente a adivinar.

Las pruebas deben incluir transiciones

Probar solo días normales no detecta bugs de calendario. Incluye fechas de inicio y fin de daylight saving, zonas con media hora de offset, usuarios en hemisferios distintos y reglas históricas. También prueba conversiones después de actualizar tzdata.

Los tests con reloj fijo ayudan, pero deben fijar zona explícita. Depender de la timezone de la máquina de CI vuelve los resultados frágiles.

Los datos de ejemplo deben cubrir también cambios futuros. Una regla que funciona para este año puede romperse cuando la base de zonas se actualiza o cuando el país anuncia una excepción. Mantener fixtures alrededor de esas fechas protege migrations, cambios de librería y actualizaciones de contenedores.

Guarda la intención, no solo el resultado

Un sistema confiable distingue instant, local date, local time, timezone y duration. Cada tipo responde una pregunta distinta. Daylight saving deja de ser un bug misterioso cuando el modelo conserva suficiente información para aplicar reglas correctas.

La simplificación adecuada ocurre al mostrar o ejecutar, no al almacenar prematuramente. Perder la zona horaria original no se arregla formateando mejor un timestamp después.