Login or Sign Up to become a member!
LessThanDot Sit Logo

LessThanDot

Data Management

Less Than Dot is a community of passionate IT professionals and enthusiasts dedicated to sharing technical knowledge, experience, and assistance. Inside you will find reference materials, interesting technical discussions, and expert tips and commentary. Once you register for an account you will have immediate access to the forums and all past articles and commentaries.

LTD Social Sitings

Lessthandot twitter Lessthandot Linkedin Lessthandot friendfeed Lessthandot facebook Lessthandot rss

Note: Watch for social icons on posts by your favorite authors to follow their postings on these and other social sites.

Your profile

Search

XML Feeds

Google Ads

« Using schemas to maintain order as a DBASQL Server Proper Case Function »
comments
Rate Post:
submit to reddit Digg!FacebookDotnetkicks

Today I saw someone asked a question in MSDN t-sql forums "How to calculate the number of working days between two given dates". I found this very interesting, since we can solve this problem by using a very simple and methodical approach and by using an unorthodox approach. I will explain both the approaches.

1. By Using Auxilary Calendar Table
2. Based on the weekdays of start date and end dates.

By Using Auxilary Calendar Table :
This is an good and methodical approach. In this approach, the database has a table that has all the dates that we can represent using sql server. The following script will insert all the dates between 1/1/1753 (minimum date sql can recognize in sql) and 12/31/9999 (maximum date sql can recognize in sql).

  1. CREATE TABLE AuxCalendarDates
  2. (
  3.     CalDate DATETIME
  4. )
  5.  
  6. ;with N AS
  7. (
  8.     SELECT 0 AS num UNION all SELECT 1 UNION all SELECT 2 UNION all SELECT 3 UNION all SELECT 4 UNION all SELECT 5 UNION all
  9.     SELECT 6 UNION all SELECT 7 UNION all SELECT 8 UNION all SELECT 9   
  10. ),
  11. Numbers AS
  12. (
  13.     SELECT row_number() OVER (ORDER BY (SELECT null)) AS Number FROM N N,N N1,N N2,N N3,N N4,N N5,N N6
  14. )
  15. INSERT INTO AuxCalendarDates
  16. SELECT DATEADD(DAY,Number - 1,'1/1/1753') FROM Numbers WHERE Number <= 3012154

3012154 is the Total number of days sql can recognize.

Once, calendar table created, Solving this is fairly straight forward. It needs to satisfy the following 2 conditions.

1. The Calendar date should between the 2 given dates. using a where clause like below will do the trick.

  1. WHERE caldate between @startdate and @enddate

2. date should be between Monday to Friday
To find the weekday, the normal approach is using datepart(dw,date) function.
But unfortunately this function depends on the @@datefirst settings.
If we change the datefirst value, we will get an different week day. run the below code in SSMS. It will give different week numbers for different datefirst values.

  1. SET datefirst 7
  2. SELECT DATEPART(dw,GETDATE())
  3. SET datefirst 5
  4. SELECT DATEPART(dw,GETDATE())

There is another way to find a weekday,
Calculating the number of days since the beginning and calculating the remainder by dividing with 7.

  1. SELECT DATEDIFF(dd,0,GETDATE())%7

The above expression will give 0 for Monday, 1 for Tuesday, 2 for Wednesday , 3 for Thursday ,4 for Friday , 5 for Saturday and 6 for Sunday.

The above expression is also independent of datefirst settings.

Now using the above expression, to check the weekday,

  1. DATEDIFF(dd,0,Caldate)%7 between 0 and 4

finally, keeping it in a function, will make this re-usable.

  1. CREATE FUNCTION [dbo].[fn_NoofWorkdaysBetweenDates_Table]
  2. (
  3.     @StartDate DATETIME,
  4.     @EndDate DATETIME
  5. )
  6. RETURNS INT
  7. AS
  8. BEGIN
  9. RETURN
  10. (
  11.     SELECT COUNT(*) FROM AuxCalendarDates WHERE CalDate between @startdate and @enddate
  12.     and DATEDIFF(dd,0,Caldate)%7 between 0 and 4
  13. );
  14. END

Now i will explain the second method, which is unorthodox. This Method depends on the pattern of week day of start date and week day of end date, and based on that finding the condition.

For example, take 2 dates Feb1st2010,Feb8th2010. Feb1st 2010 is monday and Feb8th 2010 is monday. so the no of working days = 1 + ( 5 * 1) = 6 days..
if starting day is monday, and endday is also monday, no of working days = 1 Working day + (5 * no of finished weeks between the 2 dates)

Take another example.
Take 2 dates Feb1st2010,Feb9th2010. Feb1st 2010 is monday and Feb9th 2010 is tuesday. so the no of working days = 2 + ( 5 * 1) = 7 days..

