How to add number of business days to given date
The key is to generate series of business days and number them with row_number()
:
create or replace function add_business_day(from_date date, num_days int)
returns date
as $fbd$
select d
from (
select d::date, row_number() over (order by d)
from generate_series(from_date+ 1, from_date+ num_days* 2+ 5, '1d') d
where
extract('dow' from d) not in (0, 6)
and d not in (select pyha from pyha)
) s
where row_number = num_days
$fbd$ language sql;
The test query's results seem correct:
select days, add_business_day('2018-12-08', days)
from generate_series(1, 20) days
days | add_business_day
------+------------------
1 | 2018-12-10
2 | 2018-12-11
3 | 2018-12-12
4 | 2018-12-13
5 | 2018-12-14
6 | 2018-12-17
7 | 2018-12-18
8 | 2018-12-19
9 | 2018-12-20
10 | 2018-12-21
11 | 2018-12-27
12 | 2018-12-28
13 | 2018-12-31
14 | 2019-01-02
15 | 2019-01-03
16 | 2019-01-04
17 | 2019-01-07
18 | 2019-01-08
19 | 2019-01-09
20 | 2019-01-10
(20 rows)
Alternatively, you can find the date in a loop:
create or replace function add_business_day_loop(from_date date, num_days int)
returns date
as $fbd$
begin
while num_days > 0 loop
from_date:= from_date+ 1;
while from_date in (select pyha from pyha) or extract('dow' from from_date) in (0, 6) loop
from_date:= from_date+ 1;
end loop;
num_days:= num_days- 1;
end loop;
return from_date;
end;
$fbd$ language plpgsql;
Add business days to date in SQL without loops
This answer has been significantly altered since it was accepted, since the original was wrong. I'm more confident in the new query though, and it doesn't depend on DATEFIRST
I think this should cover it:
declare @fromDate datetime
declare @daysToAdd int
select @fromDate = '20130123',@DaysToAdd = 4
declare @Saturday int
select @Saturday = DATEPART(weekday,'20130126')
;with Numbers as (
select 0 as n union all select 1 union all select 2 union all select 3 union all select 4
), Split as (
select @DaysToAdd%5 as PartialDays,@DaysToAdd/5 as WeeksToAdd
), WeekendCheck as (
select WeeksToAdd,PartialDays,MAX(CASE WHEN DATEPART(weekday,DATEADD(day,n.n,@fromDate))=@Saturday THEN 1 ELSE 0 END) as HitWeekend
from
Split t
left join
Numbers n
on
t.PartialDays >= n.n
group by WeeksToAdd,PartialDays
)
select DATEADD(day,WeeksToAdd*7+PartialDays+CASE WHEN HitWeekend=1 THEN 2 ELSE 0 END,@fromDate)
from WeekendCheck
We split the time to be added into a number of weeks and a number of days within a week. We then use a small numbers table to work out if adding those few days will result in us hitting a Saturday. If it does, then we need to add 2 more days onto the total.
Add Working Days to a Date Using JavaScript
It is possible to use Date
's setDate
function (in combination with getDate
) to add days onto a date i.e. -
var myDate = new Date(); // Tue 22/11/2016
myDate.setDate(myDate.getDate() + 3); // Fri 25/11/2016
So once you've calculated the number of weekend days within the workdays period you can add that and the required number of workdays to the start date to get the final date.
This function should work though obviously this will not take account of national holidays -
function addWorkDays(startDate, days) {
if(isNaN(days)) {
console.log("Value provided for \"days\" was not a number");
return
}
if(!(startDate instanceof Date)) {
console.log("Value provided for \"startDate\" was not a Date object");
return
}
// Get the day of the week as a number (0 = Sunday, 1 = Monday, .... 6 = Saturday)
var dow = startDate.getDay();
var daysToAdd = parseInt(days);
// If the current day is Sunday add one day
if (dow == 0)
daysToAdd++;
// If the start date plus the additional days falls on or after the closest Saturday calculate weekends
if (dow + daysToAdd >= 6) {
//Subtract days in current working week from work days
var remainingWorkDays = daysToAdd - (5 - dow);
//Add current working week's weekend
daysToAdd += 2;
if (remainingWorkDays > 5) {
//Add two days for each working week by calculating how many weeks are included
daysToAdd += 2 * Math.floor(remainingWorkDays / 5);
//Exclude final weekend if remainingWorkDays resolves to an exact number of weeks
if (remainingWorkDays % 5 == 0)
daysToAdd -= 2;
}
}
startDate.setDate(startDate.getDate() + daysToAdd);
return startDate;
}
//And use it like so (months are zero based)
var today = new Date(2016, 10, 22);
today = addWorkDays(today, 5); // Tue Nov 29 2016 00:00:00 GMT+0000 (GMT Standard Time)
It could also be added to the Date
prototype -
Date.prototype.addWorkDays = function (days) {
if(isNaN(days)) {
console.log("Value provided for \"days\" was not a number");
return
}
// Get the day of the week as a number (0 = Sunday, 1 = Monday, .... 6 = Saturday)
var dow = this.getDay();
var daysToAdd = parseInt(days);
// If the current day is Sunday add one day
if (dow == 0) {
daysToAdd++;
}
// If the start date plus the additional days falls on or after the closest Saturday calculate weekends
if (dow + daysToAdd >= 6) {
//Subtract days in current working week from work days
var remainingWorkDays = daysToAdd - (5 - dow);
//Add current working week's weekend
daysToAdd += 2;
if (remainingWorkDays > 5) {
//Add two days for each working week by calculating how many weeks are included
daysToAdd += 2 * Math.floor(remainingWorkDays / 5);
//Exclude final weekend if the remainingWorkDays resolves to an exact number of weeks
if (remainingWorkDays % 5 == 0)
daysToAdd -= 2;
}
}
this.setDate(this.getDate() + daysToAdd);
};
//And use it like so (months are zero based)
var today = new Date(2016, 10, 22)
today.addWorkDays(5); // Tue Nov 29 2016 00:00:00 GMT+0000 (GMT Standard Time)
Python - Add OR Subtract N *Business* Days from Date Input
Seems like a small tweak to your routine would do the trick:
from datetime import datetime, timedelta
def bizday_calc_func(self, start_date, num_days):
my_start_date = start_date
my_num_days = abs(num_days)
inc = 1 if num_days > 0 else -1
while my_num_days > 0:
my_start_date += timedelta(days=inc)
weekday = my_start_date.weekday()
if weekday >= 5:
continue
my_num_days -= 1
return my_start_date
disclaimer: untested.
Add two business days to a date array excluding holiday dates
You could do something as simple as use a while
loop.
$date = '2017-07-25';
$reserved = ['2017-07-27', '2017-07-28'];
$days = 2;
while ($days > 0) {
$date = date('Y-m-d', strtotime($date . ' +1 weekday'));
if (! in_array($date, $reserved)) $days--;
}
var_dump($date);
Add n business days to a given date ignoring holidays and weekends in python
Skipping weekends would be pretty easy doing something like this:
import datetime
def date_by_adding_business_days(from_date, add_days):
business_days_to_add = add_days
current_date = from_date
while business_days_to_add > 0:
current_date += datetime.timedelta(days=1)
weekday = current_date.weekday()
if weekday >= 5: # sunday = 6
continue
business_days_to_add -= 1
return current_date
#demo:
print '10 business days from today:'
print date_by_adding_business_days(datetime.date.today(), 10)
The problem with holidays is that they vary a lot by country or even by region, religion, etc. You would need a list/set of holidays for your use case and then skip them in a similar way. A starting point may be the calendar feed that Apple publishes for iCal (in the ics format), the one for the US would be http://files.apple.com/calendars/US32Holidays.ics
You could use the icalendar module to parse this.
How to add business days to date in python?
This works well enough. Not the quickest or cleanest solution, but easy to understand, and doesn't use any external libraries:
#date_calculator.py
import datetime as dt
def add_working_days(start_date, added_days):
# data from https://www.redcort.com/us-federal-bank-holidays/
federal_holidays = [dt.date(2018, 1, 1), dt.date(2018, 1, 15),
dt.date(2018, 2, 19), dt.date(2018, 5, 28), dt.date(2018, 7, 4),
dt.date(2018, 9, 3), dt.date(2018, 10, 8), dt.date(2018, 11, 12),
dt.date(2018, 11, 22), dt.date(2018, 12, 25)]
days_elapsed = 0
while days_elapsed < added_days:
test_date = start_date+dt.timedelta(days=1)
start_date = test_date
if test_date.weekday()>4 or test_date in federal_holidays:
# if a weekend or federal holiday, skip
continue
else:
# if a workday, count as a day
days_elapsed += 1
return start_date
print(add_working_days(dt.date(2018, 1, 11), 10))
#> 2018-01-26
print(add_working_days(dt.date(2018, 1, 11), 115))
#> 2018-06-26
How can I add business days to the current date in Java?
You may want to consider using ObjectLab Kit to do the heavy lifting for you.
Assuming the requirement is simply to return the next business day when the computed date falls on a non-business day:
package bizdays.example;
import java.time.LocalDate;
import java.util.HashSet;
import net.objectlab.kit.datecalc.common.DateCalculator;
import net.objectlab.kit.datecalc.common.DefaultHolidayCalendar;
import net.objectlab.kit.datecalc.common.HolidayHandlerType;
import net.objectlab.kit.datecalc.jdk8.LocalDateKitCalculatorsFactory;
import static org.junit.Assert.assertThat;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.Matchers.equalTo;
public class BizDayTest {
private DateCalculator<LocalDate> dateCalculator;
private final LocalDate startDate = LocalDate.of(2009, 12, 23);
@Before
public void setUp() {
HashSet<LocalDate> holidays = new HashSet<LocalDate>();
holidays.add(LocalDate.of(2009, 12, 25)); // Friday
DefaultHolidayCalendar<LocalDate> holidayCalendar =
new DefaultHolidayCalendar<LocalDate>(holidays);
LocalDateKitCalculatorsFactory.getDefaultInstance()
.registerHolidays("example", holidayCalendar);
dateCalculator = LocalDateKitCalculatorsFactory.getDefaultInstance()
.getDateCalculator("example", HolidayHandlerType.FORWARD);
dateCalculator.setStartDate(startDate);
}
@Test
public void should_not_change_calendar_start_date_even_after_moving() {
assertThat(
dateCalculator.moveByBusinessDays(6).getStartDate(),
equalTo(startDate));
}
@Test
public void moveByBusinessDays_will_return_24_dec_2009_as_next_business_day() {
assertThat(
dateCalculator.moveByBusinessDays(1).getCurrentBusinessDate(),
equalTo(LocalDate.of(2009, 12, 24)));
}
@Test
public void moveByBusinessDays_will_return_28_dec_2009_as_two_business_days_later() {
assertThat(
dateCalculator.moveByBusinessDays(2).getCurrentBusinessDate(),
equalTo(LocalDate.of(2009, 12, 28)));
}
@Test
public void moveByDays_will_also_return_28_dec_2009_as_two_business_days_later() {
assertThat(
dateCalculator.moveByDays(2).getCurrentBusinessDate(),
equalTo(LocalDate.of(2009, 12, 28)));
}
@Test
public void moveByBusinessDays_will_exclude_25_26_and_27_dec_when_computing_business_days() {
assertThat(
dateCalculator.moveByBusinessDays(5).getCurrentBusinessDate(),
equalTo(LocalDate.of(2009, 12, 31)));
}
@Test
public void moveByDays_will_include_25_26_and_27_dec_when_computing_business_days() {
assertThat(
dateCalculator.moveByDays(5).getCurrentBusinessDate(),
equalTo(LocalDate.of(2009, 12, 28)));
}
}
The library defaults the working week to be from Monday to Friday, but you can change the defaults by supplying a custom WorkingWeek
to DateCalculator
's setWorkingWeek()
.
As shown in the last two examples, moveByDays()
includes the weekends when moving the days, whereas moveByBusinessDays()
excludes weekends.
The library also allows you to use java.util.Calendar
or Joda Time's LocalDate
. The examples use JDK8's java.time.LocalDate
because it is the preferred way since JDK8.
Edit: Updated examples to use java.time.LocalDate
Related Topics
Create a Unique Index on a Non-Unique Column
How to Pivot Rows to Columns in MySQL Without Using Case
SQL Server:Select Rows Who's Sum Matches a Value
Combine Output Inserted.Id with Value from Selected Row
How to "Reset" Running Sum After It Reaches a Threshold
Find Min and Max for Subsets of Consecutive Rows - Gaps and Islands
SQL Server Count Number of Distinct Values in Each Column of a Table
Postgres Won't Accept Table Alias Before Column Name
How to Select First N Rows from a Table in T-Sql
Call a Stored Procedure in SQL Cte
How to Find Which Columns Don't Have Any Data (All Values Are Null)
Oracle SQL: Variables Used in Place of Table Names
How to Remove Part of the String in Oracle
How to Find All Open/Active Connections in Db2 (8.X)
Sparksql Error Table Not Found
Calculating SQL Server Row_Number() Over() for a Derived Table