By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.
(DATE_PART, DATE_TRUNC, TO_CHAR, Intervals)
You’re analyzing sales data, and your boss asks: “Show me revenue by hour for the last 30 days, but group it by day of the week.”
Or: “Calculate the average time between a customer’s first and second purchase.”
Or: “Flag all transactions that happened outside business hours (9 AM–5 PM).”
If you don’t know how to extract, truncate, format, and compare dates, you’ll waste hours writing messy workarounds—or worse, deliver wrong results.
Why this matters in production: - Time-based aggregations (daily/weekly/monthly reports) break if you can’t group dates correctly. - Performance tanks if you use string functions (SUBSTRING, LIKE) instead of native date functions. - Bugs creep in when comparing timestamps across time zones or daylight saving changes. - ETL pipelines fail if you can’t handle intervals (e.g., “last 7 days” vs. “last calendar week”).
SUBSTRING
LIKE
Superpower you gain: You can slice, dice, and analyze time data at scale—without manual Excel work or brittle Python scripts.
DATE_PART()
EXTRACT()
SUBSTRING('2023-10-15', 6, 2)
DATE_PART
DATE_TRUNC()
TO_CHAR()
'YYYY-MM-DD'
INTERVAL
+
-
INTERVAL '1 day'
BETWEEN '2023-10-01' AND '2023-10-31'
AT TIME ZONE
AGE()
DATEDIFF()
AGE(end_time, start_time)
DATEDIFF(day, ...)
GENERATE_SERIES()
DATEADD()
DATEADD(day, 7, GETDATE())
GETDATE() + 7
orders
order_time
Goal: Show total sales per hour, grouped by day of week (e.g., “Monday 10 AM: $500”).
CREATE TABLE orders ( order_id INT, order_time TIMESTAMP, amount DECIMAL(10, 2) ); INSERT INTO orders VALUES (1, '2023-10-15 10:30:00', 100.00), (2, '2023-10-15 14:15:00', 200.00), (3, '2023-10-16 10:00:00', 150.00), (4, '2023-10-16 23:45:00', 300.00), (5, '2023-10-22 10:30:00', 50.00); -- Next Sunday
SELECT DATE_PART('hour', order_time) AS hour_of_day, TO_CHAR(order_time, 'Day') AS day_of_week, -- 'Monday', 'Tuesday', etc. SUM(amount) AS total_sales FROM orders GROUP BY DATE_PART('hour', order_time), TO_CHAR(order_time, 'Day') ORDER BY day_of_week, hour_of_day;
Output:
hour_of_day | day_of_week | total_sales ------------+-------------+------------ 10 | Sunday | 50.00 10 | Monday | 250.00 14 | Monday | 200.00 23 | Monday | 300.00
SELECT DATE_TRUNC('hour', order_time) AS hour_truncated, SUM(amount) AS total_sales FROM orders GROUP BY hour_truncated ORDER BY hour_truncated;
hour_truncated | total_sales ---------------------+------------ 2023-10-15 10:00:00 | 100.00 2023-10-15 14:00:00 | 200.00 2023-10-16 10:00:00 | 150.00 2023-10-16 23:00:00 | 300.00 2023-10-22 10:00:00 | 50.00
SELECT * FROM orders WHERE DATE_PART('hour', order_time) BETWEEN 9 AND 17;
order_id | order_time | amount ---------+---------------------+-------- 1 | 2023-10-15 10:30:00 | 100.00 2 | 2023-10-15 14:15:00 | 200.00 3 | 2023-10-16 10:00:00 | 150.00 5 | 2023-10-22 10:30:00 | 50.00
WITH first_orders AS ( SELECT customer_id, MIN(order_time) AS first_order_time FROM orders GROUP BY customer_id ), second_orders AS ( SELECT o.customer_id, o.order_time AS second_order_time FROM orders o JOIN first_orders f ON o.customer_id = f.customer_id WHERE o.order_time > f.first_order_time ORDER BY o.order_time LIMIT 1 ) SELECT f.customer_id, f.first_order_time, s.second_order_time, AGE(s.second_order_time, f.first_order_time) AS time_between_orders FROM first_orders f JOIN second_orders s ON f.customer_id = s.customer_id;
customer_id | first_order_time | second_order_time | time_between_orders ------------+---------------------+---------------------+--------------------- 1 | 2023-10-15 10:30:00 | 2023-10-16 10:00:00 | 23:30:00
DATE_TRUNC
TO_CHAR
+ 7
BETWEEN '2023-01-01' AND '2023-01-31'
sql SELECT order_time AT TIME ZONE 'America/New_York' AS local_time FROM orders;
TO_CHAR(order_time, 'Mon DD, YYYY')
GENERATE_SERIES
DATE_TRUNC('day', order_time)
2023-03-12 02:00:00
'MM/DD/YYYY'
EXTRACT
SELECT
CURRENT_DATE - INTERVAL '30 days'
BETWEEN NOW() - INTERVAL '1 month' AND NOW()
DATEDIFF
TIMESTAMPDIFF
“How do you get the month from a timestamp?”
SUBSTRING(order_time, 6, 2)
DATE_PART('month', order_time)
EXTRACT(MONTH FROM order_time)
Grouping by time periods:
“Show sales by week.”
GROUP BY DATE_PART('week', order_time)
GROUP BY DATE_TRUNC('week', order_time)
Date arithmetic:
“Find orders from the last 7 days.”
WHERE order_time > '2023-10-01'
WHERE order_time > CURRENT_DATE - INTERVAL '7 days'
Time zone conversions:
order_time AT TIME ZONE 'UTC' AT TIME ZONE 'America/Los_Angeles'
DATE_PART('month', '2023-10-15')
10
DATE_TRUNC('month', '2023-10-15')
2023-10-01 00:00:00
AGE('2023-10-16', '2023-10-15')
1 day
DATEDIFF(day, '2023-10-15', '2023-10-16')
1
Challenge: Write a query to show average sales per hour of the day, but only for weekdays (Monday–Friday). Include hours with zero sales.
Solution:
WITH hours AS ( SELECT generate_series(0, 23) AS hour ), weekday_sales AS ( SELECT DATE_PART('hour', order_time) AS hour, SUM(amount) AS total_sales, COUNT(*) AS order_count FROM orders WHERE DATE_PART('dow', order_time) BETWEEN 1 AND 5 -- Monday (1) to Friday (5) GROUP BY DATE_PART('hour', order_time) ) SELECT h.hour, COALESCE(AVG(s.total_sales), 0) AS avg_sales, COALESCE(AVG(s.order_count), 0) AS avg_orders FROM hours h LEFT JOIN weekday_sales s ON h.hour = s.hour GROUP BY h.hour ORDER BY h.hour;
Why it works: - generate_series(0, 23) creates all hours in a day. - DATE_PART('dow', ...) filters for weekdays (1=Monday, 5=Friday). - LEFT JOIN ensures hours with zero sales are included.
generate_series(0, 23)
DATE_PART('dow', ...)
LEFT JOIN
DATE_PART('hour', order_time)
order_time + INTERVAL '1 day'
AGE
order_time AT TIME ZONE 'America/New_York'
generate_series('2023-01-01', '2023-01-31', '1 day')
EXTRACT(HOUR FROM order_time)
DATEDIFF(day, start_time, end_time)
WHERE TO_CHAR(order_time, 'YYYY-MM-DD') = '2023-10-15'
WHERE order_time BETWEEN '2023-10-01' AND '2023-10-31'
Join 4M+ learners. Unlock unlimited quizzes, wrong-answer tracking, flashcards + reminders, study guides, and 1-on-1 challenges.