Finding Months Difference in Days - sql-server

I want to find the difference between two dates which should be exact in months like if the date difference is greater than 182 days them on 183rd Day it should show as 7 Months.I tried below one,
SELECT ROUND(cast(DATEDIFF(DD,'2018-01-01 18:45:30.203',GETDATE()) as float)/30,0)
but it has 15 days difference.

I wouldn’t use 30. It’s fail on some months. For example Jan 1 and March 2 since February doesn’t have at least 30 days. But I think this is what you are after. If the current day isn’t the first of the month then add a month.
SELECT
Case
when datepart(day,getdate()) > 1
Then datediff(month,'2018-01-01 18:45:30.203',GETDATE()) + 1
Else datediff(month,'2018-01-01 18:45:30.203',GETDATE())
End

I think that calculating the difference in months as an integer is very similar to calculating the age of a person. We can take the DATEDIFF in months, add this number of months to the first date and compare that to the second date to decide whether we have to subtract 1 from the difference:
DECLARE #Date1 datetime = '2018-01-01 18:45:30.203';
DECLARE #Date2 datetime = GETDATE();
SELECT
CASE
WHEN DATEADD(month, DATEDIFF(month, #Date1, #Date2), #Date1) > #Date2
THEN DATEDIFF(month, #Date1, #Date2) - 1
ELSE DATEDIFF(month, #Date1, #Date2)
END

Related

SQL Server Parameter Date Range Issues

I am using the following query to get the difference between two dates. The date ranges are tolling 12 months interval.
CY stand for Current year while PY stands for Previous Year. The dates in Current year are used to calculate the previous year dates
When I execute my query I have the following output, where the month is 11 and day 364. But I want my months to be twelve and the day 365 or (366 for leap year).
DECLARE #CY_StartDate date =CAST(DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE())-13, 0) AS DATE),
#CY_EndDate date =CAST(DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE())-1, -1) AS DATE); --- Rolling 12 months
DECLARE #PY_startDate date =DATEADD(YEAR,-1,#CY_StartDate),
#PY_EndDate date =DATEADD(YEAR,-1,#CY_EndDate)
SELECT
#CY_StartDate AS CY_Start,
#CY_EndDate AS CY_End,
#PY_StartDate AS PY_Start,
#PY_EndDate AS PY_End,
DATEDIFF(year, #CY_StartDate, #CY_EndDate) AS yr,
DATEDIFF(month, #CY_StartDate, #CY_EndDate) AS month,
DATEDIFF(day, #CY_StartDate, #CY_EndDate) AS day
Current Output
CY_Start CY_End PY_Start PY_End yr month day
2017-10-01 2018-09-30 2016-10-01 2017-09-30 1 11 364
Expected output
CY_Start CY_End PY_Start PY_End yr month day
2017-10-01 2018-09-30 2016-10-01 2017-09-30 1 12 365
The values you are getting make sense. DATEDIFF counts the ticks between 2 dates, where a tick is the value of the first parameter. So, for example: DATEDIFF(MONTH, '20180101','20180228') will return 1, as only 1 tick has occured (2 - 1 = 1). Seems, here, you simply need to add 1:
DECLARE #CY_StartDate date =CAST(DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE())-13, 0) AS DATE),
#CY_EndDate date =CAST(DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE())-1, -1) AS DATE); --- Rolling 12 months
DECLARE #PY_startDate date =DATEADD(YEAR,-1,#CY_StartDate),
#PY_EndDate date =DATEADD(YEAR,-1,#CY_EndDate)
select
#CY_StartDate as CY_Start,
#CY_EndDate AS CY_End,
#PY_StartDate AS PY_Start,
#PY_EndDate AS PY_End,
DATEDIFF(year,#CY_StartDate,DATEADD(DAY,1,#CY_EndDate)) as yr,
DATEDIFF(month,#CY_StartDate,DATEADD(DAY,1,#CY_EndDate)) as month,
DATEDIFF(day,#CY_StartDate,DATEADD(DAY,1,#CY_EndDate)) as day
The reason I used a further DATEADD is because this makes it consistent with every expression. The value of yr was correct, however, for dates like 20170101 and 20171231, the value of yr would be 0. Hence adding 1 day the the value of #CY_EndDate makes this far more reliable, should the dates move.
Common sense. How many numbers are there between 1 and 10 including both? You might say that there are 10 - 1 = 9 which is incorrect. The correct answer is (10 - 1) + 1 = 10.
Likewise, if you have two inclusive dates e.g. 2017-10-01 and 2018-09-30 you add one to DATEDIFF(DAY, '2017-10-01', '2018-09-30') to get 365 instead of 364.
However, as suggested in the other answer, it is much better to the end date exclusive (not counted) which makes date calculations straight forward. In your example, you should add 1 day to the last date so that you have [2017-10-01, 2018-10-01) and DATEDIFF will produce desired results.

Counting datediff from 2 date

I have 2 datetime fields, NEW_EMPLOYMENT_STARTDATE and NEW_EMPLOYMENT_ENDDATE
When I calculate the difference in months using datediff with these values for startdate and enddate:
NEW_EMPLOYMENT_STARTDATE = 2017-15-01
NEW_EMPLOYMENT_ENDDATE = 2018-14-01
With this query:
DATEDIFF(MONTH, NEW_EMPLOYMENT_STARTDATE, NEW_EMPLOYMENT_ENDDATE)
It returns 12 months, but when I have values like this:
NEW_EMPLOYMENT_STARTDATE = 2017-01-01
NEW_EMPLOYMENT_ENDDATE = 2017-31-12
It returns 11 months.
How can I get 12 months? I am using this query:
DATEDIFF(MONTH, NEW_EMPLOYMENT_STARTDATE, NEW_EMPLOYMENT_ENDDATE)-
CASE
WHEN DATEPART(DAY, NEW_EMPLOYMENT_ENDDATE) > DATEPART(DAY, NEW_EMPLOYMENT_STARTDATE)
THEN 0
ELSE 0 END AS MONTH_DIFF
It still returns 11 months.
EDIT:
According to my case, the value of the end date always less 1 day from start date, so i make a trick to check condition with case when like this:
CASE
WHEN DATEPART(DAY, NEW_EMPLOYMENT_ENDDATE) > DATEPART(DAY, NEW_EMPLOYMENT_STARTDATE)
THEN DATEDIFF(MONTH, NEW_EMPLOYMENT_STARTDATE, NEW_EMPLOYMENT_ENDDATE)+1
ELSE DATEDIFF(MONTH, NEW_EMPLOYMENT_STARTDATE, NEW_EMPLOYMENT_ENDDATE)
END AS DATEDIF
i add + 1 value to the end date so i ta can be round to next month, give feedback from my solution sir thanks
You're expectations are incorrect. When you do a DATEDIFF using MONTH, it does not consider the day portion of the dates. Consider that it is simply considering the difference in the month numbers only, regardless of the day specified.
This query:
SELECT DATEDIFF(MONTH, '20170101', '20171231') MonthsDiff
Is equivalent to this:
SELECT DATEPART(MONTH, '20171231') - DATEPART(MONTH, '20170101') MonthsDiff
The documentation for DATEDIFF states:
DATEDIFF ( datepart , startdate , enddate )
The first option is DATEPART:
datepart
Is the part of startdate and enddate that specifies the type of boundary crossed.
If you want something closer to what you expect, you can do a simple calculation based on performing the DATEDIFF in days, the dividing it by the approximate number of days in a month.
SELECT DATEDIFF(DAY, '20170101', '20171231') / ( 365 / 12 ) MonthsDiff
This will round the output to the closest month number, it all depends on how accurate you want to be. If you want months as a decimal for greater accuracy then run the below:
SELECT DATEDIFF(DAY, '20170101', '20171220') / ( 365.00 / 12 ) MonthsDiff
Note: This does not take into account leap years, for larger date ranges that might include leap years, which will make a minor difference to the accuracy.
datediff() does something very particular. It counts the number of "boundaries" between two date/time values. In your case, there are eleven boundaries -- one for each month in the year before December.
This behavior is not necessarily intuitive. If you add one day to each of your dates:
NEW_EMPLOYMENT_STARTDATE = '2017-01-02' (YYYY-MM-DD is standard format)
NEW_EMPLOYMENT_ENDDATE = '2018-01-01'
Then you will have 12 months.
If you want to round up, you can play with the dates. One method would be to normalize the first value to the beginning of the month and then add 15 days to the second value:
DATEDIFF(MONTH,
DATEADD(DAY, 1 - DAY(NEW_EMPLOYMENT_STARTDATE), NEW_EMPLOYMENT_STARTDATE)
DATEADD(DAY, 15 + 1 - DAY(NEW_EMPLOYMENT_STARTDATE), NEW_EMPLOYMENT_ENDDATE)
)
This would happen to work for the two examples you give.
Please use this select to achieve your desired result. You can use table columns instead of variables:
declare #new_employment_startdate datetime = convert (datetime, '2017-01-01', 121);
declare #new_employment_enddate datetime = convert (datetime, '2018-01-14', 121);
select
datediff(month, #new_employment_startdate, #new_employment_enddate)
+ case when
datediff(month, dateadd(ms, -3, dateadd(dd, datediff(dd, 0, #new_employment_startdate), 0)), #new_employment_startdate) = 1
and datediff(month,#new_employment_enddate , dateadd(dd, datediff(dd, 0, #new_employment_enddate) + 1, 0)) = 1
then 1
else 0
end;
Some explanations:
I check or start date is first month day AND end date is last month day. At this case I add +1 to standard datediff by month.
You can better understand my used datetime manipulations by using these example queries: https://gist.github.com/runnerlt/60636b029ab47845fdfd8924ed482e61
You need to add 1 more day in your End Date.
DATEDIFF(MONTH, NEW_EMPLOYMENT_STARTDATE, DATEADD(DD,1,NEW_EMPLOYMENT_ENDDATE))
You could match the output with MS Excel.

SQL Server : DATEDIFF not accurate

I'm trying to retrieve the month difference of two dates but it seems like I can't find a way to get the accurate months.
Here are the queries I tried so far :
SELECT DATEDIFF(month,convert(datetime, '11/05/2015'), convert(datetime, '12/06/2015')) - 1
This will result to 0 which is wrong and when I used another date :
SELECT DATEDIFF(month,convert(datetime, '12/31/2015'), convert(datetime, '01/01/2016')) - 1
This would yield to 0 which is correct.
Leap year must also be considered.
The TSQL is return the correct results as you have written them. As you have it, it is taking the difference of the months between the two dates specified.
SELECT DATEDIFF(month, convert(datetime, '11/05/2015'), convert(datetime, '12/06/2015'))
The difference, in months, between November and December is "1".
However, if you are wanting the difference in terms of every 30 days is 1 month, then you would need to rewrite your query:
declare #daysPerMonth int = 30
SELECT (DATEDIFF(day, convert(datetime, '11/05/2015'), convert(datetime, '12/06/2015')) / #daysPerMonth)
This works if you define the number of days in a month as 30.
Here is a way to visualize what DATEDIFF(month, ...) is doing. Think of a monthly calendar hanging on the wall. For the purposes of this particular SQL function "number" of months between two dates is the number of pages you have to flip to get from one date to the next. It doesn't matter how many days are in each month or whether it's a leap year.
You aren't the first person to be confused about the behavior of the built-in function. If you need to count months by a different method then you'll need to describe the specifics of what you want to accomplish. It's very likely that a solution will be easy to create once you define the problem that needs to be addressed.
In T-SQL you can use CASE:
SELECT DATEDIFF(month, convert(datetime, '11/05/2015'),
convert(datetime, '12/06/2015')) -
CASE
WHEN (MONTH('11/05/2015') > MONTH('12/06/2015')
OR MONTH('11/05/2015') = MONTH('12/06/2015')
AND DAY('11/05/2015') > DAY('12/06/2015'))
THEN 1 ELSE 0
END
Which returns 1 which is correct and:
SELECT DATEDIFF(month, convert(datetime, '12/31/2015'),
convert(datetime, '01/01/2016')) -
CASE
WHEN (MONTH('12/31/2015') > MONTH('01/01/2016')
OR MONTH('12/31/2015') = MONTH('01/01/2016')
AND DAY('12/31/2015') > DAY('01/01/2016'))
THEN 1 ELSE 0
END
Which returns 0 which is correct.
declare
#start date = '20220105',
#end date = '20220406'
select 'Date_Diff_In_3 months' =
case
when datediff(month, #start, #end) < 3
or (datediff(month, #start, #end) = 3 and day(#start) >= day(#end))
then 'yes'
else 'no'
end

SQL Server : get day of month from Year Month WeekOfMonth

I have a complicated calendar report coming from a SQL query where I have the year, month and WeekNumOfMonth (meaning the week number from the beginning of the month).
I need a function to return the DayOfMonth (ie: values of between 1-28,29,30 or 31 depending on the month) given Year, month and WeekNumOfMonth.
I have seen functions that do this given WeekNum from beginning of year but I only have WeekNum of the given month. I just can't seem to give the datePart and dateAdd functions right. Any help would be appreciated.
Note : Combination of Year ,Month ,Week Number will not give you the
day name because a week contain more than one day so how can you find
which day is it ? it is not possible
example : year 2015 month 06 week number 1
then it has a combination of
1 monday
2 tuesday
3 wednesday
4 thursday
5 friday
6 saturday
then with these results how can you predit the exact day ??
Else need some conditions or restrictions
If you have Year, Month , and date then you can find the day name using the below query it will give the start and end date of the week then you can take the dates from start date and end with end date
SELECT (
DATENAME(dw,
CAST(DATEPART(m, GETDATE()) AS VARCHAR)
+ '/'
+ CAST(DATEPART(d, '2015-06-23') AS VARCHAR)
+ '/'
+ CAST(DATEPART(yy, getdate()) AS VARCHAR))
)
Update
If you need all days between the current week then use the following query
DECLARE
#Year INT,
#Week INT,
#FirstDayOfYear DATETIME,
#FirstMondayOfYear DATETIME,
#StartDate DATETIME,
#EndDate DATETIME
SET #Year = 2015
SET #Week = 24// week number is based on year and it is not based on month
-- Get the first day of the provided year.
SET #FirstDayOfYear = CAST('1/1/' + CAST(#YEAR AS VARCHAR) AS DATETIME)
-- Get the first monday of the year, then add the number of weeks.
SET #FirstMondayOfYear = DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(DAY, 6 - DATEPART(DAY, #FirstDayOfYear), #FirstDayOfYear)), 0)
SET #StartDate = DATEADD(WEEK, #Week - 1, #FirstMondayOfYear)
-- Set the end date to one week past the start date.
SET #EndDate = DATEADD(WEEK, 1, #StartDate)
SELECT #StartDate AS StartDate, DATEADD(SECOND, -1, #EndDate) AS EndDate
Update
if do not have the year week number then use the below query
DATEPART(WEEK,'2015-06-01')
The above query is one example
In the query you have two values that is Year and Month
you need only the third value that means date
as a technique you can give 01 as the date then it will give the
starting week number your selected month
here it will result 23 so you got the idea in 2015 - month 06 starts with week number 23 so then you can easily do your stuff
Idea : if your week number is 1 then take 23 as week if it is 2 then 24 like wise..

Get first day of week in SQL Server

I am trying to group records by week, storing the aggregated date as the first day of the week. However, the standard technique I use for rounding off dates does not appear to work correctly with weeks (though it does for days, months, years, quarters and any other timeframe I've applied it to).
Here is the SQL:
select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), 0);
This returns 2011-08-22 00:00:00.000, which is a Monday, not a Sunday. Selecting ##datefirst returns 7, which is the code for Sunday, so the server is setup correctly in as far as I know.
I can bypass this easily enough by changing the above code to:
select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), -1);
But the fact that I have to make such an exception makes me a little uneasy. Also, apologies if this is a duplicate question. I found some related questions but none that addressed this aspect specifically.
To answer why you're getting a Monday and not a Sunday:
You're adding a number of weeks to the date 0. What is date 0? 1900-01-01. What was the day on 1900-01-01? Monday. So in your code you're saying, how many weeks have passed since Monday, January 1, 1900? Let's call that [n]. Ok, now add [n] weeks to Monday, January 1, 1900. You should not be surprised that this ends up being a Monday. DATEADD has no idea that you want to add weeks but only until you get to a Sunday, it's just adding 7 days, then adding 7 more days, ... just like DATEDIFF only recognizes boundaries that have been crossed. For example, these both return 1, even though some folks complain that there should be some sensible logic built in to round up or down:
SELECT DATEDIFF(YEAR, '2010-01-01', '2011-12-31');
SELECT DATEDIFF(YEAR, '2010-12-31', '2011-01-01');
To answer how to get a Sunday:
If you want a Sunday, then pick a base date that's not a Monday but rather a Sunday. For example:
DECLARE #dt DATE = '1905-01-01';
SELECT [start_of_week] = DATEADD(WEEK, DATEDIFF(WEEK, #dt, CURRENT_TIMESTAMP), #dt);
This will not break if you change your DATEFIRST setting (or your code is running for a user with a different setting) - provided that you still want a Sunday regardless of the current setting. If you want those two answers to jive, then you should use a function that does depend on the DATEFIRST setting, e.g.
SELECT DATEADD(DAY, 1-DATEPART(WEEKDAY, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP);
So if you change your DATEFIRST setting to Monday, Tuesday, what have you, the behavior will change. Depending on which behavior you want, you could use one of these functions:
CREATE FUNCTION dbo.StartOfWeek1 -- always a Sunday
(
#d DATE
)
RETURNS DATE
AS
BEGIN
RETURN (SELECT DATEADD(WEEK, DATEDIFF(WEEK, '19050101', #d), '19050101'));
END
GO
...or...
CREATE FUNCTION dbo.StartOfWeek2 -- always the DATEFIRST weekday
(
#d DATE
)
RETURNS DATE
AS
BEGIN
RETURN (SELECT DATEADD(DAY, 1-DATEPART(WEEKDAY, #d), #d));
END
GO
Now, you have plenty of alternatives, but which one performs best? I'd be surprised if there would be any major differences but I collected all the answers provided so far and ran them through two sets of tests - one cheap and one expensive. I measured client statistics because I don't see I/O or memory playing a part in the performance here (though those may come into play depending on how the function is used). In my tests the results are:
"Cheap" assignment query:
Function - client processing time / wait time on server replies / total exec time
Gandarez - 330/2029/2359 - 0:23.6
me datefirst - 329/2123/2452 - 0:24.5
me Sunday - 357/2158/2515 - 0:25.2
trailmax - 364/2160/2524 - 0:25.2
Curt - 424/2202/2626 - 0:26.3
"Expensive" assignment query:
Function - client processing time / wait time on server replies / total exec time
Curt - 1003/134158/135054 - 2:15
Gandarez - 957/142919/143876 - 2:24
me Sunday - 932/166817/165885 - 2:47
me datefirst - 939/171698/172637 - 2:53
trailmax - 958/173174/174132 - 2:54
I can relay the details of my tests if desired - stopping here as this is already getting quite long-winded. I was a bit surprised to see Curt's come out as the fastest at the high end, given the number of calculations and inline code. Maybe I'll run some more thorough tests and blog about it... if you guys don't have any objections to me publishing your functions elsewhere.
For these that need to get:
Monday = 1 and Sunday = 7:
SELECT 1 + ((5 + DATEPART(dw, GETDATE()) + ##DATEFIRST) % 7);
Sunday = 1 and Saturday = 7:
SELECT 1 + ((6 + DATEPART(dw, GETDATE()) + ##DATEFIRST) % 7);
Above there was a similar example, but thanks to double "%7" it would be much slower.
For those who need the answer at work and creating function is forbidden by your DBA, the following solution will work:
select *,
cast(DATEADD(day, -1*(DATEPART(WEEKDAY, YouDate)-1), YourDate) as DATE) as WeekStart
From.....
This gives the start of that week. Here I assume that Sundays are the start of weeks. If you think that Monday is the start, you should use:
select *,
cast(DATEADD(day, -1*(DATEPART(WEEKDAY, YouDate)-2), YourDate) as DATE) as WeekStart
From.....
This works wonderfully for me:
CREATE FUNCTION [dbo].[StartOfWeek]
(
#INPUTDATE DATETIME
)
RETURNS DATETIME
AS
BEGIN
-- THIS does not work in function.
-- SET DATEFIRST 1 -- set monday to be the first day of week.
DECLARE #DOW INT -- to store day of week
SET #INPUTDATE = CONVERT(VARCHAR(10), #INPUTDATE, 111)
SET #DOW = DATEPART(DW, #INPUTDATE)
-- Magic convertion of monday to 1, tuesday to 2, etc.
-- irrespect what SQL server thinks about start of the week.
-- But here we have sunday marked as 0, but we fix this later.
SET #DOW = (#DOW + ##DATEFIRST - 1) %7
IF #DOW = 0 SET #DOW = 7 -- fix for sunday
RETURN DATEADD(DD, 1 - #DOW,#INPUTDATE)
END
Maybe you need this:
SELECT DATEADD(DD, 1 - DATEPART(DW, GETDATE()), GETDATE())
Or
DECLARE #MYDATE DATETIME
SET #MYDATE = '2011-08-23'
SELECT DATEADD(DD, 1 - DATEPART(DW, #MYDATE), #MYDATE)
Function
CREATE FUNCTION [dbo].[GetFirstDayOfWeek]
( #pInputDate DATETIME )
RETURNS DATETIME
BEGIN
SET #pInputDate = CONVERT(VARCHAR(10), #pInputDate, 111)
RETURN DATEADD(DD, 1 - DATEPART(DW, #pInputDate),
#pInputDate)
END
GO
Googled this script:
create function dbo.F_START_OF_WEEK
(
#DATE datetime,
-- Sun = 1, Mon = 2, Tue = 3, Wed = 4
-- Thu = 5, Fri = 6, Sat = 7
-- Default to Sunday
#WEEK_START_DAY int = 1
)
/*
Find the fisrt date on or before #DATE that matches
day of week of #WEEK_START_DAY.
*/
returns datetime
as
begin
declare #START_OF_WEEK_DATE datetime
declare #FIRST_BOW datetime
-- Check for valid day of week
if #WEEK_START_DAY between 1 and 7
begin
-- Find first day on or after 1753/1/1 (-53690)
-- matching day of week of #WEEK_START_DAY
-- 1753/1/1 is earliest possible SQL Server date.
select #FIRST_BOW = convert(datetime,-53690+((#WEEK_START_DAY+5)%7))
-- Verify beginning of week not before 1753/1/1
if #DATE >= #FIRST_BOW
begin
select #START_OF_WEEK_DATE =
dateadd(dd,(datediff(dd,#FIRST_BOW,#DATE)/7)*7,#FIRST_BOW)
end
end
return #START_OF_WEEK_DATE
end
go
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=47307
CREATE FUNCTION dbo.fnFirstWorkingDayOfTheWeek
(
#currentDate date
)
RETURNS INT
AS
BEGIN
-- get DATEFIRST setting
DECLARE #ds int = ##DATEFIRST
-- get week day number under current DATEFIRST setting
DECLARE #dow int = DATEPART(dw,#currentDate)
DECLARE #wd int = 1+(((#dow+#ds) % 7)+5) % 7 -- this is always return Mon as 1,Tue as 2 ... Sun as 7
RETURN DATEADD(dd,1-#wd,#currentDate)
END
For the basic (the current week's Sunday)
select cast(dateadd(day,-(datepart(dw,getdate())-1),getdate()) as date)
If previous week:
select cast(dateadd(day,-(datepart(dw,getdate())-1),getdate()) -7 as date)
Internally, we built a function that does it but if you need quick and dirty, this will do it.
Since Julian date 0 is a Monday just add the number of weeks to Sunday
which is the day before -1 Eg. select dateadd(wk,datediff(wk,0,getdate()),-1)
I found some of the other answers long-winded or didn't actually work if you wanted Monday as the start of the week.
Sunday
SELECT DATEADD(week, DATEDIFF(week, -1, GETDATE()), -1) AS Sunday;
Monday
SELECT DATEADD(week, DATEDIFF(week, 0, GETDATE() - 1), 0) AS Monday;
Set DateFirst 1;
Select
Datepart(wk, TimeByDay) [Week]
,Dateadd(d,
CASE
WHEN Datepart(dw, TimeByDay) = 1 then 0
WHEN Datepart(dw, TimeByDay) = 2 then -1
WHEN Datepart(dw, TimeByDay) = 3 then -2
WHEN Datepart(dw, TimeByDay) = 4 then -3
WHEN Datepart(dw, TimeByDay) = 5 then -4
WHEN Datepart(dw, TimeByDay) = 6 then -5
WHEN Datepart(dw, TimeByDay) = 7 then -6
END
, TimeByDay) as StartOfWeek
from TimeByDay_Tbl
This is my logic. Set the first of the week to be Monday then calculate what is the day of the week a give day is, then using DateAdd and Case I calculate what the date would have been on the previous Monday of that week.
This is a useful function for me
/* MeRrais 211126
select [dbo].[SinceWeeks](0,NULL)
select [dbo].[SinceWeeks](5,'2021-08-31')
*/
alter Function [dbo].[SinceWeeks](#Weeks int, #From datetime=NULL)
Returns date
AS
Begin
if #From is null
set #From=getdate()
return cast(dateadd(day, -(#Weeks*7+datepart(dw,#From)-1), #From) as date)
END
I don't have any issues with any of the answers given here, however I do think mine is a lot simpler to implement, and understand. I have not run any performance tests on it, but it should be neglegable.
So I derived my answer from the fact that dates are stored in SQL server as integers, (I am talking about the date component only). If you don't believe me, try this SELECT CONVERT(INT, GETDATE()), and vice versa.
Now knowing this, you can do some cool math equations. You might be able to come up with a better one, but here is mine.
/*
TAKEN FROM http://msdn.microsoft.com/en-us/library/ms181598.aspx
First day of the week is
1 -- Monday
2 -- Tuesday
3 -- Wednesday
4 -- Thursday
5 -- Friday
6 -- Saturday
7 (default, U.S. English) -- Sunday
*/
--Offset is required to compensate for the fact that my ##DATEFIRST setting is 7, the default.
DECLARE #offSet int, #testDate datetime
SELECT #offSet = 1, #testDate = GETDATE()
SELECT CONVERT(DATETIME, CONVERT(INT, #testDate) - (DATEPART(WEEKDAY, #testDate) - #offSet))
I had a similar problem. Given a date, I wanted to get the date of the Monday of that week.
I used the following logic: Find the day number in the week in the range of 0-6, then subtract that from the originay date.
I used: DATEADD(day,-(DATEPART(weekday,)+5)%7,)
Since DATEPRRT(weekday,) returns 1 = Sundaye ... 7=Saturday,
DATEPART(weekday,)+5)%7 returns 0=Monday ... 6=Sunday.
Subtracting this number of days from the original date gives the previous Monday. The same technique could be used for any starting day of the week.
I found this simple and usefull. Works even if first day of week is Sunday or Monday.
DECLARE #BaseDate AS Date
SET #BaseDate = GETDATE()
DECLARE #FisrtDOW AS Date
SELECT #FirstDOW = DATEADD(d,DATEPART(WEEKDAY,#BaseDate) *-1 + 1, #BaseDate)
Maybe I'm over simplifying here, and that may be the case, but this seems to work for me. Haven't ran into any problems with it yet...
CAST('1/1/' + CAST(YEAR(GETDATE()) AS VARCHAR(30)) AS DATETIME) + (DATEPART(wk, YOUR_DATE) * 7 - 7) as 'FirstDayOfWeek'
CAST('1/1/' + CAST(YEAR(GETDATE()) AS VARCHAR(30)) AS DATETIME) + (DATEPART(wk, YOUR_DATE) * 7) as 'LastDayOfWeek'

Resources