SQLab Hub
Lesson 25 · SQL Date Functions
Lesson 25 · Functions

SQL Date Functions: Syntax, Examples & Practice

SQL date functions let a query work with dates. Use them to get today's date, pull the year or month out of a date, add or subtract days, and count the days between two dates. The function names differ by database, so every example is labelled with the one it runs on.

🗄 SQLite · MySQL · PostgreSQL · SQL Server ▶ 26 runnable examples 🧪 8 exercises · 30-question quiz 🗓 Updated

What Are SQL Date Functions?

A date function takes a date and returns something useful about it: its year, the date 30 days later, the number of days since it happened. Here are two of them on real data — the year of each order, and the date 30 days after it:

SQLite · runs in the Playground
SELECT id, order_date,
       strftime('%Y', order_date)   AS order_year,
       date(order_date, '+30 days') AS plus_30_days
FROM orders
ORDER BY id
LIMIT 5;

▶ Run this query in the SQL Playground

Result of the first date function example
idorder_dateorder_yearplus_30_days
12024-04-2420242024-05-24
22024-03-1520242024-04-14
32023-02-1520232023-03-17
42022-01-1020222022-02-09
52023-06-1620232023-07-16
✓ 5 rows from the Playground orders table

For every order, strftime('%Y', …) extracts the year and date(…, '+30 days') returns the date 30 days later. The second function does real calendar arithmetic: 30 days after 15 March is 14 April, and 30 days after 15 February 2023 is 17 March, because February has 28 days. That query is written for SQLite. In SQL Server the same two columns are YEAR(order_date) and DATEADD(day, 30, order_date); the comparison table below gives the MySQL and PostgreSQL versions too.

SQL Date Functions Syntax in MySQL, PostgreSQL, SQL Server and SQLite

Every operation this lesson covers, with the function each database uses for it. d is a date column; a is the earlier date and b the later one.

SQL date function syntax by operation and database
OperationSQLiteMySQLPostgreSQLSQL Server
Current datedate('now')CURDATE()CURRENT_DATECAST(GETDATE() AS date)
Current date and timedatetime('now')NOW()NOW()GETDATE()
Extract the yearstrftime('%Y', d)YEAR(d)EXTRACT(YEAR FROM d)YEAR(d)
Extract the monthstrftime('%m', d)MONTH(d)EXTRACT(MONTH FROM d)MONTH(d)
Add 30 daysdate(d, '+30 days')DATE_ADD(d, INTERVAL 30 DAY)d + INTERVAL '30 days'DATEADD(day, 30, d)
Days between two datesjulianday(b) - julianday(a)DATEDIFF(b, a)b - aDATEDIFF(day, a, b)
Format as DD-MM-YYYYstrftime('%d-%m-%Y', d)DATE_FORMAT(d, '%d-%m-%Y')TO_CHAR(d, 'DD-MM-YYYY')FORMAT(d, 'dd-MM-yyyy')
First day of the monthdate(d, 'start of month')DATE_FORMAT(d, '%Y-%m-01')DATE_TRUNC('month', d)DATEFROMPARTS(YEAR(d), MONTH(d), 1)
💡 Read the table by row, not by column: learn the operation once, then look up the spelling for the database in front of you. Oracle follows the PostgreSQL column for EXTRACT and TO_CHAR, and uses SYSDATE and ADD_MONTHS().

What Date Functions Do

Date functions are the part of SQL that understands calendars. A column such as order_date is only a value until you ask a question about it — which year? how long ago? what is the date 30 days later? — and date functions answer those questions inside the query. They cover five everyday operations:

  • Get the current date or timestamp — today's date, or the date and time right now.
  • Extract date parts — pull the year, month, day or weekday out of a date.
  • Date arithmetic — add or subtract days, months or years.
  • Date difference — count the days, months or years between two dates.
  • Date filtering, grouping and formatting — select a date range, build monthly reports, change how a date is displayed.
One thing to know first: date functions are the least standardised part of SQL. The operations are the same in every database, but the function names differ. Examples labelled SQLite run as-is in the SQLabHub SQL Playground; every section also shows the MySQL, PostgreSQL and SQL Server spelling, each labelled in the corner of its code block.

Basic Date Function Syntax

A date function takes a date (and usually an instruction) and returns a new value:

Pattern · SQLite
function_name(date_value, instruction)

strftime('%Y', order_date)       -- extract: the year of order_date
date(order_date, '+30 days')     -- arithmetic: 30 days after order_date

The Four SQLite Date Functions

The SQLabHub Playground runs SQLite, which does everything with four functions and a list of modifiers:

The four core SQLite date functions
FunctionReturnsExampleResult
date(value, modifiers…)A date as YYYY-MM-DDdate('2024-04-24', '+1 month')2024-05-24
datetime(value, modifiers…)A date and timedatetime('2024-04-24 14:30:25', '+2 hours')2024-04-24 16:30:25
strftime(format, value)Any part or format, as textstrftime('%Y-%m', '2024-04-24')2024-04
julianday(value)A day number, for subtractionjulianday('2024-04-24') - julianday('2024-04-01')23.0

Modifiers are short strings such as '+30 days', '-1 year', 'start of month' and 'localtime'. Several can be chained and they are applied left to right.

Date and Time Data Types

Before using date functions, know what kind of value you are working with.

SQL date and time data types
TypeStoresExample
DATECalendar date only — no time component2026-07-12
DATETIMEDate plus a time of day, down to the second2026-07-12 14:30:25
TIMESTAMPDate and time, often time-zone aware, typically used for "record created" columns2026-07-12 14:30:25 UTC
TIMETime of day only — no date at all14:30:25
MySQL / PostgreSQL
CREATE TABLE deliveries (
  order_id      INT PRIMARY KEY,
  order_date    DATE,
  placed_at     TIMESTAMP,
  delivery_date DATE
);
SQLite is different: it has no dedicated date types. Dates are stored as text in ISO format ('2024-04-24') and the date functions interpret that text. This is why every date in the Playground looks like YYYY-MM-DD, and why that exact, zero-padded format matters for comparisons and sorting.
⚠ Storing dates as free-form text in any database — '12/07/2026', 'July 12' — is one of the most common causes of broken date logic. See SQL data types for choosing column types, and the data cleaning section below for fixing it.

Current Date and Time

The anchor for every "today", "this month" and "last 30 days" query.

SQL CURRENT_DATE

Definition — CURRENT_DATE

CURRENT_DATE returns today's calendar date only — no hours, minutes or seconds. It is a standard SQL keyword (written without parentheses) available in PostgreSQL, MySQL, SQLite and Oracle. SQL Server only added it in SQL Server 2025 (and Azure SQL Database); on SQL Server 2022 and earlier use CAST(GETDATE() AS date), which works on every version.

PostgreSQL / MySQL / SQLite
SELECT CURRENT_DATE;

Daily reports

Comparing order_date with today's date in a WHERE clause selects exactly today's orders.

Age and tenure calculations

The current date is the anchor point for every "how long since" calculation.

Renewal reminders

Comparing a renewal_date with today and with today plus seven days flags upcoming renewals.

SQL NOW() and CURRENT_TIMESTAMP

Definition — NOW()

NOW() returns the current date and time together, including hours, minutes and seconds. It exists in MySQL and PostgreSQL. The standard keyword is CURRENT_TIMESTAMP, SQL Server uses GETDATE(), and SQLite uses datetime('now').

MySQL / PostgreSQL
SELECT NOW();
CURRENT_DATE compared with NOW()
AspectCURRENT_DATENOW()
ReturnsDate onlyDate + time
Example2026-07-122026-07-12 14:30:25
Typical useFiltering "today", age and tenure mathsAudit logs, created_at timestamps, precise event ordering

Current Date in the Playground (SQLite)

SQLite · runs in the Playground
SELECT date('now')                  AS today_utc,
       datetime('now')              AS right_now_utc,
       datetime('now', 'localtime') AS right_now_local;

▶ Run this query in the SQL Playground

The result changes every time you run it, so no fixed output is shown here. Note the column names: SQLite's 'now' — and its CURRENT_DATE — are in UTC. Add the 'localtime' modifier to convert to the machine's local time zone.

⚠ In every database, "now" is resolved in some time zone — UTC in SQLite, the server or session time zone elsewhere. A value logged at 11:58 PM local time can land on the wrong calendar day if the two differ. See Common Mistakes.
💡 The Playground's data ends in December 2024, so "last 30 days from today" returns nothing there. The examples below use a fixed date, or the newest date in the table, as "today" — the technique is identical.

Extracting Date Parts

Pulling the year, month or day out of a date — the first step of almost every report.

SQL YEAR(), MONTH() and DAY()

Definition — YEAR, MONTH and DAY

YEAR(date) returns the four-digit year, MONTH(date) the month number from 1 to 12, and DAY(date) the day of the month from 1 to 31. These three functions exist in MySQL and SQL Server.

MySQL / SQL Server
SELECT order_date,
       YEAR(order_date)  AS yr,
       MONTH(order_date) AS mo,
       DAY(order_date)   AS dy
FROM orders
ORDER BY id;
YEAR, MONTH and DAY applied to the first Playground orders
order_dateyrmody
2024-04-242024424
2024-03-152024315
2023-02-152023215
2022-01-102022110
First 4 rows of the Playground orders table: the same dates the SQLite strftime() example below returns
YEAR compared with MONTH and DAY
FunctionReturnsExample (2026-07-12)Typical use
YEAR()Four-digit year2026Annual reports, year-over-year comparisons
MONTH()Month number 1–127Monthly reports, seasonality
DAY()Day of month 1–3112Building custom date ranges
How they combine: almost every real report uses at least two of these together — YEAR + MONTH for monthly revenue across several years, or all three when building an exact custom date range. DAY() on its own is rare.

SQL EXTRACT()

Definition — EXTRACT

EXTRACT(part FROM date) returns one component of a date or time value — YEAR, MONTH, DAY, HOUR and more. It is the standard SQL syntax, supported by PostgreSQL, MySQL and Oracle.

PostgreSQL / MySQL / Oracle
SELECT id, order_date,
       EXTRACT(YEAR FROM order_date)  AS order_year,
       EXTRACT(MONTH FROM order_date) AS order_month
FROM orders;
How EXTRACT is put together
Part of the callWhat it does
EXTRACT(...)The function call itself
YEARThe date part being requested — could also be MONTH, DAY, HOUR and so on
FROM order_dateThe source column to extract the part from

strftime() in SQLite

SQLite has neither YEAR() nor EXTRACT(). It uses strftime(format, date), where the format string names the part you want:

SQLite · runs in the Playground
SELECT id, order_date,
       strftime('%Y', order_date) AS order_year,
       strftime('%m', order_date) AS order_month,
       strftime('%d', order_date) AS order_day
FROM orders
ORDER BY id
LIMIT 5;

