Skip to content
Volver al blog
Tutoriales

¿Qué Contiene Exactamente una Columna timestamp de PostgreSQL?

Una guía en lenguaje sencillo sobre cómo PostgreSQL almacena timestamp vs timestamptz, por qué las zonas horarias dan problemas y cómo elegir el tipo correcto para tu caso de uso.

6 min de lectura

PostgreSQL timestamp vs timestamptz: ¿qué se almacena realmente bajo el capó?

PostgreSQL mantiene tanto timestamp como timestamptz como un único entero de 64 bits: el número de microsegundos desde 1970-01-01 00:00:00 UTC. La distinción emerge únicamente durante el formateo de datos para consumo humano.

¿Por Qué Confunde a la Gente?

  • Dos columnas, una fecha… dos resultados de consulta diferentes
  • Tu app inserta 2025-07-29 10:00, pero otro equipo ve 02:00
  • El frontend renderiza una cadena ISO que no coincide con el log del backend

Dos Latas de Melocotones: Una Simple, Una Etiquetada

Tipo de datoNombre oficialValor almacenadoQué ocurre en SELECT
timestamptimestamp sin zona horariaconteo de microsegundos en brutoSe devuelve sin cambios — Postgres nunca adivina una zona horaria
timestamptztimestamp con zona horariamismo conteo de microsegundosPostgres aplica la configuración TimeZone de la sesión justo antes de enviar el texto

Analogía

  • timestamp = una lata de melocotones sin etiqueta de origen. Sabes que es fruta, pero no dónde fue enlatada.
  • timestamptz = una lata orgullosamente estampada “Hecho en UTC+8.” Quien la abra puede decidir si convierte el panel nutricional.

Bajo el Capó: Es Solo un Número Enorme

2000-01-01 00:00:00 UTC  → 0
2000-01-01 00:00:01 UTC  → 1 000 000
  • Unidad: microsegundos (una millonésima de segundo)
  • Rango: 4713 a.C. – 294276 d.C. — aprobado por Indiana Jones
  • El almacenamiento para timestamp y timestamptz es idéntico; la interpretación difiere

Una Demostración de 15 Segundos

-- El cliente piensa en hora de Shanghái
SET TimeZone = 'Asia/Shanghai';

CREATE TABLE demo (
  created_ts timestamp,
  created_tz timestamptz
);

INSERT INTO demo VALUES ('2025-07-29 10:00', '2025-07-29 10:00');
ConsultaResultadoPor qué
SELECT created_ts FROM demo;2025-07-29 10:00:00Valor en bruto, sin cálculo de zona horaria
SELECT created_tz FROM demo;2025-07-29 10:00:00+08Etiqueta aplicada en la salida
SET TimeZone = 'UTC'; y luego seleccionar2025-07-29 02:00:00+00El mismo instante, nueva perspectiva

Aritmética de Timestamps e Intervalos

Uno de los aspectos más prácticos de los timestamps de PostgreSQL es la aritmética de intervalos. Como ambos tipos almacenan conteos de microsegundos, puedes añadir y restar intervalos directamente:

-- Añadir 3 horas y 30 minutos
SELECT '2025-07-29 10:00'::timestamptz + INTERVAL '3 hours 30 minutes';
-- → 2025-07-29 13:30:00+08

-- Encontrar la diferencia entre dos timestamps
SELECT '2025-07-30 09:00'::timestamptz - '2025-07-29 10:00'::timestamptz;
-- → 23:00:00  (un intervalo)

-- Extraer campos específicos
SELECT EXTRACT(EPOCH FROM '2025-07-29 10:00:00+08'::timestamptz);
-- → 1753768800  (timestamp Unix en segundos)

-- Truncar al límite del día (útil para agregaciones diarias)
SELECT date_trunc('day', '2025-07-29 15:42:19+08'::timestamptz);
-- → 2025-07-29 00:00:00+08

