'Get the last Wednesday from a given date

If a datetime field is passed, how do I determine when the last Wednesday was and set it to 10AM on that day?

Declare DateTime @datetime

For example:
if I pass in @datetime = '2022-Feb-7 5:00:00' then I should get 2022-Feb-2 10:00:00
if I pass in @datetime = '2022-Feb-10 16:00:00' then I should get 2022-Feb-9 10:00:00

I have an edge case here:
if the @datetime is Wednesday and the time is less than 10AM then it should set it to last Wednesday and if the time is greater than 10AM then it should set it current Wednesday 10AM.

For example:
if I pass in @datetime='2022-Feb-9 5:00:000' then I should get 2022-Feb-2 10:00:00
if I pass in @datetime = '2022-Feb-9 16:00:00' then I should get 2022-Feb-9 10:00:00



Solution 1:[1]

You can calculate the previous Wednesday 10 AM like this:

DECLARE @DateTime DATETIME = GETDATE();
DECLARE @LastWednesday DATETIME;

SET @LastWednesday = CAST(DATEADD(day, -(3+@@DATEFIRST+DATEPART(weekday,@DateTime-'10:00'))%7, 
CAST(@DateTime-'10:00' AS DATE)) AS DATETIME)+'10:00';

SELECT @DateTime, @LastWednesday;

Demo on db<>fiddle here

How it works

The 10 hours is first subtracted from the @DateTime to get the cutoff time effect on a Wednesday.
@DateTime-'10:00' So after 10 AM on a Wednesday it calculates current Wednesday.

Now, to calculate the first day of the week, you could subtract the weekday+1 from the date.
But the first day of the week depends on the DATEFIRST setting. If it's 1 then Monday, if 7 then it's Sunday. So the variable @@datefirst is used to normalize the calculation. So that the calculation doesn't depend on the DATEFIRST setting.
This will get the previous Saturday:
@datetime-(0+@@datefirst+DATEPART(weekday,@datetime))%7
So this gets the previous Wednesday:
@datetime-(3+@@datefirst+DATEPART(weekday,@datetime))%7
The modulus 7 (%7) makes sure that it can't subtract more than 6.

--
-- Test over date range
--
DECLARE @StartDatetime SMALLDATETIME;
DECLARE @EndDatetime SMALLDATETIME;
SET @StartDateTime = DATEADD(month, -1, DATEADD(day, 1, EOMONTH(GETDATE())));
SET @EndDatetime = EOMONTH(@StartDatetime);

with cte as (
  select @StartDatetime+'11:00' as [Dt]
  union all
  select dateadd(day, 1, [Dt]) from cte
  where [Dt] < @EndDatetime
)
select top 16
  [Dt] 
, datepart(weekday, [Dt]) AS weekday
, datename(weekday, [Dt]) AS weekdayname
, -(3+@@DATEFIRST+DATEPART(weekday,[Dt]-'10:00'))%7 AS daydiff
, prevWed = CAST(DATEADD(day,-(3+@@DATEFIRST+DATEPART(weekday,[Dt]-'10:00'))%7,CAST([Dt]-'10:00' AS DATE)) AS DATETIME)+'10:00'
, @@DATEFIRST AS df
from cte;
Dt weekday weekdayname daydiff prevWed df
2022-02-01 11:00 3 Tuesday -6 2022-01-26 10:00:00.000 7
2022-02-02 11:00 4 Wednesday 0 2022-02-02 10:00:00.000 7
2022-02-03 11:00 5 Thursday -1 2022-02-02 10:00:00.000 7
2022-02-04 11:00 6 Friday -2 2022-02-02 10:00:00.000 7
2022-02-05 11:00 7 Saturday -3 2022-02-02 10:00:00.000 7
2022-02-06 11:00 1 Sunday -4 2022-02-02 10:00:00.000 7
2022-02-07 11:00 2 Monday -5 2022-02-02 10:00:00.000 7
2022-02-08 11:00 3 Tuesday -6 2022-02-02 10:00:00.000 7
2022-02-09 11:00 4 Wednesday 0 2022-02-09 10:00:00.000 7
2022-02-10 11:00 5 Thursday -1 2022-02-09 10:00:00.000 7
2022-02-11 11:00 6 Friday -2 2022-02-09 10:00:00.000 7
2022-02-12 11:00 7 Saturday -3 2022-02-09 10:00:00.000 7
2022-02-13 11:00 1 Sunday -4 2022-02-09 10:00:00.000 7
2022-02-14 11:00 2 Monday -5 2022-02-09 10:00:00.000 7
2022-02-15 11:00 3 Tuesday -6 2022-02-09 10:00:00.000 7
2022-02-16 11:00 4 Wednesday 0 2022-02-16 10:00:00.000 7

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1