▶ Run this query in the SQL Playground

Result of extracting year, month and day with strftime
idorder_dateorder_yearorder_monthorder_day
12024-04-2420240424
22024-03-1520240315
32023-02-1520230215
42022-01-1020220110
52023-06-1620230616
✓ 5 rows from the Playground orders table
🚫 strftime() returns text, not a number. WHERE strftime('%Y', order_date) = 2024 matches nothing in the Playground; = '2024' (with quotes) matches 66 orders. To get a number, wrap it: CAST(strftime('%m', order_date) AS INTEGER).

EXTRACT vs YEAR / MONTH / DAY: Which Database Supports What

Support for YEAR, MONTH, DAY and EXTRACT by database
DatabaseYEAR() / MONTH() / DAY()EXTRACT()Other
MySQLYesYes—
SQL ServerYesNoDATEPART(year, d)
PostgreSQLNoYesdate_part('year', d)
OracleNoYes—
SQLiteNoNostrftime('%Y', d)
⚠ A query written with EXTRACT() for PostgreSQL will not run on SQL Server, and YEAR() will not run on PostgreSQL. The full comparison is in SQL Date Functions by Dialect.

Date Arithmetic: Adding and Subtracting Time

Shifting a date forward or backward — expiry dates, deadlines, "30 days ago".

SQL DATEADD() — SQL Server

Definition — DATEADD

DATEADD(unit, number, date) adds number units of time (day, month, year, hour…) to date. A negative number subtracts instead. DATEADD is a SQL Server function.

SQL Server
SELECT id, order_date,
       DATEADD(day, 30, order_date) AS thirty_days_later
FROM orders
ORDER BY id;
DATEADD adding 30 days to the first Playground orders
idorder_datethirty_days_later
12024-04-242024-05-24
22024-03-152024-04-14
32023-02-152023-03-17
42022-01-102022-02-09
First 4 rows of the Playground orders table: the same dates as plus_30_days in the SQLite example below
The three arguments of DATEADD
ArgumentMeaningValue in example
unitThe interval type — day, month, year, hourday
numberHow many units to add (negative to subtract)30
dateThe starting dateorder_date
Adding months and years
SQL Server
SELECT DATEADD(month, 6, start_date) AS six_months_later,
       DATEADD(year, 1, start_date)  AS one_year_later
FROM subscriptions;
💡 A negative number subtracts — DATEADD(day, -30, GETDATE()) means "30 days ago", the building block behind every "last 30 days" report.

DATE_ADD() in MySQL and INTERVAL in PostgreSQL

MySQL
SELECT DATE_ADD(order_date, INTERVAL 30 DAY) AS renewal
FROM orders;
PostgreSQL
SELECT order_date + INTERVAL '30 days' AS renewal
FROM orders;

MySQL also has DATE_SUB() for going backward. In PostgreSQL, adding a whole number to a date (order_date + 30) adds that many days and returns a date; adding an INTERVAL returns a timestamp.

Date Modifiers in SQLite

SQLite · runs in the Playground
SELECT id, order_date,
       date(order_date, '+30 days') AS plus_30_days,
       date(order_date, '+1 month') AS plus_1_month,
       date(order_date, '-1 year')  AS minus_1_year
FROM orders
ORDER BY id
LIMIT 5;

▶ Run this query in the SQL Playground

Result of SQLite date modifiers
idorder_dateplus_30_daysplus_1_monthminus_1_year
12024-04-242024-05-242024-05-242023-04-24
22024-03-152024-04-142024-04-152023-03-15
32023-02-152023-03-172023-03-152022-02-15
42022-01-102022-02-092022-02-102021-01-10
52023-06-162023-07-162023-07-162022-06-16
✓ 5 rows from the Playground orders table

"30 days" and "1 month" are not the same thing: for the order on 15 March, 30 days later is 14 April but one month later is 15 April.

⚠ Month arithmetic at the end of a month differs by database. Adding one month to 31 January gives the last day of February in SQL Server, MySQL and PostgreSQL. SQLite does not clamp: date('2024-01-31', '+1 month') returns 2024-03-02.

DATEADD Business Examples

Five real scenarios that are all the same pattern underneath. These use SQL Server syntax on illustrative tables.

Subscription expiry — 12 months after start
SQL Server
SELECT customer, plan, start_date,
       DATEADD(month, 12, start_date) AS expiry_date
FROM subscriptions;
Subscription expiry dates calculated with DATEADD
customerplanstart_dateexpiry_date
Neha KapoorPro2026-01-102027-01-10
Vikram SinghBasic2026-02-152027-02-15
Aditi SharmaEnterprise2025-08-012026-08-01
Rohan MehtaPro2026-06-202027-06-20
Illustrative sample rows — not a Playground table
Estimated delivery date — 6 days after order
SQL Server
SELECT id, order_date,
       DATEADD(day, 6, order_date) AS estimated_delivery
FROM orders;
Membership renewal reminder — renewals due within 7 days
SQL Server
SELECT customer, renewal_date
FROM subscriptions
WHERE renewal_date <= DATEADD(day, 7, CAST(GETDATE() AS date));
Product warranty expiration — 1 year after purchase
SQL Server
SELECT id, order_date,
       DATEADD(year, 1, order_date) AS warranty_expiry
FROM orders;
Employee probation period — 90 days after hire
SQL Server
SELECT first_name, last_name, hire_date,
       DATEADD(day, 90, hire_date) AS probation_end
FROM employees;
The pattern always repeats: subscription expiry, delivery estimates, warranty expiration and probation end dates are all the same shape — only the unit, the number and the starting column change. In the Playground the last one is date(hire_date, '+90 days') on the real employees table.

Calculating Date Differences

Measuring the gap between two dates — one of the most common SQL interview topics.

SQL DATEDIFF() — SQL Server

Definition — DATEDIFF

DATEDIFF(unit, start_date, end_date) returns the difference between two dates in the given unit — most often days. It computes end_date minus start_date. This three-argument form is SQL Server syntax.

SQL Server
SELECT order_id, order_date, delivery_date,
       DATEDIFF(day, order_date, delivery_date) AS delivery_days
FROM deliveries;
DATEDIFF measuring delivery time on sample orders
order_idorder_datedelivery_datedelivery_days
1012026-01-052026-01-094
1022026-02-142026-02-206
1032026-03-022026-03-075
1042026-04-182026-04-257
1052026-05-092026-05-145
Illustrative deliveries table (defined in the data types section), not a Playground table. The Playground's shipped_date values are random test data, so they are not used for delivery times.
The three arguments of DATEDIFF in SQL Server
ArgumentMeaningValue in example
unitThe unit of the result — day, month, yearday
start_dateThe earlier dateorder_date
end_dateThe later datedelivery_date
🚫 Argument order matters — DATEDIFF(day, delivery_date, order_date) flips the sign and produces a negative result. Always pass the earlier date first.
⚠ SQL Server's DATEDIFF counts unit boundaries crossed, not elapsed time: DATEDIFF(year, '2025-12-31', '2026-01-01') is 1, even though only one day has passed.

DATEDIFF in MySQL and Date Subtraction in PostgreSQL

MySQL
-- end date first, result always in days
SELECT DATEDIFF(delivery_date, order_date) AS delivery_days
FROM deliveries;
PostgreSQL
-- subtracting two dates gives days
SELECT delivery_date - order_date AS delivery_days
FROM deliveries;

MySQL's DATEDIFF has the same name as SQL Server's but a different signature: two arguments, end date first, days only. For months or years in MySQL use TIMESTAMPDIFF(MONTH, start_date, end_date).

Date Differences in SQLite: julianday()

julianday() converts a date into a continuous day number, so subtracting two of them gives the days between the dates. This example measures each employee's tenure as of 1 January 2025:

SQLite · runs in the Playground
SELECT id, first_name, hire_date,
       CAST(julianday('2025-01-01') - julianday(hire_date) AS INTEGER) AS tenure_days
FROM employees
ORDER BY id
LIMIT 5;

▶ Run this query in the SQL Playground

Employee tenure in days calculated with julianday
idfirst_namehire_datetenure_days
1William2022-07-24892
2John2021-08-091241
3Daniel2016-06-063131
4Melissa2022-04-13994
5Jason2019-10-171903
✓ 5 rows from the Playground employees table

William was hired on 24 July 2022, which is 892 days before 1 January 2025. Replace '2025-01-01' with 'now' to measure against today instead.

DATEDIFF Practical Examples

Five business questions built on a date difference. SQL Server syntax on illustrative tables; GETDATE() is the current date and time.

Days since a customer's last order
SQL Server
SELECT user_id, MAX(order_date) AS last_order,
       DATEDIFF(day, MAX(order_date), GETDATE()) AS days_since_last_order
FROM orders
GROUP BY user_id;
Employee tenure in days
SQL Server
SELECT first_name, last_name, hire_date,
       DATEDIFF(day, hire_date, GETDATE()) AS tenure_days
FROM employees;
Customer retention window
SQL Server
SELECT user_id,
       DATEDIFF(day, MIN(order_date), MAX(order_date)) AS retention_days
FROM orders
GROUP BY user_id;
Project duration
SQL Server
SELECT project_id,
       DATEDIFF(day, start_date, end_date) AS project_duration_days
FROM projects;
Delivery delay flag
SQL Server
SELECT order_id,
       DATEDIFF(day, order_date, delivery_date) AS delivery_days
FROM deliveries
WHERE DATEDIFF(day, order_date, delivery_date) > 7;
💡 Tenure, retention, project duration and delivery delay are the four date-difference questions that show up most often in SQL interviews. Three of them use aggregate functions such as MIN and MAX to find the dates first.

Calculating Age in SQL

Customer age, employee age, years of service — all the same calculation: count the full years between a start date and today. The first two queries assume a members table with a dob (date of birth) column.

Quick approximation
SQL Server
SELECT first_name, last_name, dob,
       DATEDIFF(day, dob, GETDATE()) / 365.25 AS approx_age
FROM members;
Exact age — subtract 1 if this year's birthday has not happened yet
SQL Server
SELECT first_name, last_name, dob,
       DATEDIFF(year, dob, GETDATE())
       - CASE WHEN DATEADD(year, DATEDIFF(year, dob, GETDATE()), dob) > GETDATE()
              THEN 1 ELSE 0 END AS exact_age
FROM members;
⚠ A naive YEAR(today) - YEAR(dob) overstates age by one whenever this year's birthday is still to come. The CASE WHEN adjustment above is the correct version. MySQL does it in one call: TIMESTAMPDIFF(YEAR, dob, CURDATE()); PostgreSQL uses EXTRACT(YEAR FROM AGE(dob)).

The Playground's employees table has no date of birth, but the same "full years" logic applies to hire_date — completed years of service as of 1 January 2025:

