← Back to Blog

SQL Date Functions: Extract, Truncate, and Format

Why Date Functions Matter

Date manipulation is one of the most common tasks in data science SQL. Nearly every analytical query involves filtering by date ranges, aggregating by time periods, or calculating durations. Interviewers test date functions because they reveal whether you can work with real-world data — and real-world data almost always has a time dimension.

EXTRACT: Pulling Parts from Dates

EXTRACT retrieves a specific component (year, month, day, hour, etc.) from a date or timestamp.

SELECT
    order_date,
    EXTRACT(YEAR FROM order_date) AS order_year,
    EXTRACT(MONTH FROM order_date) AS order_month,
    EXTRACT(DOW FROM order_date) AS day_of_week,
    EXTRACT(QUARTER FROM order_date) AS quarter
FROM orders;

Common EXTRACT Fields

Field Description Example Result
YEAR Four-digit year 2025
MONTH Month number (1-12) 3
DAY Day of month (1-31) 15
DOW Day of week (0=Sun, 6=Sat) 2
HOUR Hour (0-23) 14
QUARTER Quarter (1-4) 1
EPOCH Seconds since 1970-01-01 1710504000

Using EXTRACT for Analysis

-- Monthly revenue trend
SELECT
    EXTRACT(YEAR FROM order_date) AS year,
    EXTRACT(MONTH FROM order_date) AS month,
    SUM(amount) AS monthly_revenue
FROM orders
GROUP BY
    EXTRACT(YEAR FROM order_date),
    EXTRACT(MONTH FROM order_date)
ORDER BY year, month;

-- Busiest day of the week
SELECT
    EXTRACT(DOW FROM order_date) AS day_of_week,
    COUNT(*) AS order_count
FROM orders
GROUP BY EXTRACT(DOW FROM order_date)
ORDER BY order_count DESC;

DATE_TRUNC: Truncating to a Time Period

DATE_TRUNC rounds a timestamp down to the specified precision. This is one of the most useful functions for time series aggregation.

SELECT
    DATE_TRUNC('month', order_date) AS month_start,
    COUNT(*) AS order_count,
    SUM(amount) AS total_revenue
FROM orders
GROUP BY DATE_TRUNC('month', order_date)
ORDER BY month_start;

DATE_TRUNC Precision Levels

-- Truncate to various levels
SELECT
    order_date,
    DATE_TRUNC('year', order_date) AS year_start,
    DATE_TRUNC('quarter', order_date) AS quarter_start,
    DATE_TRUNC('month', order_date) AS month_start,
    DATE_TRUNC('week', order_date) AS week_start,
    DATE_TRUNC('day', order_date) AS day_start,
    DATE_TRUNC('hour', order_date) AS hour_start
FROM orders;

For example, if order_date is 2025-03-15 14:30:00: - DATE_TRUNC('year', ...) returns 2025-01-01 00:00:00 - DATE_TRUNC('month', ...) returns 2025-03-01 00:00:00 - DATE_TRUNC('week', ...) returns 2025-03-10 00:00:00 (Monday)

EXTRACT vs DATE_TRUNC

Use EXTRACT when you need a single numeric component (the month number, the year). Use DATE_TRUNC when you need to group by a time period and want a proper date value in your results.

-- EXTRACT: returns a number (3)
SELECT EXTRACT(MONTH FROM TIMESTAMP '2025-03-15 14:30:00');

-- DATE_TRUNC: returns a date (2025-03-01 00:00:00)
SELECT DATE_TRUNC('month', TIMESTAMP '2025-03-15 14:30:00');

DATE_TRUNC is preferred for GROUP BY because it preserves chronological sorting automatically.

Date Formatting

Different databases have different formatting functions:

PostgreSQL: TO_CHAR

SELECT
    TO_CHAR(order_date, 'YYYY-MM-DD') AS formatted_date,
    TO_CHAR(order_date, 'Month DD, YYYY') AS long_date,
    TO_CHAR(order_date, 'Day') AS day_name,
    TO_CHAR(order_date, 'HH24:MI:SS') AS time_only
FROM orders;

MySQL: DATE_FORMAT

SELECT
    DATE_FORMAT(order_date, '%Y-%m-%d') AS formatted_date,
    DATE_FORMAT(order_date, '%M %d, %Y') AS long_date,
    DATE_FORMAT(order_date, '%W') AS day_name
