Home Functions DATEDIFF()
SQL Date & Time Function

DATEDIFF()

DATEDIFF() returns the difference between two dates as an integer, but the argument order and units differ sharply between SQL Server, MySQL and PostgreSQL.

SQL ServerMySQLPostgreSQL
Returns: An integer count of the interval between two dates. The unit and argument order depend on the database engine.

Syntax

DATEDIFF(datepart, startdate, enddate) -- SQL ServerDATEDIFF(enddate, startdate) -- MySQL, returns daysTIMESTAMPDIFF(unit, startdate, enddate) -- MySQL, other unitsenddate - startdate -- PostgreSQL, returns daysAGE(enddate, startdate) -- PostgreSQL, interval
ParameterTypeRequiredDescription
datepart keyword SQL Server only The boundary to count: year, quarter, month, week, day, hour, minute, second. SQL Server counts how many of these boundaries are crossed.
startdate date or datetime yes The earlier date. In SQL Server it is the second argument; in MySQL it is the second argument of DATEDIFF but the difference is enddate minus startdate.
enddate date or datetime yes The later date. In MySQL DATEDIFF this comes FIRST, the reverse of SQL Server.
unit keyword MySQL TIMESTAMPDIFF The unit for TIMESTAMPDIFF: YEAR, MONTH, WEEK, DAY, HOUR, MINUTE, SECOND.

How it works

DATEDIFF() answers a common question: how far apart are two dates? The catch is that it is one of the least portable functions in SQL, because every engine implemented it differently. Learning the differences up front saves you from a whole class of subtle bugs.

In SQL Server, the form is DATEDIFF(datepart, startdate, enddate). You choose the unit yourself (day, month, year and so on), and it returns the number of times that boundary is crossed between the two dates. In MySQL, DATEDIFF(enddate, startdate) takes only two arguments, always returns whole days, and crucially puts the end date FIRST. For any other unit in MySQL you use TIMESTAMPDIFF(unit, start, end).

PostgreSQL has no DATEDIFF function at all. To get whole days you simply subtract one date from the other (enddate - startdate), which yields an integer. For richer results you use date arithmetic helpers such as AGE(end, start), which returns a years/months/days interval, or EXTRACT to pull a component out of that interval.

Examples

Days between two dates in SQL Server

-- datepart first, then start, then end
SELECT DATEDIFF(day, '2026-01-01', '2026-07-04') AS days_between;
Result
days_between
------------
184

Days between two dates in MySQL (reversed order)

-- end date FIRST, then start date; always returns days
SELECT DATEDIFF('2026-07-04', '2026-01-01') AS days_between;
Result
days_between
------------
184

Days between two dates in PostgreSQL

-- subtracting two dates yields an integer number of days
SELECT DATE '2026-07-04' - DATE '2026-01-01' AS days_between;
Result
days_between
------------
184

Age in whole years

-- SQL Server: subtract, then correct if the birthday
-- has not happened yet this year
SELECT DATEDIFF(year, birth_date, GETDATE())
     - CASE WHEN (MONTH(birth_date) * 100 + DAY(birth_date))
              > (MONTH(GETDATE()) * 100 + DAY(GETDATE()))
             THEN 1 ELSE 0 END AS age_years
FROM customers;
Result
age_years
---------
       34
       28
       41

Age in years in MySQL and PostgreSQL

-- MySQL: TIMESTAMPDIFF gives complete years directly
SELECT TIMESTAMPDIFF(YEAR, birth_date, CURDATE()) AS age_years
FROM customers;

-- PostgreSQL: AGE returns an interval, EXTRACT the year
SELECT EXTRACT(YEAR FROM AGE(CURRENT_DATE, birth_date)) AS age_years
FROM customers;
Result
age_years
---------
       34
       28
       41

Months between two dates

-- SQL Server
SELECT DATEDIFF(month, '2026-01-15', '2026-07-04') AS months;

-- MySQL
SELECT TIMESTAMPDIFF(MONTH, '2026-01-15', '2026-07-04') AS months;
Result
months (SQL Server) | months (MySQL)
--------------------+---------------
                  6 |             5

Common mistakes

Wrong
-- Wrong: in MySQL this is end minus start, so
-- putting the earlier date first gives a NEGATIVE result
-- (MySQL) or a totally different meaning (SQL Server)
SELECT DATEDIFF('2026-01-01', '2026-07-04');  -- MySQL: -184
Right
-- MySQL: end date FIRST
SELECT DATEDIFF('2026-07-04', '2026-01-01');  -- 184

-- SQL Server: start date FIRST (and a datepart)
SELECT DATEDIFF(day, '2026-01-01', '2026-07-04');  -- 184