SQLite · runs in the Playground
SELECT first_name, hire_date,
       CAST(strftime('%Y', '2025-01-01') AS INTEGER)
       - CAST(strftime('%Y', hire_date) AS INTEGER)
       - (strftime('%m-%d', '2025-01-01') < strftime('%m-%d', hire_date)) AS full_years
FROM employees
ORDER BY id
LIMIT 4;

▶ Run this query in the SQL Playground

Completed years of service calculated in SQLite
first_namehire_datefull_years
William2022-07-242
John2021-08-093
Daniel2016-06-068
Melissa2022-04-132
✓ 4 rows from the Playground employees table

The comparison in the last line is 1 when the anniversary (month and day) has not been reached yet, which subtracts the incomplete year. William's 2025 − 2022 = 3 becomes 2 because 24 July comes after 1 January.

Filtering Rows by Date

Selecting a day, a month, a year or a moving window.

To filter records by date, compare the date column in a WHERE clause. The most reliable pattern is a half-open range: from the first day you want, up to but not including the first day you don't.

SQLite · runs in the Playground
SELECT COUNT(*) AS orders_in_2024
FROM orders
WHERE order_date >= '2024-01-01'
  AND order_date <  '2025-01-01';

▶ Run this query in the SQL Playground

Number of orders in 2024
orders_in_2024
66
✓ 1 row from the Playground orders table

This works unchanged in MySQL, PostgreSQL and SQL Server. It also stays correct when the column holds a time of day: BETWEEN '2024-01-01' AND '2024-12-31' would miss anything timestamped after midnight on 31 December. The SQL BETWEEN lesson explains why its upper bound is inclusive.

Filtering for One Exact Date

For a single day, compare the column with a date written as 'YYYY-MM-DD':

SQLite · runs in the Playground
SELECT id, user_id, order_date, total
FROM orders
WHERE order_date = '2024-04-24'
ORDER BY id;

▶ Run this query in the SQL Playground

Orders placed on 24 April 2024
iduser_idorder_datetotal
1432024-04-242638.11
134552024-04-242007.49
157922024-04-244882
✓ 3 rows from the Playground orders table
⚠ = only finds the whole day when the column holds a date and nothing else. If it also stores a time, such as 2024-04-24 13:23:44, the same filter returns no rows in SQLite, and in MySQL, PostgreSQL and SQL Server it matches only rows stamped exactly at midnight. Use a one-day range instead — order_date >= '2024-04-24' AND order_date < '2024-04-25' — which works for both kinds of column in every database.

Filtering With a Function on the Column

MySQL / SQL Server
SELECT *
FROM orders
WHERE YEAR(order_date) = 2026;
⚠ This returns the right rows, but wrapping the column in a function usually prevents the database from using an index on it. Prefer the range form on large tables — see Performance Tips.

Rolling 30-Day Window

A rolling window moves with the calendar instead of using fixed months. In production the anchor is today; in the Playground we anchor on the newest order date, 2024-12-24:

SQLite · runs in the Playground
SELECT COUNT(*) AS orders, ROUND(SUM(total), 2) AS revenue
FROM orders
WHERE order_date >  date('2024-12-24', '-30 days')
  AND order_date <= '2024-12-24';

▶ Run this query in the SQL Playground

Orders and revenue in the 30 days up to 24 December 2024
ordersrevenue
610621.36
✓ 1 row from the Playground orders table
SQL Server
SELECT SUM(amount) AS rolling_30_day_sales
FROM sales
WHERE sale_date >  DATEADD(day, -30, CAST(GETDATE() AS date))
  AND sale_date <= CAST(GETDATE() AS date);
💡 A rolling window smooths out noisy day-to-day spikes better than fixed calendar-month buckets, which is why most BI trend lines default to a rolling 7, 30 or 90-day view.

Current Month Sales (Month to Date)

Month-to-date revenue is "everything since the first day of the current month". Here the anchor is the newest order in the table, found with a subquery:

SQLite · runs in the Playground
SELECT COUNT(*) AS orders, ROUND(SUM(total), 2) AS mtd_revenue
FROM orders
WHERE order_date >= date((SELECT MAX(order_date) FROM orders), 'start of month');

▶ Run this query in the SQL Playground

Month-to-date orders and revenue
ordersmtd_revenue
610621.36
✓ 1 row — December 2024, the latest month in the Playground data
SQL Server
SELECT SUM(amount) AS mtd_revenue
FROM sales
WHERE sale_date >= DATEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), 1);
💡 This is the query behind almost every "This Month" tile on a dashboard — it updates on its own as the current date moves forward.

Grouping Data by Date

The monthly report — the single most common date query in business SQL.

Extract the part of the date you want to report on, then GROUP BY it and summarise each group with an aggregate function.

SQLite · runs in the Playground
SELECT strftime('%Y-%m', order_date) AS month,
       COUNT(*)             AS orders,
       ROUND(SUM(total), 2) AS revenue
FROM orders
WHERE order_date >= '2024-07-01' AND order_date < '2025-01-01'
GROUP BY strftime('%Y-%m', order_date)
ORDER BY month;

▶ Run this query in the SQL Playground

Monthly orders and revenue for the second half of 2024
monthordersrevenue
2024-07610296.67
2024-08825429.09
2024-0914319.46
2024-1037996.72
2024-11617380.72
2024-12610621.36
✓ 6 rows from the Playground orders table

Each order is labelled with its year and month ('2024-07'), rows with the same label are grouped, and SUM and COUNT run once per group. Sorting with ORDER BY works because YYYY-MM text sorts chronologically.

🚫 Grouping by the month alone merges the same month from different years. In the Playground, GROUP BY strftime('%m', order_date) reports 18 orders for month 01 — that is January 2022, 2023 and 2024 added together. Always include the year.
MySQL / SQL Server
SELECT YEAR(sale_date) AS yr, MONTH(sale_date) AS mo, SUM(amount) AS total_sales
FROM sales
GROUP BY YEAR(sale_date), MONTH(sale_date)
ORDER BY yr, mo;

Date Functions with Aggregate Functions

SQLite · runs in the Playground
SELECT strftime('%Y', order_date) AS year,
       COUNT(*)             AS orders,
       ROUND(SUM(total), 2) AS revenue
FROM orders
GROUP BY strftime('%Y', order_date)
ORDER BY year;

▶ Run this query in the SQL Playground

Orders and revenue per year
yearordersrevenue
202262165062.86
202372184615.11
202466161292.74
✓ 3 rows from the Playground orders table
💡 A date function in SELECT and GROUP BY plus an aggregate measuring the metric is the most reused query shape in reporting. To keep only some groups — say, months above a revenue target — add HAVING.

Month-over-Month (MoM) Analysis

MoM compares each month with the one before it. Build the monthly totals first, then read the previous row with the window function LAG(), covered step by step in SQL LAG and LEAD:

SQLite · runs in the Playground
SELECT month, revenue,
       LAG(revenue) OVER (ORDER BY month) AS prev_month
FROM (
  SELECT strftime('%Y-%m', order_date) AS month,
         ROUND(SUM(total), 2) AS revenue
  FROM orders
  WHERE order_date >= '2024-09-01'
  GROUP BY strftime('%Y-%m', order_date)
) AS monthly;

▶ Run this query in the SQL Playground

Monthly revenue next to the previous month
monthrevenueprev_month
2024-094319.46NULL
2024-107996.724319.46
2024-1117380.727996.72
2024-1210621.3617380.72
✓ 4 rows from the Playground orders table
💡 MoM growth = (this month − last month) ÷ last month × 100. December 2024 against November: (10621.36 − 17380.72) ÷ 17380.72 = −38.9%.

Year-over-Year (YoY) Analysis

YoY compares a period with the same period one year earlier, which removes seasonal effects. Conditional aggregation puts both numbers in one row:

SQLite · runs in the Playground
SELECT ROUND(SUM(CASE WHEN strftime('%Y-%m', order_date) = '2024-12' THEN total ELSE 0 END), 2) AS dec_2024,
       ROUND(SUM(CASE WHEN strftime('%Y-%m', order_date) = '2023-12' THEN total ELSE 0 END), 2) AS dec_2023
FROM orders;

▶ Run this query in the SQL Playground

December revenue in 2024 and 2023
dec_2024dec_2023
10621.3629761.96
✓ 1 row from the Playground orders table
MySQL / SQL Server
SELECT
  SUM(CASE WHEN YEAR(sale_date) = 2026 AND MONTH(sale_date) = 7 THEN amount ELSE 0 END) AS july_2026,
  SUM(CASE WHEN YEAR(sale_date) = 2025 AND MONTH(sale_date) = 7 THEN amount ELSE 0 END) AS july_2025
FROM sales;
Executive dashboard example: "Revenue is up 18% YoY" is the most common headline number in a board deck — and it is nothing more than two SUM(CASE WHEN …) expressions compared with each other.

Formatting Dates

Presenting a date the way a report or region expects to see it.

Common date display formats
FormatExampleCommon region
YYYY-MM-DD2026-07-12ISO 8601 — unambiguous, safest for storage
DD-MM-YYYY12-07-2026India, UK, most of Europe
MM-DD-YYYY07-12-2026United States
SQLite · runs in the Playground
SELECT id, order_date,
       strftime('%d-%m-%Y', order_date) AS dd_mm_yyyy,
       strftime('%m/%d/%Y', order_date) AS us_style
FROM orders
ORDER BY id
LIMIT 3;

▶ Run this query in the SQL Playground

Dates formatted with strftime
idorder_datedd_mm_yyyyus_style
12024-04-2424-04-202404/24/2024
22024-03-1515-03-202403/15/2024
32023-02-1515-02-202302/15/2023
✓ 3 rows from the Playground orders table
MySQL
SELECT DATE_FORMAT(order_date, '%d-%m-%Y') AS formatted_date
FROM orders;
PostgreSQL / Oracle
SELECT TO_CHAR(order_date, 'DD-MM-YYYY') AS formatted_date
FROM orders;
SQL Server
SELECT FORMAT(order_date, 'dd-MM-yyyy') AS formatted_date
FROM orders;
💡 Format only at display time. The stored value should stay in an unambiguous form — a real date type, or ISO YYYY-MM-DD text in SQLite — because a formatted string can no longer be sorted, compared or used in date arithmetic. For more text manipulation, see SQL string functions.

First Day and Last Day of the Month

Clean month boundaries are essential for billing cycles and month-end reports.

SQLite · runs in the Playground
SELECT date('2024-12-24', 'start of month')                        AS month_start,
       date('2024-12-24', 'start of month', '+1 month', '-1 day') AS month_end;

▶ Run this query in the SQL Playground

First and last day of December 2024
month_startmonth_end
2024-12-012024-12-31
✓ 1 row

The modifiers run left to right: jump to the 1st, add a month, step back one day. The same chain gives 2024-02-29 for a February 2024 date, so leap years are handled for you.

