1

I have the following postgresql table;

 id   |        date_slot        
------+-------------------------
 1    | [2023-02-08,2023-02-15)
 2    | [2023-02-20,2023-02-26)
 3    | [2023-02-27,2023-03-29)

I want to make a query that return rows contained in these ranges but exclude weekends

for example the query I made return the following but does not exclude the weekends.

SELECT * FROM table where '2023-02-11'::date <@ date_slot;
 id   |        date_slot        
------+-------------------------
 1    | [2023-02-08,2023-02-15)

2023-02-11 is a weekend so it must not return a result. How can I do that?

Zegarek
  • 6,424
  • 1
  • 13
  • 24
simo9900
  • 133
  • 1
  • 9
  • 3
    try `SELECT * FROM table WHERE '2023-02-11'::date <@ date_slot AND date_part('dow', '2023-02-11'::date) NOT IN (0, 6);` where `date_part` filter weekend – NIKUNJ PATEL Feb 08 '23 at 11:00
  • Does this answer your question? [PostgreSql: select only weekends](https://stackoverflow.com/questions/47967242/postgresql-select-only-weekends) – Jonas Metzler Feb 08 '23 at 11:04

1 Answers1

1

Selecting workday-only dateranges (without weekends):

You can check what day of the week it is on the first day in the range using extract() and knowing its length from upper()-lower(), determine if it'll cross a weekend: online demo

select * 
from test_table 
where '2023-02-11'::date <@ date_slot
and extract(isodow from lower(date_slot)
                        + (not lower_inc(date_slot))::int)
  +(  (upper(date_slot) - (not upper_inc(date_slot))::int)
     -(lower(date_slot) + (not lower_inc(date_slot))::int) ) < 6 ;

Cases where your ranges have varying upper and lower bound inclusivity are handled by lower_inc() and upper_inc() - their boolean result, when cast to int, just adds or subtracts a day to account for whether it's included by the range or not.

The range is on or includes a weekend if it starts on a weekend day or continues for too long from any other day of the week:

  • 4 days, if it starts on a Monday (isodow=1)
  • 3 days, if it starts on a Tuesday (isodow=2)
  • 2 days, if it starts on a Wednesday (isodow=3)
  • 1 days, if it starts on a Thursday (isodow=4)
  • 0 days, if it starts on a Friday (isodow=5)

This means the isodow of range start date and the range length cannot sum up to more than 5 for the range not to overlap a weekend.


You can also enumerate the dates contained by these ranges using generate_series() and see if they include a Saturday (day 6) or a Sunday (0 as dow, 7 as isodow):

select * 
from test_table 
where '2023-02-11'::date <@ date_slot
and not exists (
  select true
  from generate_series(
          lower(date_slot) + (not lower_inc(date_slot))::int,
          upper(date_slot) - (not upper_inc(date_slot))::int,
          '1 day'::interval) as alias(d)
  where extract(isodow from d) in (6,7) );

Selecting records based on workday-only dates:

First comment got it right

select * 
from table_with_dateranges dr, 
     table_with_dates d
where d.date <@ dr.date_slot
and extract(isodow from d.date) not in (6,7);
Zegarek
  • 6,424
  • 1
  • 13
  • 24