Cómo evitar que los agentes de IA rompan la lógica de husos horarios en jobs diarios
Un estudio de caso muestra cómo forzar reglas estrictas de tiempo y usar tests antes de dejar que un LLM escriba el código del worker.

Dejar que un agente de IA decida la ventana de tiempo para un digest diario es pedir problemas. Si no especificas el huso horario y si el extremo final es exclusivo, el modelo suele inventar una hora de inicio, usar UTC por defecto y aplicar filtros SQL inclusivos. El resultado: eventos de la medianoche se cuentan dos veces o caen en el día equivocado.
De la prompt a la tabla de decisiones
La solución propuesta en este caso de estudio no es una mejor prompt, sino una función pura en Python llamada digest_window. El objetivo es mapear la hora actual, el huso horario del inquilino (tenant) y la hora del evento hacia un ID de ventana. La clave operativa es simple: el código que calcula la ventana es la única fuente de verdad. El agente puede escribir el worker de envío de correos, pero no puede tocar la aritmética de fechas.
Para evitar que el LLM simplifique las reglas en la próxima iteración, las reglas se congelan en Git antes de invocar al asistente. Se define una tabla de decisiones con casos límite, incluyendo el forward spring de EE.UU. en marzo de 2026, donde un día dura 23 horas. Si el agente genera código que elimina esta fixture, el cambio se rechaza aunque las pruebas de camino feliz pasen.
El módulo usa zoneinfo de la biblioteca estándar de Python. Los datetimes naive son rechazados; todo debe ser timezone-aware. Si un tenant no tiene huso horario definido, se lanza un MissingTimezoneError en lugar de asumir UTC. Esa suposición silenciosa es lo que desplaza los eventos de altas horas hacia el digest incorrecto.
Implementación y revisión
El código mantiene una separación estricta: la calculadora de ventanas no habla con HTTP, bases de datos ni SMTP. Esa frontera es crítica porque los agentes tienden a mezclar la lógica de negocio con la de despliegue. La clase Window es inmutable (frozen=True), lo que impide modificar el end_utc después de que la consulta ha comenzado.
La lógica de pertenencia usa un intervalo semiabierto: start_utc <= event < end_utc. Esto evita la doble emisión de correos que ocurre con queries SQL que usan BETWEEN inclusivo. En la revisión de código, cualquier intento de usar DATE() en UTC ignorando el huso local debe ser rechazado, independientemente de cuántos comentarios confiables genere el asistente.
El éxito no se mide por una plantilla HTML pulida, sino por una suite de pytest verde y un worker que importe la función sin inlinear operaciones de fechas. Es un recordatorio útil para quien opera infraestructura: el valor no está en dejar que el modelo escriba el código, sino en limitar el espacio de soluciones donde puede equivocarse.

