Skip to content
Torna al blog
Tutorial

Cosa contiene davvero una colonna timestamp di PostgreSQL?

Una guida online chiara su come PostgreSQL memorizza timestamp e timestamptz, perché i fusi orari creano problemi e come scegliere il tipo giusto.

6 min di lettura

PostgreSQL timestamp vs timestamptz: cosa viene davvero memorizzato sotto il cofano?

PostgreSQL gestisce sia timestamp sia timestamptz come un singolo intero a 64 bit: il numero di microsecondi a partire dal 1970-01-01 00:00:00 UTC. La distinzione emerge solo quando i dati vengono formattati per essere letti dagli umani.

Perché questo confonde così tante persone?

  • Due colonne, una sola data… due risultati di query diversi
  • La tua applicazione inserisce 2025-07-29 10:00, ma un altro team vede 02:00
  • Il frontend mostra una stringa ISO che non corrisponde al log del backend

Due barattoli di pesche: uno semplice, uno etichettato

Tipo di datoNome ufficialeValore memorizzatoCosa succede con SELECT
timestamptimestamp without time zoneconteggio grezzo dei microsecondiRestituito invariato — Postgres non indovina mai un fuso orario
timestamptztimestamp with time zonestesso conteggio dei microsecondiPostgres applica l’impostazione TimeZone della sessione subito prima di inviare il testo

Analogia

  • timestamp = un barattolo di pesche senza etichetta d’origine. Sai che è frutta, ma non dove è stata conservata.
  • timestamptz = un barattolo con timbro orgoglioso “Made in UTC+8.” Chi lo apre può decidere se convertire la tabella nutrizionale.

Sotto il cofano: è solo un numero gigante

2000-01-01 00:00:00 UTC  → 0
2000-01-01 00:00:01 UTC  → 1 000 000
  • Unità: microsecondi (un milionesimo di secondo)
  • Intervallo: 4713 a.C. – 294276 d.C. — approvato da Indiana Jones
  • L’archiviazione di timestamp e timestamptz è identica; cambia solo l’interpretazione

Una demo da 15 secondi

-- Il client ragiona in ora di Shanghai
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');
QueryRisultatoPerché
SELECT created_ts FROM demo;2025-07-29 10:00:00Valore grezzo, nessun calcolo TZ
SELECT created_tz FROM demo;2025-07-29 10:00:00+08Etichetta applicata in output
SET TimeZone = 'UTC'; poi select2025-07-29 02:00:00+00Stesso istante, lente diversa

Aritmetica dei timestamp e intervalli

Uno degli aspetti più pratici dei timestamp di PostgreSQL è l’aritmetica con gli intervalli. Poiché entrambi i tipi memorizzano conteggi di microsecondi, puoi sommare e sottrarre intervalli direttamente:

-- Aggiungi 3 ore e 30 minuti
SELECT '2025-07-29 10:00'::timestamptz + INTERVAL '3 hours 30 minutes';
-- → 2025-07-29 13:30:00+08

-- Trova la differenza tra due timestamp
SELECT '2025-07-30 09:00'::timestamptz - '2025-07-29 10:00'::timestamptz;
-- → 23:00:00  (un intervallo)

-- Estrai campi specifici
SELECT EXTRACT(EPOCH FROM '2025-07-29 10:00:00+08'::timestamptz);
-- → 1753768800  (timestamp Unix in secondi)

-- Tronca al limite del giorno (utile per aggregazioni giornaliere)
SELECT date_trunc('day', '2025-07-29 15:42:19+08'::timestamptz);
-- → 2025-07-29 00:00:00+08

La funzione EXTRACT(EPOCH FROM ...) torna molto utile quando devi passare timestamp a sistemi esterni che si aspettano secondi epoch Unix. Al contrario, puoi convertire un epoch in un timestamp:

SELECT to_timestamp(1753768800);
-- → 2025-07-29 10:00:00+08  (in sessione Asia/Shanghai)

Un dettaglio sottile ma importante: l’aritmetica con intervalli su timestamp (senza fuso orario) ignora completamente le transizioni dell’ora legale, mentre timestamptz le rispetta. Questo significa che aggiungere INTERVAL '1 day' a un valore timestamptz che attraversa un confine di ora legale restituirà correttamente la stessa ora di parete — non esattamente 24 ore dopo.

Indicizzazione e considerazioni sulle prestazioni

Sia timestamp sia timestamptz vengono memorizzati come interi a 8 byte, quindi non c’è differenza di prestazioni tra loro per archiviazione o indicizzazione. Gli indici B-tree funzionano in modo identico su entrambi i tipi perché il confronto sottostante è solo un confronto tra interi.

Tuttavia, ci sono alcune considerazioni pratiche:

  • Query di intervallo: WHERE created_at > '2025-07-01' funziona efficacemente con un indice su entrambi i tipi. Con timestamptz, PostgreSQL converte il letterale in UTC prima del confronto, quindi l’indice viene comunque usato.
  • Chiavi di partizione: quando si usa il partizionamento per intervalli su colonne timestamp, timestamptz è generalmente più sicuro perché i confini di partizione sono inequivocabili (sempre UTC). Con timestamp, un confine come '2025-07-01 00:00' potrebbe significare cose diverse per sessioni diverse.
  • Indici funzionali: se interroghi spesso solo per data (ignorando l’ora), valuta un indice su date_trunc('day', created_at) per accelerare le query di aggregazione giornaliera.

Insidie comuni e soluzioni rapide

1. Utenti diversi, orologi diversi

  • Causa: i client usano impostazioni TimeZone diverse con timestamptz
  • Soluzione: o mantieni tutto come timestamp + concorda un fuso unico, oppure imponi SET TimeZone = 'UTC' all’inizializzazione della connessione