SQL Server
SELECT DATEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), 1) AS month_start,
       EOMONTH(GETDATE())                                 AS month_end;
Business reporting scenario: subscription billing anchors every invoice to the first day of the month, while finance needs the exact last day for the month-end close. MySQL has LAST_DAY(d); PostgreSQL uses DATE_TRUNC('month', d) for the start of the month.

Cleaning Date Data

Real-world dates are messy — the five problems to check for first.

Invalid dates

Values like '2026-02-30' or '2026-13-01' that do not correspond to a real calendar day — validate or reject them before analysis.

Missing dates

NULL dates make date arithmetic and differences return NULL silently — check for them first, or supply a fallback with COALESCE.

Format issues

Mixed formats like '12-07-2026' and '2026-07-12' in one text column silently misparse without explicit standardisation.

Time zone problems

Current-timestamp functions resolve in the server (or UTC) time zone, which can differ from the business's local time.

Standardisation

Convert every date column to a real date type — or one agreed ISO text format — as early as possible in the pipeline.

Detecting Values That Are Not Real Dates

SQL Server
-- rows whose text does not parse as a date
SELECT raw_date_text
FROM legacy_orders
WHERE TRY_CAST(raw_date_text AS date) IS NULL;

In SQLite, date(value) returns NULL for text it cannot read (date('12-07-2026') and date('2026-13-01') are both NULL), so WHERE date(col) IS NULL does the same job. Be careful with impossible days: date('2026-02-30') is not rejected — it rolls forward to 2026-03-02.

A Real Data-Quality Check in the Playground

Dates can be perfectly formatted and still be wrong. An order cannot ship before it was placed, so this query looks for exactly that:

SQLite · runs in the Playground
SELECT id, order_date, shipped_date
FROM orders
WHERE shipped_date < order_date
ORDER BY id;

▶ Run this query in the SQL Playground

Orders whose shipped date is earlier than the order date
idorder_dateshipped_date
22024-03-152023-04-09
172023-11-272023-04-02
192024-08-112023-11-27
✓ 38 rows in the Playground orders table (first 3 shown)

The Playground's practice data has 38 such orders — its shipped_date values are randomly generated, so do not read shipping times from them. Finding rows like these before building a delivery-time report is precisely what date cleaning is for.

⚠ A value such as "12-07-2026" is ambiguous unless you know whether the source system is day-first or month-first — always confirm the source format before trusting a bulk import.

Practical Date Function Examples

35 real business scenarios across sales, retention, subscriptions, banking, insurance, healthcare, SaaS and more.

About these examples: they use SQL Server syntax on illustrative tables that are not in the Playground. @today stands for the current date, declared once as DECLARE @today date = CAST(GETDATE() AS date);. To translate a query to another database, swap the functions using the dialect table.
#1Sales Reporting

Daily Sales Report

Store operations wants total sales broken down by exact calendar day.

SQL Server
SELECT sale_date, SUM(amount) AS daily_total
FROM sales
GROUP BY sale_date
ORDER BY sale_date;
#2Sales Reporting

Weekly Sales Report

Regional management wants sales totals rolled up by week instead of by day.

SQL Server
SELECT DATEADD(day, 1 - DATEPART(weekday, sale_date), sale_date) AS week_start,
       SUM(amount) AS weekly_total
FROM sales
GROUP BY DATEADD(day, 1 - DATEPART(weekday, sale_date), sale_date)
ORDER BY week_start;
-- the first day of the week follows the server's DATEFIRST setting
#3Revenue Analysis

Monthly Revenue Report

Finance wants total revenue for every calendar month across all years of sales data.

SQL Server
SELECT YEAR(sale_date) AS yr, MONTH(sale_date) AS mo, SUM(amount) AS monthly_revenue
FROM sales
GROUP BY YEAR(sale_date), MONTH(sale_date)
ORDER BY yr, mo;
#4Revenue Analysis

Quarterly Revenue Report

The CFO wants revenue rolled up into fiscal quarters instead of months.

SQL Server
SELECT YEAR(sale_date) AS yr,
       DATEPART(quarter, sale_date) AS quarter,
       SUM(amount) AS quarterly_revenue
FROM sales
GROUP BY YEAR(sale_date), DATEPART(quarter, sale_date)
ORDER BY yr, quarter;
#5Revenue Analysis

Annual Revenue Report

The board wants one total revenue number per calendar year for the last 5 years.

SQL Server
SELECT YEAR(sale_date) AS yr, SUM(amount) AS annual_revenue
FROM sales
GROUP BY YEAR(sale_date)
ORDER BY yr;
#6Customer Retention

Customer Retention by Signup Month

Growth wants to see how many customers from each signup month are still active today.

SQL Server
SELECT YEAR(signup_date) AS yr, MONTH(signup_date) AS mo,
       COUNT(*) AS signups,
       SUM(CASE WHEN last_active_date >= DATEADD(day, -30, @today) THEN 1 ELSE 0 END) AS still_active
FROM customers
GROUP BY YEAR(signup_date), MONTH(signup_date);
#7Churn Analysis

Churned Subscriptions This Month

The subscriptions team wants every subscriber whose renewal_date has already passed without renewing.

SQL Server
SELECT customer, plan, renewal_date
FROM subscriptions
WHERE renewal_date < @today;
#8Subscription Renewals

Subscriptions Renewing in the Next 7 Days

The billing team wants a list of subscriptions due to renew within the next week, to trigger reminder emails.

SQL Server
SELECT customer, plan, renewal_date
FROM subscriptions
WHERE renewal_date BETWEEN @today AND DATEADD(day, 7, @today);
#9Product Warranty Tracking

Products Still Under Warranty

Support wants to check whether a product's 1-year warranty is still active based on its purchase date.

SQL Server
SELECT oi.order_id, p.name AS product_name, o.order_date,
       DATEADD(year, 1, o.order_date) AS warranty_expiry,
       CASE WHEN @today <= DATEADD(year, 1, o.order_date) THEN 'Active' ELSE 'Expired' END AS warranty_status
FROM order_items oi
JOIN orders o   ON o.id = oi.order_id
JOIN products p ON p.id = oi.product_id;
#10Inventory Aging

Inventory Aging Report

Warehouse management wants to see how many days each batch of stock has been sitting in inventory.

SQL Server
SELECT batch_id, received_date,
       DATEDIFF(day, received_date, @today) AS days_in_stock
FROM inventory_batches
ORDER BY days_in_stock DESC;
#11Employee Tenure

Employee Tenure Report

HR wants every employee's tenure with the company expressed in full years.

SQL Server
SELECT first_name, last_name, hire_date,
       DATEDIFF(year, hire_date, @today)
       - CASE WHEN DATEADD(year, DATEDIFF(year, hire_date, @today), hire_date) > @today
              THEN 1 ELSE 0 END AS tenure_years
FROM employees;
-- DATEDIFF(year, …) counts year boundaries, so subtract 1 until the anniversary has passed
#12Payroll Reporting

Employees Due for a Payroll Milestone Bonus

Payroll wants to flag employees who are hitting exactly 5 years of service this month.

SQL Server
SELECT first_name, last_name, hire_date
FROM employees
WHERE YEAR(DATEADD(year, 5, hire_date)) = YEAR(@today)
  AND MONTH(DATEADD(year, 5, hire_date)) = MONTH(@today);
#13CRM Analysis

Customers Inactive for 90+ Days

A CRM analyst wants to flag customers whose last order was more than 90 days ago as at-risk.

SQL Server
SELECT user_id, MAX(order_date) AS last_order_date,
       DATEDIFF(day, MAX(order_date), @today) AS days_since_last_order
FROM orders
GROUP BY user_id
HAVING DATEDIFF(day, MAX(order_date), @today) > 90;
#14Marketing Campaign Analysis

Orders Placed During a Campaign Window

Marketing wants every order placed during a campaign that ran from 15 October to 5 November 2024.

SQL Server
SELECT id, user_id, order_date
FROM orders
WHERE order_date BETWEEN '2024-10-15' AND '2024-11-05';
#15Banking Transactions

Transactions in the Current Financial Year

A bank's finance team wants every transaction within the current Indian financial year (April 1 - March 31).

SQL Server
SELECT transaction_id, transaction_date, amount
FROM transactions
WHERE transaction_date >= '2026-04-01' AND transaction_date < '2027-04-01';
#16Insurance Claims

Claims Filed Within 30 Days of Policy Start

An insurer wants to flag claims that were filed suspiciously soon after the policy began.

SQL Server
SELECT claim_id, policy_start_date, claim_date,
       DATEDIFF(day, policy_start_date, claim_date) AS days_since_policy_start
FROM claims
WHERE DATEDIFF(day, policy_start_date, claim_date) <= 30;
#17Hospital Appointments

Upcoming Appointments This Week

A hospital's front desk wants every appointment scheduled in the next 7 days.

SQL Server
SELECT patient_name, doctor_name, appointment_date
FROM appointments
WHERE appointment_date BETWEEN @today AND DATEADD(day, 7, @today);
#18E-Commerce Reporting

Orders Delivered Late

Operations wants every order where the delivery took longer than 7 days.

SQL Server
SELECT order_id, order_date, delivery_date,
       DATEDIFF(day, order_date, delivery_date) AS delivery_days
FROM deliveries
WHERE DATEDIFF(day, order_date, delivery_date) > 7;
#19Revenue Forecasting

Trailing 12-Month Revenue for Forecasting

An analyst wants total revenue from the trailing 12 months, up to today, to feed a simple forecasting model.

SQL Server
SELECT SUM(amount) AS trailing_12mo_revenue
FROM sales
WHERE sale_date >= DATEADD(month, -12, @today);
#20Executive Dashboards

Current Month-to-Date Revenue Tile

An executive dashboard tile needs revenue for the current month so far, updating daily.

SQL Server
SELECT SUM(amount) AS mtd_revenue
FROM sales
WHERE YEAR(sale_date) = YEAR(@today)
  AND MONTH(sale_date) = MONTH(@today);
#21KPI Reporting

Days Remaining in the Current Month

A KPI dashboard shows how many days are left in the current month to hit target.

SQL Server
SELECT DATEDIFF(day, @today, EOMONTH(@today)) AS days_remaining_this_month;
#22Product Analysis

Newest Products Launched This Quarter

Product management wants every product launched in the current quarter, from a product_launches table.

SQL Server
SELECT product_name, launch_date
FROM product_launches
WHERE YEAR(launch_date) = YEAR(@today)
  AND DATEPART(quarter, launch_date) = DATEPART(quarter, @today);
#23Store Performance

Store Sales for the Last Complete Month

Regional managers want each store's total sales for last month, not the current in-progress month.