La función EXTRACT(EPOCH FROM ...) es particularmente útil cuando necesitas pasar timestamps a sistemas externos que esperan segundos de época Unix. Por el contrario, puedes convertir una época de vuelta a timestamp:

SELECT to_timestamp(1753768800);
-- → 2025-07-29 10:00:00+08  (en sesión Asia/Shanghai)

Un punto sutil pero importante: la aritmética de intervalos con timestamp (sin zona horaria) ignora completamente las transiciones de horario de verano, mientras que timestamptz las respeta. Esto significa que añadir INTERVAL '1 day' a un valor timestamptz que cruza un límite de cambio horario devolverá correctamente la misma hora en el reloj de pared — no exactamente 24 horas después.

Consideraciones de Indexación y Rendimiento

Tanto timestamp como timestamptz se almacenan como enteros de 8 bytes, por lo que no hay diferencia de rendimiento entre ellos para almacenamiento o indexación. Los índices B-tree funcionan de forma idéntica en ambos tipos porque la comparación subyacente es simplemente una comparación de enteros.

Sin embargo, hay algunas consideraciones prácticas:

  • Consultas de rango: WHERE created_at > '2025-07-01' funciona eficientemente con un índice en cualquiera de los tipos. Con timestamptz, PostgreSQL convierte el literal a UTC antes de la comparación, por lo que el índice sigue usándose.
  • Claves de partición: Cuando se usa particionamiento por rango en columnas timestamp, timestamptz es generalmente más seguro porque los límites de partición son inequívocos (siempre UTC). Con timestamp, un límite como '2025-07-01 00:00' podría significar cosas diferentes para diferentes sesiones.
  • Índices funcionales: Si frecuentemente consultas solo por fecha (ignorando la hora), considera un índice en date_trunc('day', created_at) para acelerar las consultas de agregación diaria.

Problemas Comunes y Soluciones Rápidas

1. Diferentes usuarios, diferentes relojes

  • Causa: los clientes usan diferentes configuraciones TimeZone con timestamptz
  • Solución: mantén todo en timestamp + acordar una zona, o fuerza SET TimeZone = 'UTC' en la inicialización de la conexión

Un patrón común en el código de aplicación es establecer la zona horaria una vez en la inicialización del pool de conexiones:

-- En tu configuración de conexión (p.ej., configuración del pool de pg)
SET timezone = 'UTC';

Esto garantiza que todas las sesiones vean la misma representación UTC, y la capa de aplicación gestiona la conversión a hora local para la visualización.

2. Almacenar “hora de pared” pero elegir el tipo equivocado

  • Los calendarios de negocio (horarios de tienda, fechas límite) deberían usar timestamp
  • Los flujos de trabajo transfronterizos (pedidos, logs) deberían almacenar UTC en timestamptz

La prueba es simple: si la pregunta es “¿en qué momento ocurrió esto?” usa timestamptz. Si la pregunta es “¿qué muestra el reloj de pared?” usa timestamp.

3. APIs que se desvían

  • Siempre envía timestamptz como cadenas ISO-8601 con el desplazamiento (Z o +08:00)
  • Deja que la interfaz de usuario formatee localmente

4. Comparar timestamps entre tipos

Mezclar timestamp y timestamptz en comparaciones o joins es una fuente común de errores sutiles:

-- Peligroso: el cast implícito aplica la zona horaria de la sesión
SELECT * FROM orders o
JOIN schedules s ON o.created_tz = s.start_ts;
-- PostgreSQL convierte s.start_ts a timestamptz usando la zona horaria de la sesión
-- ¡Diferentes sesiones pueden obtener resultados de join diferentes!

Solución: siempre convierte explícitamente cuando compares entre tipos, o estandariza en un tipo por dominio.

5. Problemas predeterminados de los ORMs

