How to count days except Sundays between two dates in Postgres?

后端 未结 2 978
闹比i
闹比i 2020-12-06 23:19

To find the number of days between two dates we can use something like this:

SELECT date_part(\'day\',age(\'2017-01-31\',\'2017-01-01\')) as total_days;


        
相关标签:
2条回答
  • 2020-12-06 23:44

    You could try using generate_series to generate all the dates between given date and then take count of required days.

    SELECT
        count(case when extract(dow from generate_series) <> 0 then 1 end) n
    from generate_series('2017-01-01'::date,'2017-01-31'::date, '1 day');
    
    0 讨论(0)
  • 2020-12-06 23:57

    You need to define "between two dates" more closely. Lower and upper bound included or excluded? A common definition would be to include the lower and exclude the upper bound of an interval. Plus, define the result as 0 when lower and upper bound are identical. This definition happens to coincide with date subtraction exactly.

    SELECT date '2017-01-31' - date '2017-01-01' AS days_between
    

    This exact definition is important for excluding Sundays. For the given definition an interval from Sun - Sun (1 week later) does not include the upper bound, so there is only 1 Sunday to subtract.

    interval in days  | sundays
    0                 | 0
    1-6               | 0 or 1
    7                 | 1
    8-13              | 1 or 2
    14                | 2
    ...
    

    An interval of 7 days always includes exactly one Sunday.

    We can get the minimum result with a plain integer division (days / 7), which truncates the result.

    The extra Sunday for the remainder of 1 - 6 days depends on the first day of the interval. If it's a Sunday, bingo; if it's a Monday, too bad. Etc. We can derive a simple formula from this:

    SELECT days, sundays, days - sundays AS days_without_sundays
    FROM  (
       SELECT z - a AS days
          , ((z - a) + EXTRACT(isodow FROM a)::int - 1 ) / 7 AS sundays
       FROM  (SELECT date '2017-01-02' AS a       -- your interval here
                   , date '2017-01-30' AS z) tbl
       ) sub;
    

    Works for any given interval.
    Note: isodow, not dow for EXTRACT().

    To include the upper bound, just replace z - a with (z - a) + 1. (Would work without parentheses, due to operator precedence, but better be clear.)

    Performance characteristic is O(1) (constant) as opposed to a conditional aggregate over a generated set with O(N).

    Related:

    • How do I determine the last day of the previous month using PostgreSQL?
    • Calculate working hours between 2 dates in PostgreSQL
    0 讨论(0)
提交回复
热议问题