I\'m looking for a function which adds number of business days to given date.
Holidays table
create table pyha (pyha date primary key) ;
insert into
I've had this issue myself - here is the function i have written to replace the excel workday() function as closely as possible, allowing negative workday additions as well as positive
create or replace function workday(startdate date, i integer) returns date as
$$
with workday_cte as (
select s.a::date as workday from
generate_series(startdate - ((abs(i) * 2 + 10) || ' day')::interval,
startdate + ((abs(i) * 2 + 10) || ' day')::interval, '1 day'::interval) s(a)
where extract(dow from s.a) between 1 and 5
except
select holiday
from holidays
)
select case when i > 0 then a.workday when i = 0 then startdate else b.workday end from
(
select * from workday_cte where workday > startdate
order by workday asc limit 1 offset greatest(abs(i) - 1,0)
) as a,
(
select * from workday_cte where workday < startdate
order by workday desc limit 1 offset greatest(abs(i) - 1,0)
) as b
$$ language sql;
You still need to update the section select holiday from holidays to your own table of holiday dates.
Here also is a networkdays() function replacement, where similarly you need to update the holidays table - but note that if startdate is not before enddate it returns 0 unlike the excel function
create or replace function networkdays(startdate date, enddate date) returns bigint as
$$
with workday_cte as (
select s.a::date as workday from
generate_series(startdate, enddate, '1 day'::interval) s(a)
where extract(dow from s.a) between 1 and 5
except
select holiday
from data.dtdholidays
)
select count(workday_cte.workday) from workday_cte
$$ language sql;
running this query to check the outputs:
select *, networkdays("wd-1", wd1) from
(
select day, workday(day, 1) as wd1, workday(day,0) as wd0, workday(day,-1) as "wd-1"
from (select day::date
from generate_series('2019-12-16'::date, '2019-12-23'::date, '1 day'::interval
) days(day)
) days(day)) a;
gives me:
day | wd1 | wd0 | wd-1 | networkdays
------------+------------+------------+------------+-------------
2019-12-16 | 2019-12-17 | 2019-12-16 | 2019-12-13 | 3
2019-12-17 | 2019-12-18 | 2019-12-17 | 2019-12-16 | 3
2019-12-18 | 2019-12-19 | 2019-12-18 | 2019-12-17 | 3
2019-12-19 | 2019-12-20 | 2019-12-19 | 2019-12-18 | 3
2019-12-20 | 2019-12-23 | 2019-12-20 | 2019-12-19 | 3
2019-12-21 | 2019-12-23 | 2019-12-21 | 2019-12-20 | 2
2019-12-22 | 2019-12-23 | 2019-12-22 | 2019-12-20 | 2
2019-12-23 | 2019-12-24 | 2019-12-23 | 2019-12-20 | 3
In some situations it is better that the number of workdays in the period created by adding or subtracting a fixed number of days is constant - for example in the table above when a weekend date (the 21st or 22nd of december) is used as the startdate then networkdays is 2 and not 3. You can change the behaviour of the workday function so that the networkdays is constant by rolling to the next workday before adding or subtracting the workdays required
create or replace function workdaycwd(startdate date, i integer) returns date as
$$
with workday_cte as (
select s.a::date as workday from
generate_series(startdate - ((abs(i) * 2 + 10) || ' day')::interval,
startdate + ((abs(i) * 2 + 10) || ' day')::interval, '1 day'::interval) s(a)
where extract(dow from s.a) between 1 and 5
except
select holiday
from data.dtdholidays
)
select case when i >= 0 then a.workday else b.workday end from
(
select * from workday_cte where workday >= startdate
order by workday asc limit 1 offset (abs(i))
) as a,
(
select * from workday_cte where workday < startdate
order by workday desc limit 1 offset greatest(abs(i) - 1, 0)
) as b
$$ language sql;
running a similar query as above gives
day | wd1 | wd0 | wd-1 | networkdays
------------+------------+------------+------------+-------------
2019-12-16 | 2019-12-17 | 2019-12-16 | 2019-12-13 | 3
2019-12-17 | 2019-12-18 | 2019-12-17 | 2019-12-16 | 3
2019-12-18 | 2019-12-19 | 2019-12-18 | 2019-12-17 | 3
2019-12-19 | 2019-12-20 | 2019-12-19 | 2019-12-18 | 3
2019-12-20 | 2019-12-23 | 2019-12-20 | 2019-12-19 | 3
2019-12-21 | 2019-12-24 | 2019-12-23 | 2019-12-20 | 3
2019-12-22 | 2019-12-24 | 2019-12-23 | 2019-12-20 | 3
2019-12-23 | 2019-12-24 | 2019-12-23 | 2019-12-20 | 3