What I need is a date for the next given day (Monday, Tuesday, Wed...) following today\'s date.
The user is allowed to select what day following they want and that i
1) Your solution uses a non-deterministic function: datepart(dw...)
. Because of this aspect, changing DATEFIRST
setting will gives different results. For example, you should try:
SET DATEFIRST 7;
your solution;
and then
SET DATEFIRST 1;
your solution;
2) Following solution is independent of DATEFIRST
/LANGUAGE
settings:
DECLARE @NextDayID INT = 0 -- 0=Mon, 1=Tue, 2 = Wed, ..., 5=Sat, 6=Sun
SELECT DATEADD(DAY, (DATEDIFF(DAY, @NextDayID, GETDATE()) / 7) * 7 + 7, @NextDayID) AS NextDay
Result:
NextDay
-----------------------
2013-09-23 00:00:00.000
This solution is based on following property of DATETIME
type:
Day 0 = 19000101
= Mon
Day 1 = 19000102
= Tue
Day 2 = 19000103
= Wed
...
Day 5 = 19000106
= Sat
Day 6 = 19000107
= Sun
So, converting INT value 0 to DATETIME gives 19000101
.
If you want to find the next Wednesday
then you should start from day 2 (19000103
/Wed
), compute days between day 2 and current day (20130921
; 41534 days), divide by 7 (in order to get number of full weeks; 5933 weeks), multiple by 7 (41531 fays; in order to get the number of days - full weeks between the first Wednesday
/19000103
and the last Wednesday
) and then add 7 days (one week; 41538 days; in order to get following Wednesday
). Add this number (41538 days) to the starting date: 19000103
.
Note: my current date is 20130921
.
Edit #1:
DECLARE @NextDayID INT;
SET @NextDayID = 1; -- Next Sunday
SELECT DATEADD(DAY, (DATEDIFF(DAY, ((@NextDayID + 5) % 7), GETDATE()) / 7) * 7 + 7, ((@NextDayID + 5) % 7)) AS NextDay
Result:
NextDay
-----------------------
2013-09-29 00:00:00.000
Note: my current date is 20130923
.
I made this as a function, in which the procedure uses available streamlined knowledge thus it's, I think, a robust solution.
CREATE FUNCTION [nilnul.time_._dated.date].[NextWeekday]
(
@nextWeekDay int -- sunday as firstday is 1.
)
RETURNS datetime
AS
BEGIN
declare @time datetime;
set @time=getdate();
declare @weekday int;
set @weekday = datepart(weekday, @time) ;
declare @diff int;
set @diff= @nextWeekDay-@weekday;
--modulo 7 bijectively
declare @moduloed int;
set @moduloed = case
when @diff <=0 then @diff+7
else @diff
end;
return dateadd(day, @moduloed, @time);
END
I think this is the best way of doing of finding the next Monday
CONVERT(VARCHAR(11),DateAdd(DAY,case
when (DateName(WEEKDAY, NextVisitDate) ='Tuesday') Then 6
when (DateName(WEEKDAY, NextVisitDate) ='Wednesday') Then 5
when (DateName(WEEKDAY, NextVisitDate) ='Thursday') Then 4
when (DateName(WEEKDAY, NextVisitDate) ='Friday') Then 3
when (DateName(WEEKDAY, NextVisitDate) ='Saturday') Then 2
when (DateName(WEEKDAY, NextVisitDate) ='Sunday') Then 1
else 0 end, DateAdd(DAY, DateDiff(DAY, 0, NextVisitDate), 0)),106) AS Monday,
It's an old question. But I'm sure that posting better solution worth it.
-- 0 = 1st Mon, 1 = 1st Tue, 2 = 1st Wed, ..., 5 = 1st Sat, 6 = 1st Sun
-- 7 = 2nd Mon, 8 = 2nd Tue, ...
declare @NextDayID int = 0, @Date date = getdate()
select cast (cast (
-- last Monday before [Date] inclusive, starting from 1900-01-01
datediff (day, @NextDayID % 7, @Date) / 7 * 7
-- shift on required number of days
+ @NextDayID + 7
as datetime) as date)
This solution is improved solution of @Bogdan Sahlean. It can operate @NextDayID that is greater than 6. So you can, for example, find 2nd Monday from today.
Following query shows that my solution works correctly.
select [Date]
, convert (char(5), [0], 10) as Mon1
, convert (char(5), [1], 10) as Tue1
, convert (char(5), [2], 10) as Wed1
, convert (char(5), [3], 10) as Thu1
, convert (char(5), [4], 10) as Fri1
, convert (char(5), [5], 10) as Sat1
, convert (char(5), [6], 10) as Sun1
, convert (char(5), [7], 10) as Mon2
, convert (char(5), [8], 10) as Tue2
from (
select [Date], NextDayID
, cast (cast (
datediff (day, NextDayID % 7, [Date]) / 7 * 7 -- last Monday before [Date] inclusive, starting from 1900-01-01
+ NextDayID + 7 -- shift on required number of days
as datetime) as date) as NextDay
from (
select datefromparts (2018, 5, dt) as [Date]
from (values(14),(15),(16),(17),(18),(19),(20))t_(dt)
) d
cross join (values(0),(1),(2),(3),(4),(5),(6),(7),(8))nd(NextDayID)
) t
pivot (
min (NextDay) for NextDayID in ([0], [1], [2], [3], [4], [5], [6], [7], [8])
) pvt
Result:
Date | Mon1 | Tue1 | Wed1 | Thu1 | Fri1 | Sat1 | Sun1 | Mon2 | Tue2
-----------+-------+-------+-------+-------+-------+-------+-------+-------+------
2018-05-14 | 05-21 | 05-15 | 05-16 | 05-17 | 05-18 | 05-19 | 05-20 | 05-28 | 05-22
2018-05-15 | 05-21 | 05-22 | 05-16 | 05-17 | 05-18 | 05-19 | 05-20 | 05-28 | 05-29
2018-05-16 | 05-21 | 05-22 | 05-23 | 05-17 | 05-18 | 05-19 | 05-20 | 05-28 | 05-29
2018-05-17 | 05-21 | 05-22 | 05-23 | 05-24 | 05-18 | 05-19 | 05-20 | 05-28 | 05-29
2018-05-18 | 05-21 | 05-22 | 05-23 | 05-24 | 05-25 | 05-19 | 05-20 | 05-28 | 05-29
2018-05-19 | 05-21 | 05-22 | 05-23 | 05-24 | 05-25 | 05-26 | 05-20 | 05-28 | 05-29
2018-05-20 | 05-21 | 05-22 | 05-23 | 05-24 | 05-25 | 05-26 | 05-27 | 05-28 | 05-29
This solution doesn't depend on @@datefirst
.
The following function enables to you generate the table on-the-fly...this is how I usually do it...I don't like the idea of a perm date table...seems unnecessary, but every person and situation are different :-)
CREATE function [dbo].[fxDateTable]
(
@begindate datetime = null
, @enddate datetime = null
)
RETURNS @dates TABLE
(
EventDate datetime primary key not null
)
as
begin
select @enddate = isnull(@enddate, getdate())
select @begindate = isnull(@begindate, dateadd(day, -3, @enddate))
insert @dates
select dateadd(day, number, @begindate)
from
(select distinct number from master.dbo.spt_values
where name is null
) n
where dateadd(day, number, @begindate) < @enddate
return
end
Try this: This will give the date for required weekday in a month.
declare @monthstartdate date='2020-01-01',@monthenddate date='2020-01-31',@weekday char(9)='thursday',@weeknum int=4
; with cte(N,WeekDayName_C,Date_C) as
(select 1,datename(WEEKDAY,@monthstartdate),@monthstartdate
union all
select n+1,datename(WEEKDAY,dateadd(day,n,@monthstartdate)),dateadd(day,n,@monthstartdate) from cte where n<31 and Date_C<=@monthenddate )
select * from (select *,ROW_NUMBER() over (partition by WeekDayName_C order by Date_C asc)Weeknum from cte)a
where WeekDayName_C=@weekday and Weeknum=@weeknum