The single most common DATEDIFF bug: SQL Server is DATEDIFF(part, start, end) but MySQL is DATEDIFF(end, start). The argument order is REVERSED between the two engines, so code copied from one to the other silently flips the sign.

Wrong
-- Wrong assumption: this is NOT one full month apart.
-- SQL Server counts month BOUNDARIES crossed, not
-- 30-day periods, so Jan 31 to Feb 01 returns 1
SELECT DATEDIFF(month, '2026-01-31', '2026-02-01');  -- 1
Right
-- If you need complete elapsed months, use TIMESTAMPDIFF
-- in MySQL, or subtract and check the day-of-month
-- yourself in SQL Server.
SELECT TIMESTAMPDIFF(MONTH, '2026-01-31', '2026-02-01');  -- 0

In SQL Server DATEDIFF counts how many datepart boundaries are crossed, not how many full units elapsed. DATEDIFF(year, '2025-12-31', '2026-01-01') returns 1 even though only one day passed.

Wrong
-- Wrong: wrapping the indexed column in DATEDIFF forces
-- a full scan; the index on order_date cannot be used
SELECT * FROM orders
WHERE DATEDIFF(day, order_date, GETDATE()) <= 30;
Right
-- Right: keep the column bare and compare to a computed
-- bound so the index on order_date stays usable
SELECT * FROM orders
WHERE order_date >= DATEADD(day, -30, GETDATE());

Calling DATEDIFF on an indexed column makes the predicate non-sargable, so the optimiser cannot use the index. Rewrite the filter so the column stands alone. See the indexing guide.

Performance

The biggest performance trap with DATEDIFF() is wrapping it around a column in a WHERE clause, as in WHERE DATEDIFF(day, order_date, GETDATE()) <= 30. This makes the predicate non-sargable: the engine must compute the function for every row and cannot seek an index on that column. Always move the arithmetic to the constant side with DATEADD so the column stays bare and the index can be used.

DATEDIFF itself is a cheap scalar operation on two values, so in a SELECT list it costs almost nothing. The expense is never the function; it is losing an index seek because a column was buried inside it. When you must group or filter by a derived period, consider a persisted computed column or an indexed date bucket instead of recomputing DATEDIFF on the fly.

Because behaviour and argument order differ across engines, put date math in one place (a view or a helper) so a migration does not scatter silent bugs. Test the boundary cases, especially month and year counts, since SQL Server counts boundaries crossed rather than whole elapsed units.

Interview questions

How do you get the number of days between two dates in SQL Server, MySQL and PostgreSQL?

SQL Server: DATEDIFF(day, start, end). MySQL: DATEDIFF(end, start) (end first, always days). PostgreSQL: subtract the dates, end - start, which returns an integer number of days.

-- SQL Server
SELECT DATEDIFF(day, '2026-01-01', '2026-07-04');
-- MySQL
SELECT DATEDIFF('2026-07-04', '2026-01-01');
-- PostgreSQL
SELECT DATE '2026-07-04' - DATE '2026-01-01';

What is the difference in argument order between SQL Server and MySQL DATEDIFF?

SQL Server takes DATEDIFF(datepart, startdate, enddate) with the start date first and a unit. MySQL takes DATEDIFF(enddate, startdate) with the end date first and no unit. The order is reversed, so the same call can return opposite signs on the two engines. This is a classic interview trap.

How would you calculate a person age in whole years?

In MySQL use TIMESTAMPDIFF(YEAR, birth_date, CURDATE()), which already returns complete years. In PostgreSQL use EXTRACT(YEAR FROM AGE(CURRENT_DATE, birth_date)). In SQL Server, take DATEDIFF(year, birth_date, GETDATE()) and subtract 1 if the birthday has not occurred yet this year, because DATEDIFF only counts year boundaries.

SELECT TIMESTAMPDIFF(YEAR, birth_date, CURDATE()) AS age
FROM customers;

Why does DATEDIFF(year, '2025-12-31', '2026-01-01') return 1 in SQL Server?

Because SQL Server DATEDIFF counts how many datepart boundaries are crossed, not how many full units elapsed. Only one day passed, but the year boundary between 2025 and 2026 was crossed once, so the year count is 1. For complete elapsed years you must correct for the month and day, or use TIMESTAMPDIFF in MySQL.

Why should you avoid DATEDIFF on a column in a WHERE clause?

Wrapping an indexed column in DATEDIFF makes the predicate non-sargable, so the optimiser cannot seek the index and must scan every row. Rewrite the filter to keep the column bare, for example order_date >= DATEADD(day, -30, GETDATE()).

Master SQL, one function at a time

Browse the full SQL functions library, or learn the fundamentals with our free, structured courses.