Module 5: Date, Time & Text Functions45 min

Date/Time Data Types and Extraction

Progress Tracking

Log in to save this lesson and continue from where you left off.

Log in

Working with Time

Dates and timestamps are everywhere: signup times, order dates, event logs, and session starts. If you’re doing any kind of analytics — retention, cohorts, time-series trends — you need to slice and dice temporal data. This module gives you the toolkit.

Fair warning: date functions are where SQL dialects diverge most. We cover all four — PostgreSQL, MySQL, SQL Server, and Oracle — throughout this module. The prose and examples use PostgreSQL syntax; the practice exercises include all dialects, so you can switch to your target database using the dialect selector above the editor.

Date/Time Data Types

Before extracting parts of dates, understand what you’re working with:

  • DATE — Just the date: ‘2024-03-15’ (year, month, day only)
  • TIME — Just the time: ‘14:30:00’ (hours, minutes, seconds)
  • TIMESTAMP / DATETIME — Both date and time: ‘2024-03-15 14:30:00’
  • TIMESTAMPTZ — Timestamp with timezone (PostgreSQL)
Spotting timestamps in schemas

Most interview questions use DATE or TIMESTAMP. If you see a column ending in ‘_at’ (created_at, updated_at), it’s almost always a timestamp.

SQL
SELECT
  first_name,
  joining_date,
  EXTRACT(YEAR FROM joining_date) AS year,
  EXTRACT(MONTH FROM joining_date) AS month,
  EXTRACT(DAY FROM joining_date) AS day
FROM worker;

Common Extraction Values

  • YEAR — The 4-digit year (2024)
  • MONTH — Month number 1–12
  • DAY — Day of month 1–31
  • HOUR — Hour 0–23 (from timestamps)
  • MINUTE — Minute 0–59
  • DOW — Day of week (0=Sunday in PostgreSQL)
  • WEEK — Week number of the year
  • QUARTER — Quarter 1–4

Dialect Comparison: Date Extraction

Dialects diverge here

This is where dialects diverge significantly. If you’re interviewing somewhere specific, memorize that dialect’s syntax. Otherwise, know PostgreSQL (most common in tech) and be ready to look up the rest.

  • Current date:
    • PostgreSQL — CURRENT_DATE
    • MySQL — CURDATE()
    • MSSQL — CAST(GETDATE() AS DATE)
    • Oracle — TRUNC(SYSDATE)
  • Current timestamp:
    • PostgreSQL — NOW()
    • MySQL — NOW()
    • MSSQL — GETDATE()
    • Oracle — SYSTIMESTAMP
  • Extract year:
    • PostgreSQL — EXTRACT(YEAR FROM d)
    • MySQL — YEAR(d)
    • MSSQL — YEAR(d)
    • Oracle — EXTRACT(YEAR FROM d)
  • Extract month:
    • PostgreSQL — EXTRACT(MONTH FROM d)
    • MySQL — MONTH(d)
    • MSSQL — MONTH(d)
    • Oracle — EXTRACT(MONTH FROM d)
  • Extract day:
    • PostgreSQL — EXTRACT(DAY FROM d)
    • MySQL — DAY(d)
    • MSSQL — DAY(d)
    • Oracle — EXTRACT(DAY FROM d)
  • Day of week:
    • PostgreSQL — EXTRACT(DOW FROM d)
    • MySQL — DAYOFWEEK(d)
    • MSSQL — DATEPART(dw, d)
    • Oracle — TO_CHAR(d, 'D')
Shorter syntax in MySQL and MSSQL

In MySQL and MSSQL, YEAR(), MONTH(), DAY() are shorter than EXTRACT(). Same result, less typing. Use what your team uses for consistency.

Filtering by Date Parts

EXTRACT shines in WHERE clauses. Find employees who joined in January:

PostgreSQL
SELECT
  first_name,
  last_name,
  joining_date
FROM employees
WHERE EXTRACT(MONTH FROM joining_date) = 1;

Find all activities from 2023:

PostgreSQL
SELECT *
FROM events
WHERE EXTRACT(YEAR FROM event_date) = 2023;

Filtering by Month and Department

Use EXTRACT(MONTH) to filter for April (month 4) and LOWER() for case-insensitive department matching.

Table: worker
worker_idfirst_namelast_namesalaryjoining_datedepartment
1MonikaArora1000002014-02-20HR
2NiharikaVerma800002014-06-11Admin
3VishalSinghal3000002014-02-20HR
4AmitahSingh5000002014-02-20Admin
5VivekBhati5000002014-06-11Admin
1
April Admin Employees
View solution

Find the number of employees working in the Admin department that joined in April or later, in any year.

Tables: worker

Combining with Other Functions

EXTRACT works seamlessly with aggregations. Count events by month:

2
Group Hires by Month

The query groups hires by year. Change it to group by month instead, and add `ORDER BY` to sort by month.

Tables: worker

Monthly Active Users

Use EXTRACT for the month and COUNT(DISTINCT) for unique users.

