Get number of weekdays (Sundays, Mondays, Tuesdays) between two dates SQL - sql

So, I'd like to, for a Start Date and End Date, determine how many particular days of the week occur between these two dates.
So how many mondays, tuesdays, etc
I know I can do this with a loop between the Start and End Date and check each day, but the difference could possibly be a great number of days. I'd prefer something that didn't require a loop. Any ideas? (Must be supported in SQL Server 2005+)

Given what I think you're trying to get, this should do it:
SET DATEFIRST 1
DECLARE
#start_date DATETIME,
#end_date DATETIME
SET #start_date = '2011-07-11'
SET #end_date = '2011-07-22'
;WITH Days_Of_The_Week AS (
SELECT 1 AS day_number, 'Monday' AS day_name UNION ALL
SELECT 2 AS day_number, 'Tuesday' AS day_name UNION ALL
SELECT 3 AS day_number, 'Wednesday' AS day_name UNION ALL
SELECT 4 AS day_number, 'Thursday' AS day_name UNION ALL
SELECT 5 AS day_number, 'Friday' AS day_name UNION ALL
SELECT 6 AS day_number, 'Saturday' AS day_name UNION ALL
SELECT 7 AS day_number, 'Sunday' AS day_name
)
SELECT
day_name,
1 + DATEDIFF(wk, #start_date, #end_date) -
CASE WHEN DATEPART(weekday, #start_date) > day_number THEN 1 ELSE 0 END -
CASE WHEN DATEPART(weekday, #end_date) < day_number THEN 1 ELSE 0 END
FROM
Days_Of_The_Week

I'm not sure what the OP is after, this will give a count per weeks day:
SET DATEFIRST 1
DECLARE #StartDate datetime
,#EndDate datetime
SELECT #StartDate='7/13/2011'
,#EndDate='7/28/2011'
;with AllDates AS
(
SELECT #StartDate AS DateOf, datename(weekday,#StartDate) AS WeekDayName, datepart(weekday,#StartDate) AS WeekDayNumber
UNION ALL
SELECT DateOf+1, datename(weekday,DateOf+1), datepart(weekday,DateOf+1)
FROM AllDates
WHERE DateOf<#EndDate
)
SELECT COUNT(*) CountOf,WeekDayName FROM AllDates GROUP BY WeekDayName,WeekDayNumber ORDER BY WeekDayNumber
OUTPUT:
CountOf WeekDayName
----------- ------------------------------
2 Monday
2 Tuesday
3 Wednesday
3 Thursday
2 Friday
2 Saturday
2 Sunday
(7 row(s) affected)
this will give a count of Monday to Friday days:
SET DATEFIRST 1
DECLARE #StartDate datetime
,#EndDate datetime
SELECT #StartDate='7/13/2011'
,#EndDate='7/28/2011'
;with AllDates AS
(
SELECT #StartDate AS DateOf, datepart(weekday,#StartDate) AS WeekDayNumber
UNION ALL
SELECT DateOf+1, datepart(weekday,DateOf+1)
FROM AllDates
WHERE DateOf<#EndDate
)
SELECT COUNT(*) AS WeekDayCount FROM AllDates WHERE WeekDayNumber<=5
OUTPUT:
WeekDayCount
------------
12
(1 row(s) affected)
If you have a holiday table, you can join it in and remove those as well. Here is a slightly different version that may preform better:
SET DATEFIRST 1
DECLARE #StartDate datetime
,#EndDate datetime
SELECT #StartDate='7/13/2011'
,#EndDate='7/28/2011'
;with AllDates AS
(
SELECT #StartDate AS DateOf, datepart(weekday,getdate()) AS WeekDayNumber
UNION ALL
SELECT DateOf+1, (WeekDayNumber+1) % 7
FROM AllDates
WHERE DateOf<#EndDate
)
SELECT COUNT(*) AS WeekDayCount FROM AllDates WHERE WeekDayNumber>0 AND WeekDayNumber<6
--I don't like using "BETWEEN", ">", ">=", "<", and "<=" are more explicit in defining end points
produces same output as original query.

This assume standard settings but cans be adapted
DECLARE #StartDate datetime, #EndDate datetime
SELECT #StartDate='20110601', #EndDate='20110630'
;WITH AllDates AS
(
SELECT #StartDate AS DateOf, datepart(weekday, #StartDate) AS WeekDayNumber
UNION ALL
SELECT DateOf+1, datepart(weekday, DateOf+1)
FROM AllDates
WHERE DateOf < #EndDate
)
SELECT SUM(CASE WHEN WeekDayNumber BETWEEN 2 AND 6 THEN 1 ELSE 0 END) AS WeekDayCount
FROM AllDates
OPTION (MAXRECURSION 0)

This should be valid for SQL Server, and should be internationlization safe (note: I do not have a server to test this against).
SELECT datediff(day, #start, #end) - datediff(week, #start, #end) * 2
- CASE WHEN datepart(weekday, #start)
IN (datepart(weekday, '1970-01-03'),
datepart(weekday, '1970-01-04'))
THEN 1
ELSE 0 END,
- CASE WHEN datepart(weekday, #end)
IN (datepart(weekday, '1970-01-03'),
datepart(weekday, '1970-01-04'))
THEN 1
ELSE 0 END
Give that a whirl.
Given the clarification, this should get the number of each of the days.
Uses no recursion, and should be completely international-safe. You will have to adjust start/end date parameters for inclusion/exclusion as necessary (The DB2 version I was using to check this excluded the start date, but included the end date, for example).
WITH dayOfWeek (name, dayNumber) as (VALUES(dayname(weekday, '1970-01-01'), daypart(weekday, '1970-01-01')),
(dayname(weekday, '1970-01-02'), daypart(weekday, '1970-01-02')),
(dayname(weekday, '1970-01-03'), daypart(weekday, '1970-01-03')),
(dayname(weekday, '1970-01-04'), daypart(weekday, '1970-01-04')),
(dayname(weekday, '1970-01-05'), daypart(weekday, '1970-01-05')),
(dayname(weekday, '1970-01-06'), daypart(weekday, '1970-01-06')),
(dayname(weekday, '1970-01-07'), daypart(weekday, '1970-01-07')))
SELECT name, dayNumber, datediff(weeks, #start, #end)
+ CASE WHEN datepart(weekday, #end) >= dayNumber THEN 1 ELSE 0 END
- CASE WHEN datepart(weekday, #start) >= dayNumber THEN 1 ELSE 0 END
FROM dayOfWeek
Does that help any?

you can use DATEDIFF and DATEPART functions + some basic math to get the desired result without looping.

I would have simply added as a comment to the marked answer, but do not have enough "reputation". Instead of hardcoding the day_number which is dependent on datefirst, you could set it by discovering the weekday for each day of the week:
;WITH Days_Of_The_Week AS (
SELECT DATEPART(dw, '2007-01-01') AS day_number, 'Monday' AS day_name UNION ALL -- 2007-01-01 is a known Monday
SELECT DATEPART(dw, '2007-01-02') AS day_number, 'Tuesday' AS day_name UNION ALL
SELECT DATEPART(dw, '2007-01-03') AS day_number, 'Wednesday' AS day_name UNION ALL
SELECT DATEPART(dw, '2007-01-04') AS day_number, 'Thursday' AS day_name UNION ALL
SELECT DATEPART(dw, '2007-01-05') AS day_number, 'Friday' AS day_name UNION ALL
SELECT DATEPART(dw, '2007-01-06') AS day_number, 'Saturday' AS day_name UNION ALL
SELECT DATEPART(dw, '2007-01-07') AS day_number, 'Sunday' AS day_name
)

#start_date date = '2017-08-11',
#end_date date = '2017-08-27',
#weekday int = 7,
#count int output
As
Begin
Declare #i int = 0
set #count = 0
while(#i <= (select Datediff(Day, #start_date, #end_date)))
begin
if(Dateadd(Day, #i, #start_date) > #end_date)
break
if(Datepart(weekday, Dateadd(Day, #i, #start_date)) = #weekday)
set #count += 1
set #i += 1
end
select #count

Related

SQL Query to find the past 3rd business day excluding holidays and weekends

I am trying to write the sql query to find the past 3rd business day excluding the holidays and weekends. Below is my approachs but it's not giving the correct solution. Please suggest
First approach:
alter function getdate(#givendate datetime)
returns datetime
AS
BEGIN
declare #calcdate datetime
declare #reqdate datetime
declare #count bit
set #count=1
select #calcdate=#givendate-4;
select #reqdate=#calcdate;
while(#calcdate<#givendate)
begin
while(#count=1)
begin
if exists(select 1 from dbo.holidays with(Nolock) where dbo.holidays.date=#reqdate)
begin
select #reqdate=#reqdate-1
end
else
begin
set #count=0
end
end
select #calcdate=#calcdate+1
end
return #reqdate
END
Second approach:
alter function dbo.getpast3day (#date date)
returns datetime
As
begin
declare #reqdate datetime;
declare #Newyears_day datetime;
declare #Martin_Luther_Kin_Birthday datetime;
declare #presidents_day datetime;
declare #Memorial_day datetime;
declare #Independence_day datetime;
declare #Labour_day datetime;
declare #Columbus_day datetime;
declare #Veterans_day datetime;
declare #Thanksgiving_day datetime;
declare #Christmas_day datetime;
SELECT #reqdate= DATEADD(DAY, CASE (DATEPART(WEEKDAY, #date) + ##DATEFIRST) % 7
WHEN 1 THEN -5
WHEN 2 THEN -6
WHEN 3 THEN -6
WHEN 4 THEN -6
WHEN 5 THEN -6
ELSE -4
END, DATEDIFF(DAY, 0, #date)) ;
--#Martin_Luther_Kin_Birthday
begin
WITH dates AS (
SELECT DATEADD(day, number, #date) AS theDate
FROM master.dbo.spt_values
WHERE type = 'P'
)
SELECT #Martin_Luther_Kin_Birthday=theDate
FROM dates
WHERE DATEDIFF(day, '19000101', theDate) % 7 = 0
AND DATEPART(day, thedate)>=15 and DATEPART(day, thedate)<=21 and DATEPART(month,thedate)=1 and datepart(year,thedate)= year(#date);
end
--#presidents_day
begin
WITH dates AS (
SELECT DATEADD(day, number, #date) AS theDate
FROM master.dbo.spt_values
WHERE type = 'P'
)
SELECT #presidents_day=theDate
FROM dates
WHERE DATEDIFF(day, '19000101', theDate) % 7 = 0
AND DATEPART(day, thedate)>=15 and DATEPART(day, thedate)<=21 and DATEPART(month,thedate)=2 and datepart(year,thedate)= year(#date);
end
--#Memorial_day
begin
WITH dates AS (
SELECT DATEADD(day, number, #date) AS theDate
FROM master.dbo.spt_values
WHERE type = 'P'
)
SELECT #Memorial_day=theDate
FROM dates
WHERE DATEDIFF(day, '19000101', theDate) % 7 = 0
AND DATEPART(day, thedate)>=22 and DATEPART(day, thedate)<=31 and DATEPART(month,thedate)=2 and datepart(year,thedate)= year(#date);
end
--#Labour_day
begin
WITH dates AS (
SELECT DATEADD(day, number, #date) AS theDate
FROM master.dbo.spt_values
WHERE type = 'P'
)
SELECT #Labour_day=theDate
FROM dates
WHERE DATEDIFF(day, '19000101', theDate) % 7 = 0
AND DATEPART(day, thedate)>=1 and DATEPART(day, thedate)<=7 and DATEPART(month,thedate)=9 and datepart(year,thedate)= year(#date);
end
--#Columbus_day
begin
WITH dates AS (
SELECT DATEADD(day, number, #date) AS theDate
FROM master.dbo.spt_values
WHERE type = 'P'
)
SELECT #Columbus_day=theDate
FROM dates
WHERE DATEDIFF(day, '19000101', theDate) % 7 = 0
AND DATEPART(day, thedate)>=8 and DATEPART(day, thedate)<=14 and DATEPART(month,thedate)=10 and datepart(year,thedate)= year(#date);
end
--#Thanksgiving_day
begin
WITH dates AS (
SELECT DATEADD(day, number, #date) AS theDate
FROM master.dbo.spt_values
WHERE type = 'P'
)
SELECT #Thanksgiving_day=theDate
FROM dates
WHERE DATEDIFF(day, '19000101', theDate) % 7 = 0
AND DATEPART(day, thedate)>=22 and DATEPART(day, thedate)<=31 and DATEPART(month,thedate)=11 and datepart(year,thedate)= year(#date);
end
if (#reqdate=#Martin_Luther_Kin_Birthday or #reqdate=#presidents_day or #reqdate=#Memorial_day or #reqdate=#Labour_day or #reqdate=#Columbus_day or #reqdate=#Thanksgiving_day)
begin
select #reqdate= DATEADD(day,-6,#reqdate)
end
--RETURN '2216-09-20 00:00:00.000'
return #reqdate
end
Not 100% clear on what past 3rd business day is, but perhaps this can help. Clearly you will have to decide what a holiday is.
Select [dbo].[SomeFunctionName]('2016-01-20',3)
Returns
2016-01-15
The custom function is:
CREATE FUNCTION [dbo].[SomeFunctionName](#Date date,#nDay int)
Returns Date
As
Begin
Declare #RetVal Date
;with cteHolidays as (
Select * from (Values
('2016-01-01','New Year''s Day'),
('2016-01-18','Martin Luther King, Jr,'),
('2016-02-15','Washington''s Birthday'),
('2016-03-25','Good Friday'),
('2016-05-30','Memorial Day'),
('2016-07-04','Independence Day'),
('2016-09-05','Labor Day'),
('2016-11-24','Thanksgiving'),
('2016-11-25','Black Friday'),
('2016-12-26','Christmas Day')
) as Holidays (Date,Name)
), cteDays as (
Select *
,RowNr = Row_Number() over (Order By RetVal Desc)
From [dbo].[udf-Create-Range-Date](DateAdd(DD,-3*#nDay,#Date),#Date,'DD',1)
Where RetVal not in (Select Date from cteHolidays)
and DatePart(DW,RetVal) between 2 and 6
)
Select #RetVal=RetVal
From cteDays
Where RowNr=#nDay
Return #RetVal
End
It does require a generic helper function which I use to create dynamic date ranges
The helper function
CREATE FUNCTION [dbo].[udf-Create-Range-Date] (#DateFrom datetime,#DateTo datetime,#DatePart varchar(10),#Incr int)
Returns
#ReturnVal Table (RetVal datetime)
As
Begin
With DateTable As (
Select DateFrom = #DateFrom
Union All
Select Case #DatePart
When 'YY' then DateAdd(YY, #Incr, df.dateFrom)
When 'QQ' then DateAdd(QQ, #Incr, df.dateFrom)
When 'MM' then DateAdd(MM, #Incr, df.dateFrom)
When 'WK' then DateAdd(WK, #Incr, df.dateFrom)
When 'DD' then DateAdd(DD, #Incr, df.dateFrom)
When 'HH' then DateAdd(HH, #Incr, df.dateFrom)
When 'MI' then DateAdd(MI, #Incr, df.dateFrom)
When 'SS' then DateAdd(SS, #Incr, df.dateFrom)
End
From DateTable DF
Where DF.DateFrom < #DateTo
)
Insert into #ReturnVal(RetVal) Select DateFrom From DateTable option (maxrecursion 32767)
Return
End
-- Syntax Select * from [dbo].[udf-Create-Range-Date]('2016-10-01','2020-10-01','YY',1)
-- Syntax Select * from [dbo].[udf-Create-Range-Date]('2016-10-01','2020-10-01','DD',1)
-- Syntax Select * from [dbo].[udf-Create-Range-Date]('2016-10-01','2016-10-31','MI',15)
-- Syntax Select * from [dbo].[udf-Create-Range-Date]('2016-10-01','2016-10-02','SS',1)
EDIT
To validate the example listed above
Date Weekday BusDayNr
2016-01-20 Wednesday 1 << Selected Date
2016-01-19 Tuesday 2
2016-01-18 Monday - << Holiday Martin Luther King, Jr
2016-01-17 Sunday -
2016-01-16 Saturday -
2016-01-15 Friday 3 << Date Returned
2016-01-14 Thursday
2016-01-13 Wednesday
2016-01-12 Tuesday
2016-01-11 Monday
here is another approach:
if you putt weekends and holidays into 1 table something like this should work:
declare #holidays_weekends table (
dates date)
insert into #holidays_weekends
values('2016-09-23'),
('2016-09-24'),
('2016-09-25')
declare
#date date = getdate()
select dateadd("dd", -3 -(
select count(*)
from #holidays_weekends
where dates in(
dateadd("dd",-1,#date),
dateadd("dd",-2,#date),
dateadd("dd",-3,#date),
dateadd("dd",-4,#date),
dateadd("dd",-5,#date),
dateadd("dd",-6,#date),
dateadd("dd",-7,#date))) ,#date) as '3rd_day'

DATEDIFF between two dates, excluding two specific days

In Dubai, the standard weekend runs from Friday - Saturday rather than the traditional Saturday / Sunday.
I am trying to calculate the amount of working days between two given dates, and this is the code I have;
DATEDIFF(dd, DATEADD(hour, DATEDIFF(hh, getdate(), getutcdate()), #StartDate),
DATEADD(hour, DATEDIFF(hh, getdate(), getutcdate()), #EndDate)+1)
-
(
(DATEDIFF(wk, #StartDate, #EndDate) * 2
+(CASE WHEN DATENAME(dw, #StartDate) = 'Saturday' then 1 else 0 end)
+(CASE WHEN DATENAME(dw, #EndDate) = 'Friday' then 1 else 0 end)
))
)
However it is calculating the wrong amount of days. For example;
When the start date is 02-03-2016 and the end date is 02-06-2016 the returns as 4, but it should be 2.
When the start date is 02-03-2016 and the end date is 02-07-2016 the result is 3 which is correct.
The code below calculates the working days from your examples correctly. You can wrap it in a scalar function if you want.
declare #from date, #to date;
set #from = '2016-02-03';
set #to = '2016-02-06';
with dates(date)
as
(
select #from
union all
select dateadd(dd, 1, [date])
from dates
where [date] < #to
)
select count(*) [working days]
from (
select date, case when datename(dw, [date]) in ('Friday', 'Saturday') then 0 else 1 end as working
from dates) as dt
where dt.working = 1

SQL Set Time to Midnight in function

I have This function
ALTER FUNCTION [General].[GetWeekEnding]
(
#Date DATETIME
)
RETURNS DATETIME
AS
BEGIN
-- Return the result of the function
RETURN (DATEADD(day, -1 - (DATEPART(dw, #Date) + ##DATEFIRST - 2) % 7, #Date) + 7)
END
I need to also set the time to 00:00:00.000 as well as finding the week ending of a provided date any thoughts?
I use a UDF for adding time components to dates e.g.
CREATE FUNCTION [dbo].[DateTimeAdd]
(
#datepart date,
#timepart time
)
RETURNS datetime2
AS
BEGIN
RETURN DATEADD(dd, DATEDIFF(dd, 0, #datepart), CAST(#timepart AS datetime2));
END
Then in your case you can use it like this:
SELECT dbo.DateTimeAdd(DATEADD (D, -1 * DatePart (DW, GetDate()) + 7, GetDate()), DATEADD(hh, 0, CAST(DATEADD(DAY, DATEDIFF(DAY, -1, GETDATE()), -1) AS TIME)))
Since SQL Server 2005, the best way to remove time is to cast to the date data type:
select cast(#Date as date)
I would suggest that you have your function return a date instead of a datetime.
To get to the week ending, you need a lookup table to map days of the week to integers. You can do this with datepart. However, that is subject to system settings. Here is a function:
Create FUNCTION [GetWeekEnding] (
#Date DATETIME,
#WeekEndingDOW varchar(10)
)
RETURNS DATE
AS
BEGIN
-- Return the result of the function
declare #newdate datetime;
with lookup as (
select 'Sunday' as dow, 0 as daynum union all
select 'Monday' as dow, 1 as daynum union all
select 'Tuesday' as dow, 2 as daynum union all
select 'Wednesday' as dow, 3 as daynum union all
select 'Thursday' as dow, 4 as daynum union all
select 'Friday' as dow, 5 as daynum union all
select 'Saturday' as dow, 6 as daynum
)
select #newdate = #Date - (select daynum from lookup where datename(dw, #date) = dow) + (select daynum from lookup where #WeekEndingDOW = dow);
select #newdate = (case when #newdate < #date then #newdate + 7 else #newdate end)
RETURN cast(#newdate as date)
END;
Note that this function does use datetime internally. For some reason that I cannot fathom, you can say "#date + 1" to mean "add one day to the date value" when #date is a datetime. However, this does not work when #date is a date. (This is also true of the dateadd function.)

How to get all the weekend dates of the current year in SQL?

I tried but could not get the right solution. I want an SQL query that lists all the weekend dates of the current year.
I tried this SQL query:
WITH hier(num, lvl) AS (
SELECT 0, 1
UNION ALL
SELECT 100, 1
UNION ALL
SELECT num + 1, lvl + 1
FROM hier
WHERE lvl < 100
)
SELECT lvl [Week],
convert(date,DATEADD(dw, -DATEPART(dw, DATEADD(wk,DATEDIFF(wk,0,'12/31/'+convert(nvarchar,YEAR(getdate()))), 0)+6 ),
DATEADD(wk, DATEDIFF(wk,0,'12/31/'+convert(nvarchar,YEAR(getdate()))), 0)+6 ) - num * 7,101) [End Date]
FROM hier a
where num < 52
ORDER BY [End Date] asc
Its output is like this:
Week End date
52 2012-01-14
51 2012-01-21
50 2012-01-28
49 2012-02-04
I want the dates to start from the beginning – so, the above is missing one weekend, which is 2012-07-01. Also, I want the week numbers to show as 1, 2, 3... instead of 52, 51....
Check out this blog post.
Your question is explained in detail.
DECLARE #Year AS INT,
#FirstDateOfYear DATETIME,
#LastDateOfYear DATETIME
-- You can change #year to any year you desire
SELECT #year = 2010
SELECT #FirstDateOfYear = DATEADD(yyyy, #Year - 1900, 0)
SELECT #LastDateOfYear = DATEADD(yyyy, #Year - 1900 + 1, 0)
-- Creating Query to Prepare Year Data
;WITH cte AS (
SELECT 1 AS DayID,
#FirstDateOfYear AS FromDate,
DATENAME(dw, #FirstDateOfYear) AS Dayname
UNION ALL
SELECT cte.DayID + 1 AS DayID,
DATEADD(d, 1 ,cte.FromDate),
DATENAME(dw, DATEADD(d, 1 ,cte.FromDate)) AS Dayname
FROM cte
WHERE DATEADD(d,1,cte.FromDate) < #LastDateOfYear
)
SELECT FromDate AS Date, Dayname
FROM CTE
WHERE DayName IN ('Saturday','Sunday') -- For Weekend
/*
WHERE DayName LIKE 'Sunday'
WHERE DayName NOT IN ('Saturday','Sunday') -- For Weekday
WHERE DayName LIKE 'Monday' -- For Monday
WHERE DayName LIKE 'Sunday' -- For Sunday
*/
OPTION (MaxRecursion 370)
Will this help
DECLARE #startDate DATETIME, #endDate DATETIME
SELECT #startDate = '2012-01-01', #endDate = '2012-12-31'
;WITH Calender AS (
SELECT #startDate AS dt
UNION ALL
SELECT dt + 1 FROM Calender
WHERE dt + 1 <= #endDate
)
SELECT
dt
,NameMonth = DATENAME(Month, dt)
,NameDay = DATENAME (Weekday,dt)
,WeekofYr = DATEPART(WEEK, dt) FROM Calender
WHERE DATENAME (Weekday,dt) IN ('Sunday')
Option(MaxRecursion 0)
Result(Partial)
dt NameMonth NameDay WeekofYr
2012-01-01 00:00:00.000 January Sunday 1
2012-01-08 00:00:00.000 January Sunday 2
...............................................
...............................................
2012-12-30 00:00:00.000 December Sunday 53
you can try this
DECLARE #FirstDateOfYear DATETIME
SET #FirstDateOfYear = ’2010-01-01′
SELECT DISTINCT DATEADD(d, number, #FirstDateOfYear),
CASE DATEPART(dw, DATEADD(d, number, #FirstDateOfYear))
WHEN 7 THEN ‘Saturday’
WHEN 1 THEN ‘Sunday’
ELSE ‘Work Day’
END
FROM master..spt_values
WHERE number BETWEEN 0 AND 364
AND (DATEPART(dw, DATEADD(d, number, #FirstDateOfYear)) = 1 OR DATEPART(dw, DATEADD(d, number, #FirstDateOfYear)) = 7)
ORDER BY DATEADD(d, number, #FirstDateOfYear)
Try to find the first Saturday by doing this:
Start on 2012-01-01
If it's not a Saturday, add a day
Goto 2
Then, into a temporary table, add that date and the following date (Sunday).
After that, loop the following:
Add 7 and 8 days to the last Saturday you found (you get the following Saturday and Sunday)
Check whether they are still in 2012
If they are, store them in temp table and goto 1
There may be more elegant ways, but that's my quick & dirty solution. As you didn't post any code of what you've tried, I'll leave the implementation up to you.
this also works
declare #dat datetime, #add int
set #dat = '20120101'
set #add = datepart(w,#dat)
set #add = 5 - #add -- friday
set #dat = dateadd(d,#add,#dat)
while #dat <= '20121231'
begin
print #dat
set #dat = dateadd(d,7,#dat)
end
;with AllDaysOfYear (Day) as (
select DATEADD(year,DATEDIFF(year,0,CURRENT_TIMESTAMP),0) --Jan 1st
union all
select DATEADD(day,1,Day) from AllDaysOfYear
where DATEPART(year,DATEADD(day,1,Day)) = DATEPART(year,CURRENT_TIMESTAMP)
)
select
ROW_NUMBER() OVER (ORDER BY Day) as WeekNo,
Day
from
AllDaysOfYear
where
DATEPART(weekday,Day) = DATEPART(weekday,'20120714')
option (maxrecursion 0)
First, generate a set of all of the days in the current year (AllDaysInYear). Then, select those whose weekday is a saturday. The value I've used ('20120714') isn't terribly important - it just has to be any saturday, from any year. I'm just using it to avoid needing to have particular DATEFIRST or language settings.
This query shows how to get the first day of this year and the first day of the next year in the first part. The first day of the next year is calculated once so as not to keep getting and comparing the year parts.
;WITH cte(TheDate,NextYear) AS
(
SELECT CAST(CONVERT(CHAR(4),GETDATE(),112)+'0101' AS DATETIME),
CAST(YEAR(GETDATE())*10000+10101 AS CHAR(8))
UNION ALL
SELECT DateAdd(d,1,TheDate),NextYear
FROM cte
WHERE DateAdd(d,1,TheDate)<NextYear
)
SELECT Week = DatePart(wk,TheDate),
TheDate
FROM cte
WHERE DateName(dw,TheDate) in ('Saturday')
ORDER BY TheDate
OPTION (MAXRECURSION 366)
with t as
(
select 1 b
union all
select 1 b
union all
select 1 b
union all
select 1 b
union all
select 1 b
union all
select 1 b
union all
select 1 b
union all
select 1 b
)
select * from
(
select
current_timestamp
-datepart(dy,current_timestamp)
+row_number() over (order by t.b) d
from t, t t1, t t2
) tmp
where datepart(yyyy,d)=datepart(yyyy,current_timestamp)
and
DATENAME(dw,d)='sunday'
DECLARE #Year AS INT
SELECT #Year = 2020
;WITH weekends AS (
SELECT DATEFROMPARTS(#Year, 1, 1) AS dt
UNION ALL
SELECT DATEADD(DAY, 1, dt)
FROM weekends
WHERE dt < DATEFROMPARTS(#Year, 12, 31)
)
SELECT dt, DATENAME(MONTH, dt), DATENAME(DW, dt)
FROM weekends
WHERE DATEPART(DW, dt) IN (1, 7)
OPTION(MaxRecursion 366)

Return number of weekdays between 2 dates in T-SQL

I have 2 dates and want to know how many weekdays (mon-fri) there are
e.g.
thu jan 1 20xx
fri jan 2 20xx
sat jan 3 20xx
sun jan 4 20xx
mon jan 5 20xx
jan 1, jan 5 would return 3
(can ignore public holidays)
Try
DateDiff(day, #DtA, #DtB) - 2 * DateDiff(Week, #DtA, #DtB)
this may not work exactly, but you can see the idea. Some slight modification will work.
Assuming the dates can't be more than five and a half years from each other (or use your own tally table instead of master..spt_values):
DECLARE #date1 datetime, #date2 datetime;
SET #date1 = '20110901';
SET #date2 = '20110905';
SELECT COUNT(*)
FROM (
SELECT
Date = DATEADD(day, number, #date1)
FROM master..spt_values
WHERE type = 'P'
AND number between 0 AND DATEDIFF(day, #date1, #date2)
) s
WHERE DATENAME(DW, Date) NOT IN ('Saturday', 'Sunday')
try this:
SET DATEFIRST 1
DECLARE #StartDate datetime
,#EndDate datetime
SELECT #StartDate='6/21/2011'
,#EndDate='6/28/2011'
;with AllDates AS
(
SELECT #StartDate AS DateOf, datepart(weekday,getdate()) AS WeekDayNumber
UNION ALL
SELECT DateOf+1, datepart(weekday,DateOf+1)
FROM AllDates
WHERE DateOf<#EndDate
)
SELECT COUNT(*) AS WeekDayCount FROM AllDates WHERE WeekDayNumber<=5
OUTPUT:
WeekDayCount
------------
6
(1 row(s) affected)
If you have a holiday table, you can join it in and remove those as well.
EDIT based on #Ross Watson comment:
SET DATEFIRST 1
DECLARE #StartDate datetime
,#EndDate datetime
SELECT #StartDate='6/21/2011'
,#EndDate='6/28/2011'
;with AllDates AS
(
SELECT #StartDate AS DateOf, datepart(weekday,getdate()) AS WeekDayNumber
UNION ALL
SELECT DateOf+1, (WeekDayNumber+1) % 7
FROM AllDates
WHERE DateOf<#EndDate
)
SELECT COUNT(*) AS WeekDayCount FROM AllDates WHERE WeekDayNumber>0 AND WeekDayNumber<6
--I don't like using "BETWEEN", ">", ">=", "<", and "<=" are more explicit in defining end points
produces same output as original query.
Try the following:
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #StartDate = '2011/06/01'
SET #EndDate = '2011/06/31'
SELECT
(DATEDIFF(dd, #StartDate, #EndDate) + 1) -
(DATEDIFF(wk, #StartDate, #EndDate) * 5) -
(
CASE
WHEN DATENAME(dw, #StartDate) in
('Sunday', 'Tuesday', 'Wednesday', 'Turesday', 'Saturday')
THEN 1
ELSE 0
END
) -
(
CASE
WHEN DATENAME(dw, #EndDate) in
('Sunday', 'Tuesday', 'Wednesday', 'Turesday', 'Saturday')
THEN 1
ELSE 0
END
)
Also see if this is helpful
http://blog.sqlauthority.com/2007/06/08/sql-server-udf-function-to-display-current-week-date-and-day-weekly-calendar/
This approach is limited to ~100 days due to recursion. This works for the date ranges i've tested. Same idea above, removed the math and simplified:
BEGIN
SET DATEFIRST 1
DECLARE #StartDate datetime
,#EndDate datetime
SELECT #StartDate='12/16/2015'
,#EndDate='1/8/2016'
;with AllDates AS
(
SELECT #StartDate AS DateOf
UNION ALL
SELECT DateOf+1
FROM AllDates
WHERE DateOf<#EndDate
)
SELECT COUNT(*) AS WeekDayCount
FROM
AllDates
WHERE
datepart(weekday,DateOf) between 1 AND 5
--SELECT DateOf [date], datepart(weekday,DateOf) [day]
--FROM
-- AllDates
--WHERE
-- datepart(weekday,DateOf) between 1 AND 5
END