Reputation: 6516
Currently, I am calculating a finish date based on the start date (DateTime) and duration (# of days), but my calculations do not take into account weekends or holidays.
So, my solution is not correct. This was just a starting point.
I read some articles out there and one approach is to create a gigantic calendar table that has all the weekends and holidays for the next 50 years. I guess the idea is to query a date range against the calendar table and subtract the # of weekends and/or holidays.
The problem is the software I am working on allows users to set their own project calendar. Wouldn't the table become to large to maintain given that the software allows users to manage multiple projects?
So, I guess my question how do I get started and what are some possible approaches to this problem?
Basically, for each project task I need to calculate the task finish date given the START date and DURATION, but taking weekends and custom days(aka holidays) into consideration.
Any ideas?
BTW: I'm using SQL Server 2005.
Upvotes: 3
Views: 16633
Reputation: 1
Use below code to get next wrking date after excluding weekends and Holidays
Declare @AddDay as integer = 3
Declare @NextWorkingDate DateTime
Declare @StartDate DateTime = Cast(getdate() as date)
While @AddDay > 0
begin
Select @NextWorkingDate = @StartDate + @AddDay +
(datediff(wk, @StartDate, @StartDate+ @AddDay ) * 2) -- add weekend
--Exclude weekend
If datepart(dw,@NextWorkingDate ) = 1 or datepart(dw,@NextWorkingDate ) = 7 --Add 2 days if target date is either Saturday or Sunday
set @NextWorkingDate = @NextWorkingDate + 2
--Count no of holidays if falling within start date and nextwrking date
Select @AddDay = Count(*) from HolidayTable ST --Holiday list
where ST.OffDate between @StartDate+1 and @NextWorkingDate
Set @StartDate = @NextWorkingDate
End
Select @NextWorkingDate
Upvotes: 0
Reputation: 13
Hi this is haw I resolve it:
first I created a calendar table (tb_cal) with two fields date_day (smalldate), holiday (bit)
CREATE TABLE [user].[tb_cal](
[date_day] [smalldatetime] NULL,
[holiday] [bit] NULL
) ON [PRIMARY]
then this function:
CREATE FUNCTION [user].[fc_get_labor_days]
(@from datetime, @to datetime)
RETURNS int
AS
BEGIN
return (
select count(*) as total
from tb_cal
where datepart(dw, date_day) not in (1,7)
and holiday <> 1
and date_day > @from and date_day <= @to )
END
you can call this function by passing the parameter (from, to)
SELECT user.fc_get_labor_days(my_date_from, my_date_to) as [days]
Hope this helps
Upvotes: 0
Reputation: 332521
I read some articles out there and one approach is to create a gigantic calendar table that has all the weekends and holidays for the next 50 years. I guess the idea is to query a date range against the calendar table and subtract the # of weekends and/or holidays.
It's because some holidays don't consistently fall on the same date. Labour day for example - the first Monday of September. It's easier & takes less space in the db to store the date it falls on in a per year basis than to try to code the rules to calculate it.
Other considerations are holidays that land on Sat/Sun - it's a tossup if the day off will fall on Monday or Friday. And some holidays will be federal while others are local...
Upvotes: 1
Reputation: 6516
@Stuart: Yes, 50 days is a type =P. I meant 50 years.
@rexem: That's true. The calculations would be too complex and error-prone.
@David: Creating a select-only master calendar table and then copying the dates I need per project sounds like a great idea.
(pardon, my twitter-like response, haha)
Thanks for the great responses!
Upvotes: 0
Reputation: 15849
Suppose you have a table called AllDays, which has columns called theDay and IsPublicHoliday. Also suppose your @@DATEFIRST is set to 1, so that your weekends are days 1 and 7. You want to find the date which is @n days on from @StartDate.
WITH NumberedDays AS
(
SELECT theDay, ROW_NUMBER() OVER (ORDER BY theDay) AS DayNum
FROM AllDays
WHERE DATEPART(dw, theDay) NOT IN (1,7)
AND IsPublicHoliday = 0
AND theDay > @StartDate
)
SELECT theDay
FROM NumberedDays
WHERE DayNum = @n
;
If you don't have a table called AllDays, then you can easily use a table of numbers, where theDay is DATEADD(day,num,@StartDate). You could do a LEFT JOIN to your list of ineligible days (which should be indexed, of course).
Upvotes: 0
Reputation: 13065
Create a large calendar that contains all weekends and holidays for the rest of the [long period of time], have it set to select only. Then copy the needed days into the calendar into the project's calendar each time a new project is created.
Upvotes: 1