Table: fact_events
idtime_iduser_idcustomer_idclient_idevent_typeevent_id
12020-02-283668-QPYBKSenditdesktopmessage sent3
22020-02-287892-POOKPConnectixmobilefile received2
32020-04-039763-GRSKDZoomitdesktopvideo call received7
42020-04-029763-GRSKDConnectixdesktopvideo call received7
52020-02-069237-HQITUSenditdesktopvideo call received7
3
Unique Users Per Client Per Month
View solution

Write a query that returns the number of unique users per client for each month. Assume all events occur within the same year, so only month needs to be be in the output as a number from 1 to 12.

Tables: fact_events

Day of Week Analysis

Finding patterns by day of week is a product analytics staple. Are users more active on weekends? Do signups spike on Mondays?

PostgreSQL
-- PostgreSQL: 0 = Sunday, 6 = Saturday
SELECT
  EXTRACT(DOW FROM created_at) AS day_of_week,
  COUNT(*) AS signups
FROM users
GROUP BY EXTRACT(DOW FROM created_at)
ORDER BY day_of_week;
Day of week numbering varies

Day of week numbering varies wildly! PostgreSQL: 0–6 (Sunday=0). MySQL DAYOFWEEK(): 1–7 (Sunday=1). MSSQL depends on server settings. Always test with a known date like ‘2024-01-01’ (a Monday) to verify your database’s behavior.

4
Hires by Day of Week

The query groups hires by month. Change it to group by day of week instead. Keep the descending sort by hire count.

Tables: worker

Grouping by Year

Annual analysis is straightforward with EXTRACT:

PostgreSQL
SELECT
  EXTRACT(YEAR FROM order_date) AS year,
  SUM(total_order_cost) AS annual_revenue
FROM orders
GROUP BY EXTRACT(YEAR FROM order_date)
ORDER BY year;

Violations by Year

Use EXTRACT(YEAR) with GROUP BY to count violations per year.

Table: sf_restaurant_health_violations
business_idbusiness_namebusiness_addressbusiness_citybusiness_statebusiness_postal_codebusiness_latitudebusiness_longitudebusiness_locationbusiness_phone_numberinspection_idinspection_dateinspection_scoreinspection_typeviolation_idviolation_descriptionrisk_category
5800John Chin Elementary School350 Broadway StSan FranciscoCA9413337.8-122.4{'longitude': '-122.403154', 'needs_recoding': False, 'latitude': '37.798358', 'human_address': '{"address":"","city":"","state":"","zip":""}'}5800_201710172017-10-1798Routine - Unscheduled5800_20171017_103149Wiping cloths not clean or properly stored or inadequate sanitizerLow Risk
64236Sutter Pub and Restaurant700 Sutter StSan FranciscoCA9410237.79-122.41{'longitude': '-122.41188', 'needs_recoding': False, 'latitude': '37.78881', 'human_address': '{"address":"","city":"","state":"","zip":""}'}64236_201707252017-07-2588Routine - Unscheduled64236_20170725_103133Foods not protected from contaminationModerate Risk
1991SRI THAI CUISINE4621 LINCOLN WaySan FranciscoCA9412237.76-122.51{'longitude': '-122.507779', 'needs_recoding': False, 'latitude': '37.764073', 'human_address': '{"address":"","city":"","state":"","zip":""}'}1991_201711292017-11-2986Routine - Unscheduled1991_20171129_103139Improper food storageLow Risk
3816Washington Bakery & Restaurant733 Washington StSan FranciscoCA9410837.8-122.41{'longitude': '-122.405845', 'needs_recoding': False, 'latitude': '37.795174', 'human_address': '{"address":"","city":"","state":"","zip":""}'}3816_201607282016-07-2867Routine - Unscheduled3816_20160728_103108Contaminated or adulterated foodHigh Risk
39119Brothers Restaurant4128 GEARY BlvdSan FranciscoCA9411837.78-122.46{'longitude': '-122.463762', 'needs_recoding': False, 'latitude': '37.781148', 'human_address': '{"address":"","city":"","state":"","zip":""}'}39119_201607182016-07-1879Routine - Unscheduled39119_20160718_103133Foods not protected from contaminationModerate Risk
5
Number of violations
View solution

You are given a dataset of health inspections that includes details about violations. Each row represents an inspection, and if an inspection resulted in a violation, the `violation_id` column will contain a value. Count the total number of violations that occurred at **'Roxanne Cafe'** for each year, based on the inspection date. Output the year and the corresponding number of violations in ascending order of the year.

Tables: sf_restaurant_health_violations

Key Takeaways

  • EXTRACT(part FROM date) pulls out year, month, day, hour, etc.
  • Dialect syntax varies significantly — know your target database
  • PostgreSQL — EXTRACT(); MySQL/MSSQL use YEAR(), MONTH(), DAY()
  • Day of week numbering differs by database — always verify with test data
  • EXTRACT works in SELECT, WHERE, GROUP BY, and ORDER BY

What’s Next

Extracting parts is just the beginning. Next, you’ll learn to perform date arithmetic: adding days, calculating differences between dates, and truncating to periods like weeks or months for time-series analysis.