PostgreSQL: days/months/years between two dates

后端 未结 10 1867
旧时难觅i
旧时难觅i 2020-12-12 20:22

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

相关标签:
10条回答
  • 2020-12-12 20:30

    @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))

    0 讨论(0)
  • 2020-12-12 20:32

    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
    
    0 讨论(0)
  • 2020-12-12 20:33

    Simply subtract them:

    SELECT ('2015-01-12'::date - '2015-01-01'::date) AS days;
    

    The result:

     days
    ------
       11
    
    0 讨论(0)
  • 2020-12-12 20:33

    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 */
    
    0 讨论(0)
  • 2020-12-12 20:39

    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;

    0 讨论(0)
  • 2020-12-12 20:39

    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.

    0 讨论(0)
提交回复
热议问题