FROM orders;

Interval Arithmetic

Date arithmetic lets you add or subtract time periods from dates.

Adding and Subtracting Intervals

-- PostgreSQL syntax
SELECT
    CURRENT_DATE AS today,
    CURRENT_DATE + INTERVAL '7 days' AS next_week,
    CURRENT_DATE - INTERVAL '1 month' AS last_month,
    CURRENT_DATE + INTERVAL '1 year' AS next_year;

Calculating Differences Between Dates

-- Days between two dates
SELECT
    order_date,
    ship_date,
    ship_date - order_date AS days_to_ship
FROM orders;

-- Using AGE function (PostgreSQL)
SELECT
    AGE(CURRENT_DATE, hire_date) AS tenure
FROM employees;

-- Using DATEDIFF (MySQL)
SELECT
    DATEDIFF(ship_date, order_date) AS days_to_ship
FROM orders;

Filtering by Date Ranges

-- Orders from the last 30 days
SELECT *
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '30 days';

-- Orders from a specific month
SELECT *
FROM orders
WHERE order_date >= '2025-01-01'
  AND order_date < '2025-02-01';

-- Using DATE_TRUNC for clean month filtering
SELECT *
FROM orders
WHERE DATE_TRUNC('month', order_date) = '2025-01-01';

Common Interview Patterns

Pattern 1: Year-over-Year Comparison

SELECT
    DATE_TRUNC('month', order_date) AS month,
    SUM(CASE
        WHEN EXTRACT(YEAR FROM order_date) = 2025
        THEN amount ELSE 0
    END) AS revenue_2025,
    SUM(CASE
        WHEN EXTRACT(YEAR FROM order_date) = 2024
        THEN amount ELSE 0
    END) AS revenue_2024
FROM orders
WHERE order_date >= '2024-01-01'
GROUP BY DATE_TRUNC('month', order_date)
ORDER BY month;

Pattern 2: Cohort Analysis by Sign-Up Month

SELECT
    DATE_TRUNC('month', u.signup_date) AS cohort_month,
    EXTRACT(MONTH FROM AGE(o.order_date, u.signup_date)) AS months_since_signup,
    COUNT(DISTINCT u.user_id) AS active_users
FROM users u
JOIN orders o ON u.user_id = o.user_id
GROUP BY cohort_month, months_since_signup
ORDER BY cohort_month, months_since_signup;

Pattern 3: Rolling 7-Day Active Users

SELECT
    date,
    COUNT(DISTINCT user_id) AS daily_active,
    COUNT(DISTINCT user_id) OVER (
        ORDER BY date
        RANGE BETWEEN INTERVAL '6 days' PRECEDING AND CURRENT ROW
    ) AS weekly_active
FROM user_activity
GROUP BY date
ORDER BY date;

Pattern 4: Business Days Calculation

-- Count business days between two dates (PostgreSQL)
SELECT
    order_date,
    ship_date,
    (
        SELECT COUNT(*)
        FROM generate_series(order_date, ship_date - INTERVAL '1 day', '1 day') d
        WHERE EXTRACT(DOW FROM d) NOT IN (0, 6)
    ) AS business_days
FROM orders;

Database Differences

Date function syntax varies across databases. Here are key differences to be aware of:

Operation PostgreSQL MySQL
Current date CURRENT_DATE CURDATE()
Truncate DATE_TRUNC('month', d) DATE_FORMAT(d, '%Y-%m-01')
Extract EXTRACT(MONTH FROM d) EXTRACT(MONTH FROM d)
Date diff date2 - date1 DATEDIFF(date2, date1)
Add interval d + INTERVAL '1 day' DATE_ADD(d, INTERVAL 1 DAY)

In interviews, if you are unsure which dialect to use, mention PostgreSQL syntax. It is the most commonly expected in data science interviews.

Practice Problems

For more date function practice, visit the date functions practice page.

Key Takeaways

EXTRACT pulls individual components from dates. DATE_TRUNC rounds dates to time period boundaries and is ideal for GROUP BY queries. Interval arithmetic handles date addition, subtraction, and difference calculations. Master these functions and you can handle virtually any time-based SQL question in an interview.

Practice Makes Perfect

Ready to test your skills?

Practice real Date Functions interview questions from top companies — with solutions.

Get interview tips in your inbox

Join data scientists preparing smarter. No spam, unsubscribe anytime.