Manejo de Zonas Horarias
Todo el sistema SFS opera en hora local de Colombia (UTC-5). Esta página documenta el porqué, las reglas, y los errores comunes.
Decisión de diseño
Section titled “Decisión de diseño”SFS está orientado al mercado colombiano como target primario. Usar hora local en vez de UTC simplifica:
- Los dueños configuran horarios en su hora natural (“abro de 8am a 11pm”)
- Los jugadores ven slots en hora local sin conversión mental
- Las queries de base de datos no requieren conversión de timezone
Si en el futuro SFS escala a múltiples países, se migrará a un modelo con timezone por complejo y almacenamiento UTC.
Regla de oro
Section titled “Regla de oro”NUNCA usar el sufijo
Zal construir fechas connew Date()a partir de valores de hora local.
❌ Incorrecto
Section titled “❌ Incorrecto”// Esto crea 18:00 UTC = 13:00 hora Colombiaconst apertura = new Date("2026-08-06T18:00:00Z");✅ Correcto
Section titled “✅ Correcto”// Esto crea 18:00 hora local (Colombia)const apertura = new Date("2026-08-06T18:00:00");Cómo funciona
Section titled “Cómo funciona”Almacenamiento (PostgreSQL)
Section titled “Almacenamiento (PostgreSQL)”Las columnas Timestamptz almacenan el timestamp en UTC internamente. PostgreSQL convierte automáticamente al insertar/leer según el timezone de la sesión.
Lectura (Prisma)
Section titled “Lectura (Prisma)”Prisma devuelve objetos Date de JavaScript. Un Date en JavaScript siempre representa un instante en UTC internamente (milisegundos desde epoch), pero sus métodos como getHours() usan la zona horaria local del sistema.
El bug clásico (y cómo evitarlo)
Section titled “El bug clásico (y cómo evitarlo)”// 🐛 BUG: "Z" fuerza interpretación UTC// Si el dueño configuró apertura a las 18:00 (6pm Colombia)const hora = 18;const fecha = "2026-08-06";
// ❌ MAL: 18:00 UTC = 13:00 Colombiaconst apertura = new Date(`${fecha}T${hora}:00:00Z`);// → Los slots se generan desde las 13:00
// ✅ BIEN: 18:00 hora localconst apertura = new Date(`${fecha}T${hora}:00:00`);// → Los slots se generan desde las 18:00Serialización para API
Section titled “Serialización para API”Los endpoints devuelven fechas en ISO 8601 (UTC) mediante toISOString(). El frontend las convierte a hora local para mostrar:
// API response{ "inicio": "2026-08-06T23:00:00.000Z" }
// Frontend display (hora Colombia)new Date("2026-08-06T23:00:00.000Z").toLocaleTimeString("es-CO", { hour: "2-digit", minute: "2-digit"})// → "18:00" (6pm Colombia)Checklist para nuevas features
Section titled “Checklist para nuevas features”Cuando trabajes con fechas en el backend, verificá:
- ¿Estoy construyendo fechas manualmente con
new Date()? → SinZ - ¿Estoy usando
getUTCHours()para extraer horas de unDateque viene de la DB? → Correcto, los timestamps de DB están en UTC - ¿Estoy filtrando por rango de fechas en Prisma? → Usar hora local para los límites (
"2026-08-06T23:59:59"sin Z) - ¿Estoy serializando para API? →
toISOString()es correcto, el frontend se encarga de la conversión
Stack trace del bug real
Section titled “Stack trace del bug real”Este bug ocurrió en producción (Agosto 2026) y costó 1 hora de debugging:
- Dueño configura cancha con horario 18:00-23:00
SlotConfig.horaAperturase almacena como1970-01-01T18:00:00.000Zen PostgreSQL- El código extrae la hora con
getUTCHours()→18✅ - Pero construye la fecha con
new Date("2026-08-06T18:00:00Z")→ 13:00 hora local ❌ - Resultado: slots mostrados de 13:00 a 17:00 en vez de 18:00 a 22:00
Fix: quitar Z de las 3 ocurrencias en src/app/api/disponibilidad/route.ts (líneas 50, 78, 79).