Skip to main content
eLearner.app
Module 6 · Lesson 2 of 422/57 in the course~12 min
Module lessons (2/4)

Date and timestamp functions

Dates and timestamps are everywhere: orders, logins, events, billing. In this lesson we see how to extract parts from a date (year, month…), truncate it to a granularity (month, week…), compute intervals and differences, and make comparisons across periods.

The base types

PostgreSQL distinguishes:

  • DATE — date only, format YYYY-MM-DD;
  • TIMESTAMP — date + time, no timezone;
  • TIMESTAMPTZ — date + time + timezone;
  • INTERVAL — a period (e.g. '3 days', '2 hours 30 minutes').

In our ecommerce dataset:

ColumnType
customers.signed_up_onDATE
orders.ordered_atTIMESTAMP
orders.shipped_atTIMESTAMP (can be NULL)

EXTRACT — reading a part

EXTRACT(part FROM date) extracts a number:

SQL
SELECT EXTRACT(YEAR  FROM ordered_at) AS year,
       EXTRACT(MONTH FROM ordered_at) AS month,
       EXTRACT(DOW   FROM ordered_at) AS day_of_week  -- 0=domenica
FROM   orders;

The most used parts: YEAR, MONTH, DAY, HOUR, MINUTE, DOW (day of week 0-6), DOY (day of the year).

DATE_TRUNC — rounding down

DATE_TRUNC('part', date) "truncates" the date to a granularity. It is essential for aggregating by month, week, hour:

SQL
-- Tutti gli ordini fatti in "marzo 2025" cadono nello stesso bucket:
SELECT DATE_TRUNC('month', ordered_at) AS month,
       COUNT(*) AS order_count
FROM   orders
GROUP BY month
ORDER BY month;

Valid parts: 'year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second'.

For readability, people often cast to DATE when time is not needed: DATE_TRUNC('month', ordered_at)::date.

Differences and intervals

Subtracting two TIMESTAMPs produces an INTERVAL:

SQL
SELECT id,
       shipped_at - ordered_at AS wait_time
FROM   orders
WHERE  shipped_at IS NOT NULL;

Comparisons with an INTERVAL literal:

SQL
WHERE  shipped_at - ordered_at > INTERVAL '1 day'

Adding days to a date:

SQL
SELECT signed_up_on + INTERVAL '30 days' AS trial_expiration
FROM   customers;

Try it

Exercise#sql.m6.l2.e1
Attempts: 0Loading…

Count how many orders were placed per year. Two columns: year (integer) and order_count. Order by year ascending.

Loading editor…
Show hint

EXTRACT(YEAR FROM ordered_at) returns a NUMERIC: cast it to int with ::int for readability.

Solution available after 3 attempts

Review exercise

Exercise#sql.m6.l2.e2
Attempts: 0Loading…

For each month of 2024, show the first day of the month (column month, of type date) and the number of orders. Order by month.

Loading editor…
Show hint

DATE_TRUNC('month', ordered_at) returns the first day of the month as a timestamp; ::date converts it to date.

Solution available after 3 attempts