SQL Server
SELECT store_id, SUM(amount) AS last_month_sales
FROM sales
WHERE sale_date >= DATEADD(month, -1, DATEFROMPARTS(YEAR(@today), MONTH(@today), 1))
  AND sale_date <  DATEFROMPARTS(YEAR(@today), MONTH(@today), 1)
GROUP BY store_id;
#24Branch Performance

Branch Revenue Growth This Year vs Last

Branch performance needs this year's revenue compared to the same period last year.

SQL Server
SELECT branch_id,
  SUM(CASE WHEN YEAR(sale_date) = YEAR(@today)
            AND sale_date <= @today THEN amount ELSE 0 END) AS this_year,
  SUM(CASE WHEN YEAR(sale_date) = YEAR(@today) - 1
            AND sale_date <= DATEADD(year, -1, @today) THEN amount ELSE 0 END) AS last_year
FROM sales
GROUP BY branch_id;
#25Franchise Reporting

Franchise Locations Opened in the Last 2 Years

Franchise operations wants every location opened within the last 24 months.

SQL Server
SELECT location_name, opened_date
FROM franchise_locations
WHERE opened_date >= DATEADD(month, -24, @today);
#26Supply Chain Analytics

Average Supplier Lead Time

Procurement wants the average number of days between placing a purchase order and receiving the goods.

SQL Server
SELECT supplier_id, AVG(1.0 * DATEDIFF(day, po_date, received_date)) AS avg_lead_time_days
FROM purchase_orders
GROUP BY supplier_id;
#27Delivery Tracking

Open Orders Older Than a Week

Logistics wants every order that is still pending or processing, has no shipped date and was placed more than 7 days ago.

SQL Server
SELECT id, user_id, status, order_date
FROM orders
WHERE shipped_date IS NULL
  AND status IN ('pending', 'processing')
  AND order_date < DATEADD(day, -7, @today);
#28Customer Lifetime Value

Customer Lifespan in Days

An analyst wants each customer's active lifespan, from first order to most recent order.

SQL Server
SELECT user_id,
       DATEDIFF(day, MIN(order_date), MAX(order_date)) AS lifespan_days,
       SUM(total) AS lifetime_value
FROM orders
GROUP BY user_id;
#29Cohort Analysis

Monthly Signup Cohorts

Growth wants customers grouped into cohorts based on the month they first signed up.

SQL Server
SELECT YEAR(first_order_date) AS cohort_year, MONTH(first_order_date) AS cohort_month,
       COUNT(DISTINCT user_id) AS cohort_size
FROM (SELECT user_id, MIN(order_date) AS first_order_date FROM orders GROUP BY user_id) t
GROUP BY YEAR(first_order_date), MONTH(first_order_date);
#30User Activity Analysis

Daily Active Users

A SaaS product team wants a count of distinct users active on each calendar day.

SQL Server
SELECT activity_date, COUNT(DISTINCT user_id) AS daily_active_users
FROM user_activity_log
GROUP BY activity_date
ORDER BY activity_date;
#31Financial Reporting

Month-End Close Snapshot

Finance wants account balances exactly as of the last day of last month.

SQL Server
SELECT account_id, balance
FROM account_snapshots
WHERE snapshot_date = EOMONTH(@today, -1);
#32Budget Analysis

Spend vs Budget for the Current Quarter

FP&A wants total spend so far in the current quarter, to compare against the quarterly budget.

SQL Server
SELECT SUM(amount) AS qtd_spend
FROM expenses
WHERE YEAR(expense_date) = YEAR(@today)
  AND DATEPART(quarter, expense_date) = DATEPART(quarter, @today);
#33Cash Flow Reporting

Upcoming Payables Due This Week

Treasury wants every payable due within the next 7 days for cash flow planning.

SQL Server
SELECT vendor_name, invoice_id, due_date, amount
FROM payables
WHERE due_date BETWEEN @today AND DATEADD(day, 7, @today);
#34Order Fulfillment Analysis

Average Order-to-Fulfillment Time by Month

Operations wants the average number of days from order to delivery, tracked month by month.

SQL Server
SELECT YEAR(order_date) AS yr, MONTH(order_date) AS mo,
       AVG(1.0 * DATEDIFF(day, order_date, delivery_date)) AS avg_fulfillment_days
FROM deliveries
GROUP BY YEAR(order_date), MONTH(order_date);
#35SaaS Subscription Analytics

Monthly Recurring Revenue by Renewal Month

A SaaS finance analyst wants MRR grouped by the month subscriptions are set to renew.

SQL Server
SELECT YEAR(renewal_date) AS yr, MONTH(renewal_date) AS mo, COUNT(*) AS renewals_due,
       SUM(monthly_price) AS mrr_at_risk
FROM subscriptions
GROUP BY YEAR(renewal_date), MONTH(renewal_date);

Why Do We Need Date Functions?

Without date functions

Manual calculations in a spreadsheet, exporting raw dates and subtracting by hand, hours of copy-paste work for a report that changes every day.

With date functions

One reusable query returns live monthly revenue, retention, and trend data — refreshed automatically every time it runs.

Easy reporting

Monthly, quarterly, and yearly rollups become a GROUP BY away instead of a manual export.

Trend analysis

Month-over-month and year-over-year growth, rolling averages, and seasonality all depend on date arithmetic.

Business intelligence

Every BI dashboard — Power BI, Tableau, Looker — is built on date functions running underneath the visuals.

Business example: "How many customers who signed up in January are still active six months later?" cannot be answered by eyeballing a spreadsheet at scale — it needs a date difference and date arithmetic working together across thousands of rows, instantly.

Date Functions for Data Analysts

Where date arithmetic and date differences show up in day-to-day BI work.

Power BI

DAX time-intelligence functions such as SAMEPERIODLASTYEAR and DATEADD mirror the date arithmetic in this lesson.

Tableau

DATEDIFF and DATEADD calculated fields in Tableau take the same arguments and follow the same logic as SQL Server.

Excel Power Query

"Date.AddDays" and "Duration.Days" are Power Query's equivalents of adding days and measuring a date difference.

Dashboard development

Every "Last 30 Days" filter, month-to-date tile and trend line on a dashboard is powered by a date query underneath.

Time series analysis

Forecasting and trend detection are built on date-bucketed aggregates — the group-by-year-and-month pattern from this lesson.

Revenue analysis

Monthly, quarterly and annual revenue rollups are the most requested date reports in any analyst role.

Customer retention analysis

The difference between signup and last activity is the core metric behind retention and churn dashboards.

Cohort analysis

Grouping customers by the year and month of their first order is the foundation of every cohort retention curve.

KPI reporting

MTD, QTD and YTD tiles all filter on date ranges built from the current date.

Executive reporting

Board decks lead with YoY and MoM growth numbers — both are date comparisons dressed up as headlines.

Common Date Function Mistakes

7 mistakes that trip up almost every SQL learner working with dates — the wrong version, the fix, and why it matters.

#1

Wrong Date Formats (Ambiguous DD-MM vs MM-DD)

✘ Wrong
Any database
SELECT * FROM orders
WHERE order_date = '03-04-2026';
-- is this March 4th or April 3rd? Ambiguous without knowing the format
✔ Fix
Any database
SELECT * FROM orders
WHERE order_date = '2026-04-03'; -- YYYY-MM-DD is unambiguous
💡 DD-MM-YYYY and MM-DD-YYYY formats are easy to confuse and silently produce the wrong date. The ISO 8601 format YYYY-MM-DD is unambiguous across every SQL engine and locale, and should be the standard for stored dates and literals.
#2

Comparing Dates as Badly Formatted Text

✘ Wrong
SQLite
SELECT COUNT(*) FROM orders
WHERE order_date > '2024-3-15';
-- returns 0 rows in the Playground: '2024-3-15' is not zero-padded,
-- so the text comparison sorts it after every real date in 2024
✔ Fix
SQLite
SELECT COUNT(*) FROM orders
WHERE order_date > '2024-03-15';
-- returns 47: zero-padded YYYY-MM-DD text sorts chronologically
💡 SQLite stores dates as text, and many real-world tables keep dates in VARCHAR columns too. Text is compared character by character, so a date literal only compares correctly when it uses the same zero-padded YYYY-MM-DD format as the column. In databases with a real DATE type, compare against a properly typed date value.
#3

Time Zone Problems With "Today"

✘ Wrong
SQLite
SELECT * FROM orders
WHERE order_date = date('now');
-- date('now') is the current date in UTC, which can be
-- yesterday or tomorrow in the business's local time zone
✔ Fix
SQLite
SELECT * FROM orders
WHERE order_date = date('now', 'localtime');
-- convert to local time before taking the date
💡 Every "current date" function resolves in some time zone: SQLite's date('now') and CURRENT_DATE use UTC, while NOW() and GETDATE() in MySQL, PostgreSQL and SQL Server use the server or session time zone. A report run near midnight can land on the wrong calendar day unless you convert explicitly — for example with the 'localtime' modifier in SQLite or AT TIME ZONE in PostgreSQL and SQL Server.
#4

DATEDIFF Parameter Order Issues

✘ Wrong
SQL Server
SELECT DATEDIFF(day, delivery_date, order_date) AS delivery_days
FROM deliveries;
-- arguments reversed — produces a negative delivery time
✔ Fix
SQL Server
SELECT DATEDIFF(day, order_date, delivery_date) AS delivery_days
FROM deliveries;
💡 DATEDIFF is directional — DATEDIFF(unit, start, end) computes end minus start. Reversing the two date arguments flips the sign of the result, turning a correct "7 days" into a confusing "-7 days".
#5

DATEADD Logic Errors (Adding Instead of Subtracting)

✘ Wrong
SQL Server
SELECT DATEADD(day, 30, GETDATE()) AS orders_last_30_days;
-- meant to look BACK 30 days, but added 30 days INTO THE FUTURE instead
✔ Fix
SQL Server
SELECT DATEADD(day, -30, GETDATE()) AS thirty_days_ago;
💡 DATEADD with a positive number moves a date forward in time; to look backward (as in "orders from the last 30 days"), the interval must be negative. Forgetting the negative sign is one of the most common DATEADD mistakes.
#6

Incorrect Grouping by Date (Losing the Year)

✘ Wrong
MySQL / SQL Server
SELECT MONTH(order_date) AS mo, SUM(total) AS revenue
FROM orders
GROUP BY MONTH(order_date);
-- merges January 2022, 2023 and 2024 into a single "month 1" row
✔ Fix
MySQL / SQL Server
SELECT YEAR(order_date) AS yr, MONTH(order_date) AS mo, SUM(total) AS revenue
FROM orders
GROUP BY YEAR(order_date), MONTH(order_date);
💡 Grouping by MONTH() alone ignores which year each row belongs to. For any data spanning more than one calendar year, YEAR() must also be included in the GROUP BY, or January of every year gets silently combined into one bucket.
#7

