I am looking for a way to implement the SQLServer-function datediff in PostgreSQL. That is,
This function returns the count (as a signed integer valu
@WebWanderer 's answer is very close to the DateDiff using SQL server, but inaccurate. That is because of the usage of age() function.
e.g. days between '2019-07-29' and '2020-06-25' should return 332, however, using the age() function it will returns 327. Because the age() returns '10 mons 27 days" and it treats each month as 30 days which is incorrect.
You shold use the timestamp to get the accurate result. e.g.
ceil((select extract(epoch from (current_date::timestamp - <your_date>::timestamp)) / 86400))
Here is a complete example with output. psql (10.1, server 9.5.10).
You get 58, not some value less than 30.
Remove age() function, solved the problem that previous post mentioned.
drop table t;
create table t(
d1 date
);
insert into t values(current_date - interval '58 day');
select d1
, current_timestamp - d1::timestamp date_diff
, date_part('day', current_timestamp - d1::timestamp)
from t;
d1 | date_diff | date_part
------------+-------------------------+-----------
2018-05-21 | 58 days 21:41:07.992731 | 58
Simply subtract them:
SELECT ('2015-01-12'::date - '2015-01-01'::date) AS days;
The result:
days
------
11
Almost the same function as you needed (based on atiruz's answer, shortened version of UDF from here)
CREATE OR REPLACE FUNCTION datediff(type VARCHAR, date_from DATE, date_to DATE) RETURNS INTEGER LANGUAGE plpgsql
AS
$$
DECLARE age INTERVAL;
BEGIN
CASE type
WHEN 'year' THEN
RETURN date_part('year', date_to) - date_part('year', date_from);
WHEN 'month' THEN
age := age(date_to, date_from);
RETURN date_part('year', age) * 12 + date_part('month', age);
ELSE
RETURN (date_to - date_from)::int;
END CASE;
END;
$$;
Usage:
/* Get months count between two dates */
SELECT datediff('month', '2015-02-14'::date, '2016-01-03'::date);
/* Result: 10 */
/* Get years count between two dates */
SELECT datediff('year', '2015-02-14'::date, '2016-01-03'::date);
/* Result: 1 */
/* Get days count between two dates */
SELECT datediff('day', '2015-02-14'::date, '2016-01-03'::date);
/* Result: 323 */
/* Get months count between specified and current date */
SELECT datediff('month', '2015-02-14'::date, NOW()::date);
/* Result: 47 */
I spent some time looking for the best answer, and I think I have it.
This sql will give you the number of days between two dates as integer
:
SELECT
(EXTRACT(epoch from age('2017-6-15', now())) / 86400)::int
..which, when run today (2017-3-28
), provides me with:
?column?
------------
77
The misconception about the accepted answer:
select age('2010-04-01', '2012-03-05'),
date_part('year',age('2010-04-01', '2012-03-05')),
date_part('month',age('2010-04-01', '2012-03-05')),
date_part('day',age('2010-04-01', '2012-03-05'));
..is that you will get the literal difference between the parts of the date strings, not the amount of time between the two dates.
I.E:
Age(interval)=-1 years -11 mons -4 days;
Years(double precision)=-1;
Months(double precision)=-11;
Days(double precision)=-4;
One more solution, version for the 'years' difference:
SELECT count(*) - 1 FROM (SELECT distinct(date_trunc('year', generate_series('2010-04-01'::timestamp, '2012-03-05', '1 week')))) x
2
(1 row)
And the same trick for the months:
SELECT count(*) - 1 FROM (SELECT distinct(date_trunc('month', generate_series('2010-04-01'::timestamp, '2012-03-05', '1 week')))) x
23
(1 row)
In real life query there can be some timestamp sequences grouped by hour/day/week/etc instead of generate_series.
This 'count(distinct(date_trunc('month', ts)))'
can be used right in the 'left' side of the select:
SELECT sum(a - b)/count(distinct(date_trunc('month', c))) FROM d
I used generate_series() here just for the brevity.