if starting day is monday , and endday is tuesday, no of working days
= 2 working days + ( 5 * no of weeks between the 2 dates)

Generalizing the above examples, the following formula can be derived.

No of working dates between 2 dates = (Additional Working days) + (5 * no of finished weeks between the 2 dates)

In above formula, Additional Working days will varies and it depends on the week day of starting date and weekday of ending date.

To get the Additional working days, below table will be useful..

0 1 2 3 4 5 6
------------------
0| 1 2 3 4 5 5 5
1| 5 1 2 3 4 4 4
2| 4 5 1 2 3 3 3
3| 3 4 5 1 2 2 2
4| 2 3 4 4 1 1 1
5| 1 2 3 4 5 0 0
6| 1 2 3 4 5 5 0

0 to 6 in row represents the week day of starting date
0 to 6 in columns represent the week day of ending date

Suppose to calculate Additional working days between Tuesday and Friday,

As, Tuesday, week day no is 1 and Friday Week day no is 4, checking the value in RowNo-1 against colNo-4 will give you the no of additional working days as 4.

Now, to use this table in query, concatenate these all columns into single row, so, that there will be only 7 rows,and use those in the query as inline table.. based on the weekday of starting date, filtering the row and based on weekday of ending date, filtering the column..
so, final implementation of this function will be

  1. CREATE FUNCTION [dbo].[fn_NoofWorkdaysBetweenDates]
  2. (
  3.     @StartDate DATETIME,
  4.     @EndDate DATETIME
  5. )
  6. RETURNS INT
  7. AS
  8. BEGIN
  9. RETURN
  10. (
  11.     SELECT (DATEDIFF(dd,@startdate,@enddate)/7)*5 +  SUBSTRING(EndWk,DATEDIFF(dd,0,@enddate)%7+1,1)
  12.     FROM ( SELECT 0 AS StartWk,'1234555' AS EndWK UNION all SELECT 1,'5123444' UNION all SELECT 2,'4512333' UNION all
  13.     SELECT 3,'3451222' UNION all SELECT 4,'2344111' UNION all SELECT 5,'1234500' UNION all SELECT 6,'1234550')t
  14.     WHERE StartWk = DATEDIFF(dd,0,@startdate)%7
  15. );
  16. END
1520 views
submit to reddit Digg!FacebookDotnetkicks

Comments and Feedback

8 comments

Comment from: ca8msm [Member] Email
Excellent post, keep up the good work!
26/02/10 @ 00:56
Comment from: remou [Member] Email
Interesting ideas. I look forward to more.
26/02/10 @ 03:11
Comment from: SQLDenis [Member] Email
Very neat indeed

congrats on your first blog post here.....can't wait for more great stuff
26/02/10 @ 09:00
Comment from: Ramireddy [Member] Email
Thank You Smith,Remou and Denis..

I am very happy all you liked it..


Thanks Remou for giving me very valuable review comments and helping me in cleaning the errors in this..
26/02/10 @ 10:13
Comment from: Ken [Visitor]
Nice math, too bad it is total bull. For one full year you will get between 260 to 262 workdays.
The actual number of workdays all depends on the recognized holidays you company recognizes.
Do you work a 10/4 workweek? (10 hours for 4 days a week. Maybe from Friday to Monday? Never taking a day off you have 208 to 210 workdays in a year.)
What about the companies open 24/7? The company workdays are 365 to 366 in a year. Don't count company workdays? Then you need to remove your vacation days!
What about when you work from 10PM to 6:30AM? Is one shift 1 or 2 workdays?
When I was a teenager, I worked from 9 to 9:30 (PM to AM) for 6 days a week during summer. I had 92 workdays a year then. (June 1st through August 31) OK, it wasn't exactly those days, I don't remember which days I worked.
27/02/10 @ 21:37
Comment from: Ramireddy [Member] Email
Ken, I will agree with you. But when you are writing a blog or article, you cannot cover all the cases. u use this as starting point and convert it as per requirements.

27/02/10 @ 22:32
Comment from: SQLDenis [Member] Email
Ken, all you need is a holiday/non working day table in addition to this

you do this calculation and then you subtract the days in the holiday table and you are done.

I have a holiday table that covers over 90 different countries and it even accounts for short trading days
02/03/10 @ 11:21
Comment from: Smel [Visitor]
Found this really helpful. It was exactly what I needed.

Thanks
23/06/10 @ 22:36

Leave a comment


Your email address will not be revealed on this site.

Your URL will be displayed.
(Line breaks become <br />)
(Name, email & website)
(Allow users to contact you through a message form (your email will not be revealed.)