Poor Reporting Logic — Using a Function-Wrapped Filter Instead of a Range

✘ Wrong
MySQL / SQL Server
SELECT * FROM sales
WHERE YEAR(sale_date) = 2026;
-- correct result, but cannot use a standard index on sale_date
✔ Fix
MySQL / SQL Server
SELECT * FROM sales
WHERE sale_date >= '2026-01-01' AND sale_date < '2027-01-01';
💡 Both queries return identical rows, but wrapping the column in YEAR() typically disables a plain B-tree index, forcing a full scan on large tables. A raw-column range filter returns the same rows while remaining fully index-friendly — see the Performance Tips section for details.

SQL Date Functions by Dialect

SQLite vs MySQL vs PostgreSQL vs SQL Server — exactly where the syntax diverges.

Date function syntax in SQLite, MySQL, SQL Server and PostgreSQL
TaskSQLiteMySQLSQL ServerPostgreSQL
Current datedate('now')CURDATE()CAST(GETDATE() AS date)CURRENT_DATE
Current timestampdatetime('now')NOW()GETDATE()NOW() / CURRENT_TIMESTAMP
Date difference (days)julianday(b) - julianday(a)DATEDIFF(b, a)DATEDIFF(day, a, b)b - a
Date adddate(d, '+n days')DATE_ADD(d, INTERVAL n DAY)DATEADD(day, n, d)d + INTERVAL 'n days'
Extract yearstrftime('%Y', d)YEAR(d)YEAR(d)EXTRACT(YEAR FROM d)
Extract monthstrftime('%m', d)MONTH(d)MONTH(d)EXTRACT(MONTH FROM d)
Format a datestrftime(fmt, d)DATE_FORMAT(d, fmt)FORMAT(d, fmt)TO_CHAR(d, fmt)
Last day of monthdate(d, 'start of month', '+1 month', '-1 day')LAST_DAY(d)EOMONTH(d)(DATE_TRUNC('month', d) + INTERVAL '1 month - 1 day')::date

The same task — a renewal date 30 days after an order — in each database:

SQLite · runs in the Playground
SELECT date(order_date, '+30 days') AS renewal
FROM orders;

▶ Run this query in the SQL Playground

MySQL
SELECT DATE_ADD(order_date, INTERVAL 30 DAY) AS renewal
FROM orders;
SQL Server
SELECT DATEADD(day, 30, order_date) AS renewal
FROM orders;
PostgreSQL
SELECT order_date + INTERVAL '30 days' AS renewal
FROM orders;
💡 The concepts transfer across every SQL dialect — only the function name and argument order change. Oracle uses SYSDATE, ADD_MONTHS(), EXTRACT() and plain date subtraction for days.

Performance Tips

Keeping date-heavy queries fast, even at millions of rows.

Avoid wrapping filtered date columns in functions

WHERE YEAR(order_date) = 2026 usually prevents the database from using a plain index on order_date, forcing a full scan.

Use range filters instead

WHERE order_date >= '2026-01-01' AND order_date < '2027-01-01' returns identical rows while staying index-friendly.

Index the raw date column

A standard B-tree index on order_date helps range-filtered queries, not function-wrapped ones.

Partitioning

Partitioning a large fact table by month or year lets the database skip irrelevant partitions for range-filtered date queries.

Measure before optimising

Never assume a date-heavy query is slow — confirm with the query plan and real timings first.

Checking the Query Plan

SQLite
EXPLAIN QUERY PLAN
SELECT *
FROM orders
WHERE order_date >= '2024-01-01';

A query plan shows whether the database can jump straight to matching rows through an index (a search or index range scan) or must read every row (a scan). MySQL and PostgreSQL use EXPLAIN; SQL Server shows an execution plan. The Playground's small tables have no index on order_date, so both forms of the filter scan there — the difference appears on large, indexed tables, and it grows with table size.

Date Functions Cheat Sheet

Quick revision before an interview — and the format codes you will reuse constantly.

📋 Quick revision — date functions

Get todayCURRENT_DATE · date('now')
Get date + timeNOW() · GETDATE()
Extract a partYEAR() · EXTRACT() · strftime()
Shift a dateDATEADD() · DATE_ADD() · date(d, '+n days')
Measure a gapDATEDIFF() · julianday() difference
Index-friendly filterRange on the raw column
Monthly reportGROUP BY year and month
Calculate ageFull years + birthday check

SQLite strftime() Format Codes

SQLite strftime format codes
CodeMeaningResult for '2024-12-24 14:30:25'
%YFour-digit year2024
%mMonth, 01–1212
%dDay of month, 01–3124
%HHour, 00–2314
%MMinute, 00–5930
%SSecond, 00–5925
%wDay of week, 0 (Sunday) – 62
%jDay of year, 001–366359

MySQL's DATE_FORMAT() uses similar percent codes; PostgreSQL's TO_CHAR() and SQL Server's FORMAT() use letter patterns such as YYYY-MM-DD and yyyy-MM-dd.

Practice SQL Date Queries

Learn → Practice — 8 exercises, easy to challenging, on the real Playground tables.

Every exercise uses a table and columns that exist in the SQLabHub SQL Playground, and every solution has been run against that database. Write your own query first, run it, then compare.

  1. 1Easy For every employee, show the name, the hire date, and the hire year and hire month as separate columns. Sort by hire date. Table: employees

    Show solution
    SQLite · runs in the Playground
    SELECT first_name, last_name, hire_date,
           strftime('%Y', hire_date) AS hire_year,
           strftime('%m', hire_date) AS hire_month
    FROM employees
    ORDER BY hire_date;

    ▶ Run this query in the SQL Playground

    strftime() formats a date using a pattern: %Y is the four-digit year and %m the two-digit month. The first row is Steven Nguyen, hired 2015-01-14 → 2015, 01.

  2. 2Easy List every order placed in March 2024, oldest first. Table: orders

    Show solution
    SQLite · runs in the Playground
    SELECT id, order_date, total
    FROM orders
    WHERE order_date >= '2024-03-01'
      AND order_date <  '2024-04-01'
    ORDER BY order_date;

    ▶ Run this query in the SQL Playground

    A half-open range (from the 1st, up to but not including the 1st of the next month) is the safest way to select a month. It returns 6 orders.

  3. 3Medium Each order can be returned within 30 days. Show each order with its return deadline, newest orders first. Table: orders

    Show solution
    SQLite · runs in the Playground
    SELECT id, order_date,
           date(order_date, '+30 days') AS return_deadline
    FROM orders
    ORDER BY order_date DESC;

    ▶ Run this query in the SQL Playground

    date() accepts modifiers that shift the value. The newest order (2024-12-24) gets a deadline of 2025-01-23 — the function rolls over the month and year for you.

  4. 4Medium Build a monthly revenue report for 2023: one row per month with the number of orders and total revenue. Table: orders

    Show solution
    SQLite · runs in the Playground
    SELECT strftime('%Y-%m', order_date) AS month,
           COUNT(*)              AS orders,
           ROUND(SUM(total), 2)  AS revenue
    FROM orders
    WHERE order_date >= '2023-01-01'
      AND order_date <  '2024-01-01'
    GROUP BY strftime('%Y-%m', order_date)
    ORDER BY month;

    ▶ Run this query in the SQL Playground

    Grouping by '%Y-%m' keeps the year and month together, so months from different years can never merge. You should get 12 rows, starting with 2023-01 (3 orders, 10518.82).

  5. 5Medium Find the five longest-serving employees and their tenure in years (one decimal) as of 1 January 2025. Table: employees

    Show solution
    SQLite · runs in the Playground
    SELECT first_name, last_name, hire_date,
           ROUND((julianday('2025-01-01') - julianday(hire_date)) / 365.25, 1) AS tenure_years
    FROM employees
    ORDER BY hire_date
    LIMIT 5;

    ▶ Run this query in the SQL Playground

    julianday() turns a date into a day number, so subtracting two of them gives the days between the dates. Dividing by 365.25 converts days to years. Steven Nguyen comes first with 10 years.

  6. 6Medium Which day of the week gets the most orders? Show each weekday name with its order count, busiest first. Table: orders

    Show solution
    SQLite · runs in the Playground
    SELECT CASE strftime('%w', order_date)
             WHEN '0' THEN 'Sunday'    WHEN '1' THEN 'Monday'
             WHEN '2' THEN 'Tuesday'   WHEN '3' THEN 'Wednesday'
             WHEN '4' THEN 'Thursday'  WHEN '5' THEN 'Friday'
             ELSE 'Saturday' END AS weekday,
           COUNT(*) AS orders
    FROM orders
    GROUP BY strftime('%w', order_date)
    ORDER BY orders DESC;

    ▶ Run this query in the SQL Playground

    %w returns the day of the week as text from '0' (Sunday) to '6' (Saturday), and CASE WHEN turns it into a name. Friday is the busiest day with 37 orders.

  7. 7Challenging The newest order in the table is dated 2024-12-24. Treating that as “today”, find the customers (user_id) whose last order was more than 365 days earlier, most inactive first. Table: orders

    Show solution
    SQLite · runs in the Playground
    SELECT user_id,
           MAX(order_date) AS last_order,
           CAST(julianday('2024-12-24') - julianday(MAX(order_date)) AS INTEGER) AS days_inactive
    FROM orders
    GROUP BY user_id
    HAVING days_inactive > 365
    ORDER BY days_inactive DESC;

    ▶ Run this query in the SQL Playground

    MAX(order_date) finds each customer’s latest order, julianday() measures the gap, and HAVING filters on the aggregated result. 57 customers qualify; the most inactive is user 15 (last order 2022-01-15, 1074 days).

  8. 8Challenging For each month of 2024, show revenue, the previous month’s revenue, and the month-over-month change as a percentage. Table: orders

    Show solution
    SQLite · runs in the Playground
    SELECT month, revenue,
           LAG(revenue) OVER (ORDER BY month) AS prev_revenue,
           ROUND((revenue - LAG(revenue) OVER (ORDER BY month)) * 100.0
                 / LAG(revenue) OVER (ORDER BY month), 1) AS mom_change_pct
    FROM (
      SELECT strftime('%Y-%m', order_date) AS month,
             ROUND(SUM(total), 2) AS revenue
      FROM orders
      WHERE order_date >= '2024-01-01' AND order_date < '2025-01-01'
      GROUP BY strftime('%Y-%m', order_date)
    ) AS monthly
    ORDER BY month;

    ▶ Run this query in the SQL Playground

    The inner query builds monthly totals; LAG() then reads the previous row’s revenue. January has no previous month, so its change is NULL; February is +4.2% (16998.61 vs 16318.57).

📝 Quiz — SQL Date Functions

30 questions · instant feedback · 0/30 answered

Question 1 of 30

What is the primary purpose of SQL Date Functions?

Question 2 of 30