Muchos ORMs (Django, SQLAlchemy, ActiveRecord) usan por defecto timestamp sin zona horaria. Revisa tus archivos de migración — si tu app sirve a usuarios en múltiples zonas horarias, cambia el valor predeterminado a timestamptz. En Django, establece USE_TZ = True en la configuración. En SQLAlchemy, usa DateTime(timezone=True).

Tabla de Referencia Rápida: ¿Cuál Debo Usar?

Solo calendario local → timestamp
Cualquier cosa global  → timestamptz (almacenar en UTC)
  • Informes financieros, horarios de clases → timestamp
  • Registros de auditoría, pedidos de e-commerce → timestamptz

Verifica en Segundos con Go Tools

NecesidadHerramientaCómo hacerlo
Inspeccionar el valor de época desde SQLConversor de ÉpocaPega 1690622400, haz clic en Convertir
Comparar dos zonas horarias de un vistazoConversor de Zona HorariaIntroduce 10:00 Asia/Shanghai
Ordenar JSON en bulk con campos de tiempoFormateador JSONSuelta el payload, formatea y examina

Todas las utilidades se ejecutan completamente en tu navegador — los datos nunca salen de tu máquina.

Preguntas Frecuentes

¿Cuál es la diferencia entre timestamp y timestamptz en PostgreSQL?

timestamp (sin zona horaria) almacena un valor de fecha y hora tal cual, sin contexto de zona horaria. timestamptz (con zona horaria) convierte la entrada a UTC para su almacenamiento y vuelve a convertir a la zona horaria de la sesión en la recuperación. Usa timestamptz en casi todos los casos — previene los errores relacionados con zonas horarias en sistemas distribuidos.

¿PostgreSQL realmente almacena la zona horaria en timestamptz?

No — a pesar del nombre, PostgreSQL no almacena la zona horaria en sí. Convierte la entrada a UTC y almacena únicamente el valor UTC (un conteo de microsegundos desde 2000-01-01). En la recuperación, convierte de UTC a la zona horaria que especifique la configuración timezone de tu sesión. La información de la zona horaria original se descarta.

¿Cómo cambio la zona horaria de una sesión de PostgreSQL?

Ejecuta SET timezone = 'America/New_York'; para cambiar la zona horaria de la sesión. Esto afecta a cómo se muestran e interpretan los valores timestamptz. Para valores predeterminados a nivel de servidor, establece timezone en postgresql.conf. Usa siempre nombres de zona horaria IANA (como Asia/Shanghai) en lugar de abreviaciones (como CST) para evitar ambigüedades.

¿Debería usar timestamp o timestamptz para almacenar tiempos de eventos?

Usa timestamptz para prácticamente todo — acciones de usuario, llamadas a API, registros de auditoría y eventos programados. Solo usa timestamp (sin zona horaria) para tiempos abstractos que no están ligados a un momento específico, como “la tienda abre a las 09:00” que significa las 9 de la mañana en la zona horaria local, no un instante UTC específico.

¿Cómo maneja PostgreSQL el horario de verano con timestamptz?

PostgreSQL maneja correctamente el horario de verano cuando usa timestamptz porque almacena todo en UTC internamente. Cuando recuperas un valor, PostgreSQL convierte desde UTC usando las reglas DST actuales para la zona horaria de tu sesión. Esto significa que el mismo instante UTC almacenado muestra correctamente diferentes horas locales antes y después de una transición de horario de verano.

Para una guía completa sobre los timestamps Unix — incluyendo el manejo de precisión, las mejores prácticas de zona horaria y ejemplos de código en JavaScript, Python y Go — consulta nuestra Guía de Timestamp Unix.

Resumen

  • Ambos tipos de tiempo de Postgres son contadores de microsegundos; la etiqueta es toda la diferencia
  • Elegir el equivocado significa timestamps desconcertantes y cálculos rotos
  • Prueba, convierte y verifica con las herramientas adecuadas para ahorrarte horas de depuración

Artículos relacionados

Ver todos los artículos