Un pattern comune nel codice applicativo è impostare il fuso orario una sola volta all’inizializzazione del pool di connessioni:

-- Nel setup della tua connessione (es., config del pool pg)
SET timezone = 'UTC';

Questo garantisce che tutte le sessioni vedano la stessa rappresentazione UTC, e il livello applicativo gestisce la conversione all’ora locale per la visualizzazione.

2. Memorizzi “ora di parete” ma hai scelto il tipo sbagliato

  • I calendari aziendali (orari di apertura, scadenze) dovrebbero usare timestamp
  • I flussi cross-border (ordini, log) dovrebbero memorizzare UTC in timestamptz

Il test è semplice: se la domanda è “in quale momento nel tempo è successo?” usa timestamptz. Se la domanda è “cosa segna l’orologio sul muro?” usa timestamp.

3. API che vanno alla deriva

  • Trasmetti sempre timestamptz come stringhe ISO-8601 con l’offset (Z o +08:00)
  • Lascia che la UI formatti localmente

4. Confrontare timestamp di tipi diversi

Mescolare timestamp e timestamptz in confronti o join è una fonte comune di bug sottili:

-- Pericoloso: il cast implicito applica il fuso orario di sessione
SELECT * FROM orders o
JOIN schedules s ON o.created_tz = s.start_ts;
-- PostgreSQL fa il cast di s.start_ts a timestamptz usando il fuso di sessione
-- Sessioni diverse possono ottenere risultati di join diversi!

Soluzione: fai sempre il cast esplicito quando confronti tipi diversi, oppure standardizza su un solo tipo per dominio.

5. Insidie predefinite degli ORM

Molti ORM (Django, SQLAlchemy, ActiveRecord) usano per default timestamp senza fuso orario. Verifica i tuoi file di migrazione — se la tua app serve utenti su più fusi, sovrascrivi il default a timestamptz. In Django, imposta USE_TZ = True nelle settings. In SQLAlchemy, usa DateTime(timezone=True).

Cheat Sheet: quale dovrei usare?

Solo calendario locale → timestamp
Qualsiasi cosa globale → timestamptz (memorizza UTC)
  • Report finanziari, orari delle lezioni → timestamp
  • Log di audit, ordini e-commerce → timestamptz

Verifica in pochi secondi con Go Tools

NecessitàStrumentoCome
Ispezionare il valore epoch da SQLConvertitore EpochIncolla 1690622400, premi Convert
Confrontare due fusi orari a colpo d’occhioConvertitore Fuso OrarioInserisci 10:00 Asia/Shanghai
Riordinare JSON di massa con campi temporaliJSON FormatterIncolla il payload, abbelliscilo e analizzalo

Tutte le utility girano interamente nel tuo browser — nessun dato lascia mai la tua macchina.

Domande frequenti

Qual è la differenza tra timestamp e timestamptz in PostgreSQL?

timestamp (senza fuso orario) memorizza un valore data-ora così com’è, senza alcun contesto di fuso orario. timestamptz (con fuso orario) converte l’input in UTC per l’archiviazione e lo riconverte nel fuso orario della sessione al recupero. Usa timestamptz per quasi tutti i casi — previene bug legati ai fusi orari nei sistemi distribuiti.

PostgreSQL memorizza davvero il fuso orario in timestamptz?

No — nonostante il nome, PostgreSQL non memorizza il fuso orario stesso. Converte l’input in UTC e memorizza solo il valore UTC (un conteggio di microsecondi dal 2000-01-01). Al recupero, converte da UTC a qualunque fuso orario specificato dall’impostazione timezone della sessione. L’informazione originale sul fuso orario viene scartata.

Come cambio il fuso orario per una sessione PostgreSQL?

Esegui SET timezone = 'America/New_York'; per cambiare il fuso orario di sessione. Questo influisce su come i valori timestamptz vengono visualizzati e interpretati. Per i default a livello di server, imposta timezone in postgresql.conf. Usa sempre nomi di fuso orario IANA (come Asia/Shanghai) anziché abbreviazioni (come CST) per evitare ambiguità.

Dovrei usare timestamp o timestamptz per memorizzare gli orari degli eventi?

Usa timestamptz per quasi tutto — azioni utente, chiamate API, log di audit ed eventi pianificati. Usa timestamp (senza fuso orario) solo per orari astratti che non sono legati a un momento specifico, come “il negozio apre alle 09:00” che significa le 9 del mattino in qualunque sia il fuso orario locale, non un istante UTC specifico.

Come gestisce PostgreSQL l’ora legale con timestamptz?

PostgreSQL gestisce l’ora legale correttamente quando si usa timestamptz perché memorizza tutto in UTC internamente. Quando recuperi un valore, PostgreSQL converte da UTC usando le regole di ora legale correnti per il fuso orario di sessione. Questo significa che lo stesso istante UTC memorizzato mostra correttamente orari locali diversi prima e dopo una transizione di ora legale.

Per una guida completa ai timestamp Unix — inclusi gestione della precisione, best practice sui fusi orari ed esempi di codice in JavaScript, Python e Go — leggi la nostra Guida ai Timestamp Unix.

  • Entrambi i tipi temporali di Postgres sono contatori di microsecondi; l’etichetta fa tutta la differenza
  • Scegliere quello sbagliato significa timestamp enigmatici e calcoli rotti
  • Verifica, converti e fai sanity-check con gli strumenti giusti per risparmiare ore di debug

Articoli correlati

Vedi tutti gli articoli