What does CURRENT_DATE return?

Question 3 of 30

What is the key difference between CURRENT_DATE and NOW()?

Question 4 of 30

What does YEAR(order_date) return for an order placed on 2026-03-02?

Question 5 of 30

What does MONTH(order_date) return for an order placed on 2026-03-02?

Question 6 of 30

What does DAY(order_date) return for an order placed on 2026-03-02?

Question 7 of 30

In SQL Server, what does DATEADD(day, 30, order_date) calculate?

Question 8 of 30

In SQL Server, what does DATEDIFF(day, start_date, end_date) calculate?

Question 9 of 30

In MySQL's DATEDIFF(end_date, start_date), what happens if the argument order is reversed?

Question 10 of 30

What does EXTRACT(YEAR FROM order_date) do?

Question 11 of 30

Which engines commonly support EXTRACT()?

Question 12 of 30

Why can WHERE YEAR(order_date) = 2026 be slow on a large orders table?

Question 13 of 30

What is the recommended index-friendly rewrite of WHERE YEAR(order_date) = 2026?

Question 14 of 30

What does SELECT MONTH(order_date), SUM(sales_amount) FROM sales GROUP BY MONTH(order_date); produce?

Question 15 of 30

Why should YEAR() often be added alongside MONTH() in a GROUP BY for multi-year data?

Question 16 of 30

How would you typically calculate a person's age in SQL?

Question 17 of 30

Why is a naive YEAR(CURRENT_DATE) - YEAR(date_of_birth) age calculation sometimes wrong?

Question 18 of 30

What does 'first day of the month' typically mean in a SQL reporting query?

Question 19 of 30

What is Month-over-Month (MoM) analysis?

Question 20 of 30

What is Year-over-Year (YoY) analysis?

Question 21 of 30

What is Rolling 30-Day analysis used for?

Question 22 of 30

Which of these is a common SQL date data cleaning problem?

Question 23 of 30

What is a practical reason to standardize date formats before analysis?

Question 24 of 30

What does SELECT YEAR(order_date), SUM(sales_amount) FROM sales GROUP BY YEAR(order_date); return?

Question 25 of 30

Why is DATEDIFF considered a major SQL interview topic?

Question 26 of 30

Which function returns the current date and time in SQL Server?

Question 27 of 30

How does PostgreSQL typically add 7 days to a date?

Question 28 of 30

Why are Date Functions considered essential for Data Analysts?

Question 29 of 30

What does an EXPLAIN plan typically reveal about WHERE YEAR(order_date) = 2026 versus a range filter?

Question 30 of 30

In SQLite (the SQLabHub Playground), which expression adds 30 days to order_date?

SQL Date Functions Interview Questions

50 questions grouped by level — select any question to reveal the answer.

Syntax in these answers: where an answer names DATEADD or DATEDIFF(unit, start, end), it is SQL Server syntax, with GETDATE() as the current date and time. Interviewers usually accept any dialect as long as you say which one you are writing. More topics are covered in the SQL interview questions guide.

Beginner

Q1What are Date Functions in SQL?
Date functions are built-in SQL functions that operate on DATE, DATETIME, and TIMESTAMP data — extracting parts of a date (YEAR, MONTH, DAY), performing date arithmetic (DATEADD, DATEDIFF), and returning the current date/time (CURRENT_DATE, NOW).
Q2What is the difference between DATE and DATETIME?
DATE stores only a calendar date (e.g. 2026-07-12) with no time component, while DATETIME (or TIMESTAMP) stores both the date and a time-of-day component (e.g. 2026-07-12 14:30:25).
Q3What does DATEDIFF do?
DATEDIFF() calculates the difference between two dates, most commonly in days, for example DATEDIFF(day, order_date, delivery_date) to measure how long a delivery took.
Q4What does DATEADD do?
DATEADD() adds (or, with a negative number, subtracts) a specified time interval to a date — DATEADD(day, 30, order_date) computes the date 30 days after order_date.
Q5How do you calculate age in SQL?
A typical approach uses DATEDIFF between date_of_birth and GETDATE(), converting days to years (dividing by ~365.25), or DATEDIFF(year, ...) adjusted with a CASE WHEN check on whether the birthday has occurred yet this year.
Q6How do you extract the month from a date?
Use MONTH(date_column) in MySQL/SQL Server, or EXTRACT(MONTH FROM date_column) in PostgreSQL/Oracle/ANSI SQL — both return the numeric month (1-12).
Q7What is the difference between CURRENT_DATE and NOW()?
CURRENT_DATE returns just today's calendar date with no time component, while NOW() returns the full current date and time, down to the second.
Q8What does YEAR() return?
YEAR(date_column) extracts the four-digit calendar year portion of a date value, e.g. YEAR('2026-07-12') returns 2026.
Q9What does DAY() return?
DAY(date_column) extracts the day-of-month portion of a date (1-31), e.g. DAY('2026-07-12') returns 12.
Q10What is EXTRACT() used for?
EXTRACT(part FROM date_column) is the ANSI-SQL-standard function for pulling out a specific component of a date or time — YEAR, MONTH, DAY, HOUR, and more — commonly used in PostgreSQL and Oracle.
Q11What data types store dates in SQL?
The main types are DATE (calendar date only), DATETIME/TIMESTAMP (date plus time), and TIME (time of day only, no date).
Q12Can date functions be used inside a WHERE clause?
Yes — for example WHERE YEAR(order_date) = 2026 filters rows by year, though a raw-column range filter is usually preferred for performance reasons covered later in this lesson.
Q13What does CURRENT_TIMESTAMP return?
CURRENT_TIMESTAMP (an ANSI-standard alternative to NOW()) returns the current date and time together, including hours, minutes, and seconds.
Q14Why would a business need Date Functions?
Businesses constantly need to answer time-based questions — monthly revenue, customer tenure, delivery delays, subscription renewals — and date functions are the SQL toolkit for calculating and reporting on all of them.
Q15What does DATEADD(month, 1, hire_date) calculate?
It calculates the date exactly one calendar month after hire_date — commonly used for probation periods, billing cycles, or renewal dates.
Q16Is TIME the same as DATETIME?
No — TIME stores only a time-of-day value (e.g. 14:30:25) with no date at all, while DATETIME stores both a date and a time together.
Q17What is the simplest way to get "today" in a query?
CURRENT_DATE returns today's date in PostgreSQL, MySQL and SQLite. MySQL also has CURDATE(), SQLite has date('now'), and SQL Server uses CAST(GETDATE() AS date).
Q18Can you subtract one date from another directly?
In some engines like PostgreSQL, subtracting one DATE from another (end_date - start_date) directly returns the number of days between them; other engines require DATEDIFF() instead.

Intermediate

Q19How would you calculate the number of days between two dates?
SELECT DATEDIFF(day, order_date, delivery_date) AS days_taken FROM deliveries; — DATEDIFF(unit, start, end) computes end minus start in the given unit.
Q20How would you find all orders placed in the current year?
WHERE YEAR(order_date) = YEAR(CURDATE()) works in MySQL but is not index-friendly. The preferred rewrite is a range that starts on the first day of the year: order_date >= DATE_TRUNC('year', CURRENT_DATE) in PostgreSQL, or order_date >= date('now', 'start of year') in SQLite.
Q21How would you calculate a subscription's renewal date, 6 months after start?
SELECT DATEADD(month, 6, start_date) AS renewal_date FROM subscriptions;
Q22How would you find employees hired more than 5 years ago?
SELECT * FROM employees WHERE hire_date <= DATEADD(year, -5, GETDATE()); — comparing hire_date against "5 years before today."
Q23How would you build a monthly sales report grouped by year and month?
SELECT YEAR(sale_date) AS yr, MONTH(sale_date) AS mo, SUM(amount) AS total FROM sales GROUP BY YEAR(sale_date), MONTH(sale_date); — including YEAR() avoids merging the same month across different years.
Q24What is the difference between EXTRACT(YEAR FROM date) and YEAR(date)?
They return the same value — EXTRACT() is the ANSI-SQL-standard syntax available in PostgreSQL, Oracle, and MySQL, while YEAR() is a shorthand function available in MySQL and SQL Server (SQL Server does not support EXTRACT the same way).
Q25How would you calculate an employee's age given their date_of_birth?
SELECT DATEDIFF(day, date_of_birth, GETDATE()) / 365.25 AS approx_age FROM employees; is a quick approximation; a precise version adjusts using a CASE WHEN check on whether this year's birthday has passed yet.
Q26How would you find the first and last day of the current month?
The first day is typically computed as DATEADD(day, 1 - DAY(GETDATE()), GETDATE()); the last day is one day before adding a month to that first-day value.
Q27How would you calculate Month-over-Month revenue growth?
Compute revenue per month with GROUP BY YEAR/MONTH, then compare each month's total to the previous month's total (via a self-join, window function LAG(), or subquery) to get a percentage change.
Q28How would you calculate Year-over-Year revenue growth for July?
Compare SUM(amount) WHERE MONTH(sale_date) = 7 AND YEAR(sale_date) = 2026 against the same filter with YEAR(sale_date) = 2025, then compute the percentage difference between the two totals.
Q29How would you calculate a rolling 30-day sales total as of today?
SELECT SUM(amount) AS rolling_30_day_sales FROM sales WHERE sale_date >= DATEADD(day, -30, GETDATE()); — a continuously moving window rather than a fixed calendar month.
Q30How would you find customers who have not ordered in over 90 days?
SELECT user_id, MAX(order_date) AS last_order FROM orders GROUP BY user_id HAVING DATEDIFF(day, MAX(order_date), GETDATE()) > 90;
Q31What does DATE_TRUNC do, and which engines support it?
DATE_TRUNC('month', date_column) in PostgreSQL truncates a date or timestamp down to the start of the given unit (day, month, year and so on). SQL Server 2022 added DATETRUNC(month, date_column), and SQLite uses date(date_column, 'start of month').
Q32How would you standardize a date stored as inconsistent text like "12-07-2026"?
Explicitly parse it with the correct format using the engine's date-parsing function (e.g. STR_TO_DATE in MySQL, TO_DATE in Oracle/PostgreSQL with an explicit format mask) rather than relying on implicit conversion, which can silently misinterpret ambiguous formats.
Q33How would you find subscriptions renewing in the next 7 days?
SELECT * FROM subscriptions WHERE renewal_date BETWEEN CAST(GETDATE() AS date) AND DATEADD(day, 7, CAST(GETDATE() AS date)); — cast to a date first: GETDATE() includes the time of day, so comparing a date column with it would skip renewals due today.
Q34How would you calculate average delivery time per month?
SELECT YEAR(order_date) AS yr, MONTH(order_date) AS mo, AVG(1.0 * DATEDIFF(day, order_date, delivery_date)) AS avg_days FROM deliveries GROUP BY YEAR(order_date), MONTH(order_date); — multiplying by 1.0 stops SQL Server from averaging whole numbers and truncating the result.

