NOW(), INTERVAL și date_trunc() în PostgreSQL

Date, timp și generare serii PostgreSQL 8.0+ (date_bin din PostgreSQL 14+)

Lucrează cu momente de timp cu fus orar (timestamptz), aritmetică de durate (INTERVAL) și rotunjire calendaristică (date_trunc).

Sintaxă SQL
SELECT NOW() - INTERVAL '7 days', date_trunc('month', NOW());

Ce face NOW(), INTERVAL și date_trunc()

În PostgreSQL este o bună practică universală să folosești tipul timestamptz (timestamp with time zone) în loc de timestamp simplu. PostgreSQL stochează intern toate valorile timestamptz în UTC (pe 8 octeți) și le convertește automat la fusul orar al sesiunii curente la afișare.

Funcția NOW() (echivalentă cu CURRENT_TIMESTAMP și transaction_timestamp()) returnează momentul de început al tranzacției curente, tipul INTERVAL permite adunarea și scăderea intuitivă a perioadelor de timp (ex: INTERVAL '2 hours 30 minutes'), iar date_trunc(unitate, timestamp) trunchiază o dată la începutul orei, zilei, săptămânii, lunii sau anului — fiind esențială pentru grupările cronologice.

Clauze, operatori și argumente

Clauză / Operator Descriere
NOW() / CURRENT_TIMESTAMP Returnează data și ora (cu fus orar) de la începutul tranzacției curente.
clock_timestamp() Returnează data și ora reală a ceasului sistemului în momentul exact al evaluării (se schimbă și în cadrul aceleiași interogări).
INTERVAL 'n unit' Reprezintă o durată de timp (ex: '1 year 2 months', '14 days', '45 minutes').
date_trunc('unitate', ts) Resetează câmpurile mai mici decât unitatea specificată (hour, day, week, month, quarter, year).
AGE(ts_final, ts_start) Calculează diferența simbolică (în ani, luni și zile) dintre două timestamp-uri sub formă de interval.

Exemple

Exemplul 1: Gruparea vânzărilor pe săptămâni calendaristice cu date_trunc() și filtrare cu INTERVAL

Selectăm comenzile din ultimele 30 de zile și le agregăm pe săptămâna calendaristică (începând de luni).

SELECT date_trunc('week', plasata_la)::date AS inceput_saptamana,
       COUNT(*) AS nr_comenzi,
       SUM(total_lei) AS total_saptamana
FROM comenzi
WHERE plasata_la >= NOW() - INTERVAL '30 days'
GROUP BY inceput_saptamana
ORDER BY inceput_saptamana DESC;

Rezultat interogare

 inceput_saptamana | nr_comenzi | total_saptamana 
-------------------+------------+-----------------
 2025-03-17        |         19 |         9420.00
 2025-03-10        |         34 |        16850.50
 2025-03-03        |         28 |        12310.00
(3 rows)

Exemplul 2: Calcularea datelor de expirare și a vechimii contului cu INTERVAL și AGE()

Calculăm când expiră un abonament de 1 an și 15 zile bonus, precum și vechimea exactă a unui client.

SELECT id,
       creat_la::date AS data_inscrierii,
       (creat_la + INTERVAL '1 year 15 days')::date AS expira_la,
       AGE(NOW(), creat_la) AS vechime_cont
FROM clienti
WHERE id = 101;

Rezultat interogare

 id  | data_inscrierii | expira_la  |       vechime_cont        
-----+-----------------+------------+---------------------------
 101 | 2023-11-05      | 2024-11-20 | 1 year 4 mons 15 days 09:12:04
(1 row)

Greșeli frecvente

  • Așteptarea ca NOW() să își schimbe valoarea între două instrucțiuni din același bloc BEGIN ... COMMIT: NOW() este înghețat la începutul tranzacției; dacă măsori durata pașilor intermediari, folosește clock_timestamp().
  • Folosirea tipului timestamp without time zone pentru evenimente globale: la schimbarea orei de vară/iarnă sau la mutarea serverului apar ambiguități; folosește întotdeauna timestamptz.

Comenzi și funcții înrudite

  • to_char() și EXTRACT(): Formatează date, ore și numere în șiruri personalizate (to_char) sau extrage componente numerice din timestamp-uri (EXTRACT).
  • generate_series(): Generează pe loc serii de numere întregi sau calendare de date/ore pentru rapoarte fără zile lipsă și date de test.
  • GROUP BY, HAVING și clauza FILTER: Grupează rândurile pentru agregări și aplică condiții individuale pe fiecare funcție agregată prin FILTER (WHERE …).