2

I have a table that contains the columns start and end, which contain the start and end date of a date range. I want to select the number of working days (number of days excluding saturday and sunday) using pure sqlite3 functionality from each time range. Is there any way to do this?

I checked the several answers for this problem, but many seem to use functions like DATEDIFF which aren't available in sqlite.

Example: Start date '2015-09-19' and end date '2015-09-22' should result in two working days.

Hunter Turner
  • 6,804
  • 11
  • 41
  • 56
muffel
  • 7,004
  • 8
  • 57
  • 98

2 Answers2

4

If you had a table like this, your job would be easy.

-- I'm going to drop this table later.
create table calendar (
  cal_date date primary key
);

insert into calendar values ('2015-09-18');
insert into calendar values ('2015-09-19');
insert into calendar values ('2015-09-20');
insert into calendar values ('2015-09-21');
insert into calendar values ('2015-09-22');
insert into calendar values ('2015-09-23');

-- Weekdays
select cal_date, strftime('%w', cal_date) day_of_week
from calendar
where day_of_week between 1 and 5;
cal_date    day_of_week
----------  -----------
2015-09-18  5          
2015-09-21  1          
2015-09-22  2          
2015-09-23  3          
-- Weekdays between two dates
select cal_date, strftime('%w', cal_date) day_of_week
from calendar
where day_of_week between 1 and 5
  and cal_date between '2015-09-19' and '2015-09-22';
cal_date    day_of_week
----------  -----------
2015-09-21  1          
2015-09-22  2          
-- Count them
select count(*) num_days
from (select cal_date, strftime('%w', cal_date) day_of_week
      from calendar
      where day_of_week between 1 and 5
        and cal_date between '2015-09-19' and '2015-09-22') x;
num_days  
----------
2         

So let's manufacture a table like that from start and end dates. To do that, we need a table of integers.

create table integers (
  n integer primary key
);

insert into integers values (0);
insert into integers values (1);
insert into integers values (2);
insert into integers values (3);
insert into integers values (4);
insert into integers values (5);

The number of rows you insert is important. The lowest start date plus the largest integer usually needs to return a result later than the latest end date. Typically, you need a lot more than five rows. You've been warned.

create table test (start date, end date);
insert into test values('2015-09-19','2015-09-22');
insert into test values('2015-09-19','2015-09-19');

-- Calendar from date range. Look at your query plan.
-- There might be a better way to do this. I think you're 
-- going to have to use a cross join with SQLite, though.
select distinct date(start, '+' || n || ' days') as cal_date
from test, integers 
order by cal_date;
cal_date  
----------
2015-09-19
2015-09-20
2015-09-21
2015-09-22
2015-09-23
2015-09-24
2015-09-25

Let's expand that a little to include the day of the week, and make it a view.

-- Calendar view from date range
drop table calendar;
create view calendar as 
select distinct date(start, '+' || n || ' days') as cal_date,
       case strftime('%w', date(start, '+' || n || ' days')) 
       when 0 then 'Sun'
       when 1 then 'Mon'
       when 2 then 'Tue'
       when 3 then 'Wed'
       when 4 then 'Thu'
       when 5 then 'Fri'
       when 6 then 'Sat'
       end as day_of_week
from test, integers;

Now we can count the number of weekdays using a join and an aggregate function.

select start, end, count(cal_date) as num_weekdays
from test
left join calendar on cal_date between start and end 
             and day_of_week in ('Mon', 'Tue', 'Wed', 'Thu', 'Fri')
group by start, end
order by start, end;
start       end         num_weekdays
----------  ----------  ------------
2015-09-19  2015-09-19  0           
2015-09-19  2015-09-22  2           
Mike Sherrill 'Cat Recall'
  • 91,602
  • 17
  • 122
  • 185
0

Have a look at the Date and Time Functions of SQLite. Those are the ones that can be used as part of SQLite-internal queries.

Unfortunately, those are rather basic functions that can be used to map the String representation of a date to something else. The julianday() function is the only one one could calculate differences in days with, but it would not care about Saturday and Sunday.

Maybe there could be an approach based on "day of the week" (0 == Sunday, 6 == Saturday) and using the "seconds since 1970-01-01" to come up with a conditional expression using the time-difference in seconds.

Martin C.
  • 12,140
  • 7
  • 40
  • 52
  • Yes, but how could I do this with ranges? I haven't found a way to enumerate weekdays of a day range – muffel Sep 22 '15 at 10:32
  • Given number of seconds, you can calculate how many weeks (divide by 604,800), and then given the first day-of-week in the range you can subtract the correct number of weekend days from the total days. – Martin C. Sep 22 '15 at 11:18
  • What if it is less than one week? It could include the weekend as well (but doesn't always do) – muffel Sep 22 '15 at 12:26
  • Then the number of weeks would be 0 (integer division). And that's why I said it depends on the starting day. – Martin C. Sep 22 '15 at 12:27