Advanced

Q35Why can WHERE YEAR(order_date) = 2026 hurt performance on a large table?
Applying a function to the filtered column typically prevents the database from using a standard B-tree index on that column, since the index stores raw date values, not the YEAR()-transformed ones — this often forces a full table scan instead of an index range scan.
Q36How can you keep year-based filtering fast on a large table?
Rewrite the filter as a range directly on the raw column: WHERE order_date >= '2026-01-01' AND order_date < '2027-01-01' — this returns identical rows while remaining fully index-friendly, since no function wraps the indexed column.
Q37How would you design a query to detect Month-over-Month growth exceeding 20%?
Compute monthly totals with GROUP BY YEAR/MONTH, use a window function like LAG() (or a self-join on the previous month) to pull the prior month's total alongside the current one, then filter WHERE (current - previous) / previous > 0.20.
Q38How would you correctly calculate age accounting for whether the birthday has passed this year?
In SQL Server: DATEDIFF(year, date_of_birth, GETDATE()) - CASE WHEN DATEADD(year, DATEDIFF(year, date_of_birth, GETDATE()), date_of_birth) > GETDATE() THEN 1 ELSE 0 END. DATEDIFF(year, ...) only counts year boundaries, so the CASE subtracts 1 when this year's birthday has not happened yet. MySQL does the same in one call with TIMESTAMPDIFF(YEAR, date_of_birth, CURDATE()).
Q39What is the risk of comparing dates stored as VARCHAR instead of a real DATE type?
String comparisons on text-formatted dates sort and compare lexicographically, not chronologically — '2026-2-1' can sort incorrectly relative to '2026-10-1' as plain text, and ambiguous formats can silently misparse — a proper DATE/DATETIME column type avoids both problems entirely.
Q40How would you build a Rolling 30-Day trend line for a dashboard, one point per day?
For each day D in the reporting range, compute SUM(amount) WHERE sale_date BETWEEN DATEADD(day,-29,D) AND D — often implemented efficiently with a window function (SUM() OVER a 30-row/30-day RANGE frame ordered by date) rather than a correlated subquery per day.
Q41What is the interview significance of the Date Functions topic?
It tests whether a candidate can translate real business timing questions — tenure, delivery delay, retention, MoM/YoY growth — into correct SQL, while also probing awareness of indexing trade-offs, time zone handling, and cross-database syntax differences.
Q42How do time zones complicate NOW()/CURRENT_TIMESTAMP in a global business?
NOW() resolves in the database server's configured time zone, which may not match the time zone of the business user viewing a report; a sale logged at 11:45 PM local time could be attributed to the wrong calendar day if the server and the business operate in different zones without explicit conversion.
Q43How would you calculate Year-over-Year growth for a rolling 12-month period rather than a fixed calendar year?
Compare SUM(amount) WHERE sale_date >= DATEADD(month,-12,GETDATE()) (the trailing 12 months) against SUM(amount) WHERE sale_date BETWEEN DATEADD(month,-24,GETDATE()) AND DATEADD(month,-12,GETDATE()) (the 12 months before that), rather than comparing fixed Jan-Dec calendar years.
Q44Why might DATEDIFF(year, start_date, end_date) give a misleading result compared to DATEDIFF(day, ...)/365?
DATEDIFF(year, ...) in some engines simply subtracts calendar years (2026 - 2025 = 1) regardless of the month/day, which can overstate a duration that is really just a few days across a year boundary (e.g. Dec 31 to Jan 1) — day-based calculations avoid this distortion.
Q45How would you detect and flag invalid or impossible dates during data cleaning?
Attempt a strict parse/cast of the raw date value and flag rows that fail (e.g. '2026-02-30' or '2026-13-01'), or validate day-of-month against the specific month's maximum (accounting for leap years in February) before trusting the column for calculations.
Q46How would you build a cohort retention query showing what % of each signup month's customers were still active N months later?
Join each customer's first-order month (the cohort) to their subsequent order months, compute DATEDIFF(month, cohort_month, activity_month) as the "months since signup" offset, then aggregate the percentage of the original cohort still present at each offset.
Q47Why is understanding Date Functions considered foundational for a Data Analyst role and for Time Series Analysis?
Time series analysis, forecasting, and nearly every executive dashboard are built on a time axis; without solid command of YEAR/MONTH/DAY, DATEADD, DATEDIFF, and correct date-range filtering, an analyst cannot reliably build the monthly/yearly rollups, retention curves, or rolling trend lines that time series work depends on.
Q48How would you write a single query that reports both MTD and the prior month-to-date figure for a like-for-like comparison?
Filter this month's data WHERE sale_date BETWEEN month_start AND GETDATE(), and separately filter the prior month's data WHERE sale_date BETWEEN prior_month_start AND DATEADD(month,-1,GETDATE()) — matching the same day-of-month cutoff in both periods avoids comparing a full month against a partial one.
Q49What are the trade-offs of partitioning a large fact table by date?
Partitioning a sales or orders table by month or year lets the database prune irrelevant partitions entirely for range-filtered date queries, dramatically reducing I/O — the trade-off is added schema/maintenance complexity and the need for queries to filter on the partitioning column to benefit.
Q50How would you safely handle DATEADD(month, n, date) when the resulting day doesn't exist in the target month (e.g. Jan 31 + 1 month)?
SQL Server, MySQL and PostgreSQL clamp the result to the last valid day of the target month, so Jan 31 + 1 month becomes Feb 28 or 29. SQLite does not clamp: date('2024-01-31', '+1 month') overflows to 2024-03-02. Test edge-of-month dates before relying on month arithmetic in billing logic.

Summary

Key takeaways from this lesson.

Date functions analyse time-based data — the foundation of nearly every business report.
The operations are universal: current date, extract a part, add or subtract time, measure a difference, format.
The function names are not universal — always know which database you are writing for.
YEAR(), MONTH() and DAY() (MySQL, SQL Server), EXTRACT() (PostgreSQL, MySQL, Oracle) and strftime() (SQLite) extract date parts.
DATEADD (SQL Server), DATE_ADD (MySQL), INTERVAL (PostgreSQL) and date modifiers (SQLite) shift a date.
DATEDIFF, date subtraction and julianday() measure the gap between two dates, powering tenure, retention and delay metrics.
Age needs a check on whether this year's birthday has happened yet, to avoid an off-by-one error.
Group by year and month together, never by month alone.
Function-wrapped WHERE filters usually disable indexes — use a range on the raw column instead.
Date functions are the foundation for time series analysis and window functions.

Frequently Asked Questions

What are SQL date functions?

SQL date functions are built-in functions that work with date and time values. They return the current date, extract parts such as the year or month, add or subtract time, calculate the difference between two dates, and format dates for display. They are the basis of time-based reporting such as monthly revenue, tenure and retention.

How do I get the current date in SQL?

Use CURRENT_DATE in PostgreSQL, MySQL and SQLite. MySQL also offers CURDATE(), SQLite offers date('now'), and SQL Server uses CAST(GETDATE() AS date). For the date and time together use NOW() in MySQL and PostgreSQL, GETDATE() in SQL Server, or datetime('now') in SQLite.

How do I extract the year from a date in SQL?

Use YEAR(date_column) in MySQL and SQL Server, EXTRACT(YEAR FROM date_column) in PostgreSQL, MySQL and Oracle, or strftime('%Y', date_column) in SQLite. All of them return the four-digit year, although SQLite returns it as text.

What is DATEDIFF in SQL?

DATEDIFF calculates the difference between two dates. In SQL Server the syntax is DATEDIFF(day, start_date, end_date) and the unit can be day, month, year and more. In MySQL it is DATEDIFF(end_date, start_date) and always returns days. PostgreSQL subtracts dates directly (end_date - start_date), and SQLite uses julianday(end_date) - julianday(start_date).

What is DATEADD in SQL?

DATEADD is the SQL Server function that adds an interval to a date: DATEADD(day, 30, order_date) returns the date 30 days later, and a negative number subtracts. The equivalents are DATE_ADD(order_date, INTERVAL 30 DAY) in MySQL, order_date + INTERVAL '30 days' in PostgreSQL, and date(order_date, '+30 days') in SQLite.

How do I calculate age in SQL?

Count the full years between the date of birth and today, subtracting one if this year's birthday has not happened yet. MySQL does this with TIMESTAMPDIFF(YEAR, date_of_birth, CURDATE()) and PostgreSQL with EXTRACT(YEAR FROM AGE(date_of_birth)). In SQL Server, DATEDIFF(year, ...) only counts year boundaries, so it needs a CASE WHEN adjustment for the birthday.

What is the difference between DATE, DATETIME and TIMESTAMP?

DATE stores only a calendar date such as 2026-07-12. DATETIME and TIMESTAMP store a date together with a time of day, such as 2026-07-12 14:30:25, and TIMESTAMP is often time-zone aware. SQLite has no dedicated date types and stores these values as text, which is why the YYYY-MM-DD format matters there.

Why can WHERE YEAR(order_date) = 2026 be slow?

Wrapping a date column in a function such as YEAR() usually stops the database from using a standard index on that column, so it scans every row. A range filter — WHERE order_date >= '2026-01-01' AND order_date < '2027-01-01' — returns the same rows and can use the index.

Are SQL date functions the same in every database?

No. The operations are the same everywhere, but the function names differ: SQL Server uses GETDATE(), DATEADD() and DATEDIFF(unit, start, end); MySQL uses CURDATE(), DATE_ADD() and DATEDIFF(end, start); PostgreSQL uses CURRENT_DATE, INTERVAL arithmetic and date subtraction; SQLite uses date(), strftime() and julianday(). Always check which database you are writing for.

Which date functions work in SQLite?

SQLite has date(), time(), datetime(), julianday(), unixepoch() and strftime(), plus the keywords CURRENT_DATE, CURRENT_TIME and CURRENT_TIMESTAMP. It does not have YEAR(), MONTH(), DATEADD(), DATEDIFF() or EXTRACT(). The SQLabHub SQL Playground runs SQLite, so the practice examples in this lesson use those functions.

How are date functions used in data analytics?

Analysts use date functions for monthly and yearly revenue rollups, customer retention and churn analysis, cohort analysis, rolling 30-day trends, and for building the date fields behind dashboards in tools such as Power BI and Tableau.

What is time series analysis in SQL?

Time series analysis studies how a metric changes over time. In SQL it combines date functions with GROUP BY and aggregate functions to build trends such as month-over-month growth, year-over-year comparisons and rolling averages.

How can I practice SQL date queries?

Work through the practice exercises in this lesson, then run them in the free SQL Playground, which has orders, employees, users and payments tables with real date columns. The SQL date challenges add graded problems.