SQL Server - Finding the first day of the week - sql-server

I've been looking around for a chunk of code to find the first day of the current week, and everywhere I look I see this:
DATEADD(WK, DATEDIFF(WK,0,GETDATE()),0)
Every place says this is the code I'm looking for.
The problem with this piece of code is that if you run it for Sunday it chooses the following Monday.
If I run:
SELECT GetDate() , DATEADD(WK, DATEDIFF(WK,0,GETDATE()),0)
Results for today (Tuesday):
2013-05-14 09:36:39.650................2013-05-13 00:00:00.000
This is correct, it chooses Monday the 13th.
If I run:
SELECT GetDate()-1 , DATEADD(WK, DATEDIFF(WK,0,GETDATE()-1),0)
Results for yesterday (Monday):
2013-05-13 09:38:57.950................2013-05-13 00:00:00.000
This is correct, it chooses Monday the 13th.
If I run:
SELECT GetDate()-2 , DATEADD(WK, DATEDIFF(WK,0,GETDATE()-2),0)
Results for the 12th (Sunday):
2013-05-12 09:40:14.817................2013-05-13 00:00:00.000
This is NOT correct, it chooses Monday the 13th when it should choose the previous Monday, the 6th.
Can anyone illuminate me as to what's going in here? I find it hard to believe that no one has pointed out that this doesn't work, so I'm wondering what I'm missing.

It is DATEDIFF that returns the "incorrect" difference of weeks, which in the end results in the wrong Monday. And that is because DATEDIFF(WEEK, ...) doesn't respect the DATEFIRST setting, which I'm assuming you have set to 1 (Monday), and instead always considers the week crossing to be from Saturday to Sunday, or, in other words, it unconditionally considers Sunday to be the first day of the week in this context.
As for an explanation for that, so far I haven't been able to find an official one, but I believe this must have something to do with the DATEDIFF function being one of those SQL Server treats as always deterministic. Apparently, if DATEDIFF(WEEK, ...) relied on the DATEFIRST, it could no longer be considered always deterministic, which I can only guess wasn't how the developers of SQL Server wanted it.
To find the first day of the week's date, I would (and most often do actually) use the first suggestion in #Jasmina Shevchenko's answer:
DATEADD(DAY, 1 - DATEPART(WEEKDAY, #Date), #Date)
DATEPART does respect the DATEFIRST setting and (most likely as a result) it is absent from the list of always deterministic functions.

Try this one -
SET DATEFIRST 1
DECLARE #Date DATETIME
SELECT #Date = GETDATE()
SELECT CAST(DATEADD(DAY, 1 - DATEPART(WEEKDAY, #Date), #Date) AS DATE)
SELECT CAST(#Date - 2 AS DATE), CAST(DATEADD(WK, DATEDIFF(WK, 0, #Date-2), 0) AS DATE)
Results:
---------- ----------
2013-05-12 2013-05-13

SQL Server has a SET DATEFIRST function which allows you to tell it what the first day of the week should be. SET DATEFIRST = 1 tells it to consider Monday as the first day of the week. You should check what the server's default setting is via ##DATEFIRST. Or you could simply change it at the start of your query.
Some references:
MSDN
Similar Question

That worked for me like a charm:
Setting moday as first day of the week without changing DATEFIRST variable:
-- FirstDayWeek
select dateadd(dd,(datepart(dw, case datepart(dw, [yourDate]) when 1 then dateadd(dd,-1,[yourDate]) else [yourDate] end) * -1) + 2, case datepart(dw, [yourDate]) when 1 then dateadd(dd,-1,[yourDate]) else [yourDate] end) as FirstDayWeek;
-- LastDayWeek
select dateadd(dd, (case datepart(dw, [yourDate]) when 1 then datepart(dw, dateadd(dd,-1,[yourDate])) else datepart(dw, [yourDate]) end * -1) + 8, case datepart(dw, [yourDate]) when 1 then dateadd(dd,-1,[yourDate]) else [yourDate] end) as LastDayWeek;
Setting sunday as fist day of the week without changing DATEFIRST variable
select convert(varchar(50), dateadd(dd, (datepart(dw, [yourDate]) * -1) + 2, [yourDate]), 103) as FirstDayWeek, convert(varchar(50), dateadd(dd, (datepart(dw, [yourDate]) * -1) + 8, [yourDate]), 103) as LastDayWeek;
You can change [yourDate] by GETDATE() for testing

Related

How can I make a dynamic SQL Server query with sliding dates but specific times?

I'm trying to make a dynamic query which searches records from 9 days ago at 21:00 to 2 days ago at 23:00.
I've been asked to make a weekly report in Maximo which lists all records between Sundays at 21:00 and the following Fridays at 23:00. The report must generate each week on Tuesdays at 6:00. I have pieced something together from other queries but can't figure out where to go from here:
startdatetime >= dateadd(hour, 21, dateadd(dd, datediff(dd, 0, getdate()), 0))
and startdatetime <= dateadd(hour, 23, dateadd(dd, datediff(dd, 0, getdate()), 0))
I've used DATEADD before to show all records between certain times of the current day. What I can't figure out is how to get those times for previous dates. I tried doing "...dateadd(dd,datediff(dd,0,getdate()-9),0) and ...dateadd(dd,datediff(dd,0,getdate()-2),0)", but I got a SQL error code 207. I obviously don't understand how the second DATEADD segment works. Can anyone show me what I'm doing wrong?
You're really close, you just need to change the second zero to the days you want to move.
WHERE startdatetime >= dateadd(hour,21,dateadd(dd,datediff(dd,0,getdate()),-9))
AND startdatetime <= dateadd(hour,23,dateadd(dd,datediff(dd,0,getdate()),-3))
The error 207 refers to an invalid column name. Make sure that you don't have any typos on your query and that the columns actually exist.
Using 0 as a base (which equates to 1900-01-01) is not very intuitive or self-documenting. Also writing a query that assumes it will only ever run on a Tuesday is a bit dangerous; we can make it so that if it needs to be run again on Wednesday or Thursday it still yields the right results.
-- first, let's make sure we know what weekday is a Sunday
DECLARE #OriginalDateFirst int = ##DATEFIRST;
IF #OriginalDateFirst <> 7
BEGIN
SET DATEFIRST 7;
END
-- let's figure out today
DECLARE #today datetime = CONVERT(date, GETDATE()),
#weekday int = DATEPART(WEEKDAY, GETDATE());
-- if weekday is 3 (Tuesday), the previous Sunday is today -2,
-- and the Sunday before that is today -9
-- if weekday is 4 (Wednesday), the days are -3, -10
-- if weekday is 5 (Thursday), the days are -4, -11
-- and so on
-- so the range is -6-#weekday to 1-#weekday
DECLARE #MostRecentSunday datetime = DATEADD(HOUR, 23, DATEADD(DAY, 1-#weekday, #today)),
#PreviousSunday datetime = DATEADD(HOUR, 21, DATEADD(DAY, -6-#weekday, #today));
-- make sure this is what you want:
SELECT #PreviousSunday, #MostRecentSunday;
-- now your query just says:
... WHERE startdatetime >= #PreviousSunday
AND startdatetime <= #MostRecentSunday;
-- let's put the datefirst setting back
IF #OriginalDateFirst <> 7
BEGIN
SET DATEFIRST #OriginalDateFirst;
END
Yes, it's definitely more code (though you can condense it quite a bit if you like, and if you know nobody messes with DATEFIRST, you can clear out some of that logic, too).
But I'm a big fan of making code self-documenting vs. terse/cryptic.

SET DATEFIST = 1 not working correctly

I'm having trouble to make SET DATEFIRST to work properly on some simple queries I working on at the moment.
Here is my first example:
SET DATEFIRST = 1
SELECT count(Distinct ID)
FROM Products
WHERE
Location in (12)
and YEAR (CREATED) = '2018'
Group by datepart(wk, created), year(created)
The above code gives me some results correct and some others wrong. It's basically counting from Monday to Monday, but I want it to count from Monday to Sunday. I still don't understand why it's counting 8 days instead of 7 days on some of the weeks.
Also I have multiple selections with SET DATEFIRST = 1 which also doesn't work:
SET DATEFIRST = 1
--Products finished this week
SELECT count(Distinct ID)
FROM Products
WHERE
Created >= dateadd(day, 1-datepart(dw, getdate()), CONVERT(date,getdate()))
AND Created < dateadd(day, 8-datepart(dw, getdate()), CONVERT(date,getdate()))
AND Location in (15,16,17) AND (Location IS NOT NULL OR Location NOT IN(18))
UNION ALL
--------------------------------------------
-- Products received this week
SELECT count(Distinct ID)
FROM Products
WHERE
Created >= dateadd(day, 1-datepart(dw, getdate()), CONVERT(date,getdate()))
AND Created < DATEADD(DAY,0,DATEDIFF(DAY,0,dateadd(day, 8-datepart(dw, getdate()),getdate())))
AND Location in(1)
The above code is not reacting to SET DATEFIRST = 1
It's not counting from Monday to sunday, instead it's counting from Sunday to sunday (8 days)
Here is your issue:
DATEADD(day, 8-datepart(DW, GETDATE()), CONVERT(DATE,GETDATE())))
You are comparing a DATETIME from the database with a TIME to a DATE with no TIME. So the DATE portions on next Monday will be equal and your < will fail.
This will not work because the DB field is a DATETIME and you are comparing to a DATE.
SET #D= '06/25/2018 01:20:23 AM' --Monday next week with a time
IF(#D < DATEADD(day, 8-datepart(DW, GETDATE()), CONVERT(GETDATE()))) --<--The whole day
SELECT 1 -- '06/25/2018 12:00:23 AM' < (DATE)2018-06-25
ELSE
SELECT 2
Returns 2
If you do want to include everything through Sunday then you need to set the cutoff to 12:00:00 AM Monday morning. One way to do that would be something like:
DATEADD(DAY,0,DATEDIFF(DAY,0,dateadd(day, 8-datepart(dw, getdate()),getdate())))

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

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'

Deterministic scalar function to get day of week for a date

SQL Server, trying to get day of week via a deterministic UDF.
Im sure this must be possible, but cant figure it out.
UPDATE: SAMPLE CODE..
CREATE VIEW V_Stuff WITH SCHEMABINDING AS
SELECT
MD.ID,
MD.[DateTime]
...
dbo.FN_DayNumeric_DateTime(MD.DateTime) AS [Day],
dbo.FN_TimeNumeric_DateTime(MD.DateTime) AS [Time],
...
FROM {SOMEWHERE}
GO
CREATE UNIQUE CLUSTERED INDEX V_Stuff_Index ON V_Stuff (ID, [DateTime])
GO
Ok, i figured it..
CREATE FUNCTION [dbo].[FN_DayNumeric_DateTime]
(#DT DateTime)
RETURNS INT WITH SCHEMABINDING
AS
BEGIN
DECLARE #Result int
DECLARE #FIRST_DATE DATETIME
SELECT #FIRST_DATE = convert(DATETIME,-53690+((7+5)%7),112)
SET #Result = datediff(dd,dateadd(dd,(datediff(dd,#FIRST_DATE,#DT)/7)*7,#FIRST_DATE), #DT)
RETURN (#Result)
END
GO
Slightly similar approach to aforementioned solution, but just a one-liner that could be used inside a function or inline for computed column.
Assumptions:
You don't have dates before
1899-12-31 (which is a Sunday)
You want to imitate ##datefirst = 7
#dt is smalldatetime, datetime,
date, or datetime2 data type
If you'd rather it be different, change the date '18991231' to a date with the weekday that you'd like to equal 1. The convert() function is key to making the whole thing work - cast does NOT do the trick:
((datediff(day, convert(datetime,
'18991231', 112), #dt) % 7)
+ 1)
I know this post is way-super-old, but I was trying to do a similar thing and came up with a different solution and figured I'd post for posterity. Plus I did some searching around and did not find much content on this question.
In my case, I was trying to use a computed column PERSISTED, which requires the calculation to be deterministic. The calculation I used is:
datediff(dd,'2010-01-03',[DateColumn]) % 7 + 1
The idea is to figure out a known Sunday that you know will occur before any possible date in your table (in this case, Jan 3 2010), then calculate the modulo 7 + 1 of the number of days since that Sunday.
The problem is that including a literal date in the function call is enough to mark it as non-deterministic. You can work around that by using the integer 0 to represent the epoch, which for SQL Server is Jan 1st, 1900, a Sunday.
datediff(dd,0,[DateColumn]) % 7 + 1
The +1 just makes the result work the same as datepart(dw,[datecolumn]) when datefirst is set to 7 (default for US), which sets Sunday to 1, Monday to 2, etc
I can also use this in conjunction with case [thatComputedColumn] when 1 then 'Sunday' when 2 then 'Monday' ... etc. Wordier, but deterministic, which was a requirement in my environs.
Taken from Deterministic scalar function to get week of year for a date
;
with
Dates(DateValue) as
(
select cast('2000-01-01' as date)
union all
select dateadd(day, 1, DateValue) from Dates where DateValue < '2050-01-01'
)
select
year(DateValue) * 10000 + month(DateValue) * 100 + day(DateValue) as DateKey, DateValue,
datediff(day, dateadd(week, datediff(week, 0, DateValue), 0), DateValue) + 2 as DayOfWeek,
datediff(week, dateadd(month, datediff(month, 0, DateValue), 0), DateValue) + 1 as WeekOfMonth,
datediff(week, dateadd(year, datediff(year, 0, DateValue), 0), DateValue) + 1 as WeekOfYear
from Dates option (maxrecursion 0)
There is an already built-in function in sql to do it:
SELECT DATEPART(weekday, '2009-11-11')
EDIT:
If you really need deterministic UDF:
CREATE FUNCTION DayOfWeek(#myDate DATETIME )
RETURNS int
AS
BEGIN
RETURN DATEPART(weekday, #myDate)
END
GO
SELECT dbo.DayOfWeek('2009-11-11')
EDIT again: this is actually wrong, as DATEPART(weekday) is not deterministic.
UPDATE:
DATEPART(weekday) is non-deterministic because it relies on DATEFIRST (source).
You can change it with SET DATEFIRST but you can't call it inside a stored function.
I think the next step is to make your own implementation, using your preferred DATEFIRST inside it (and not considering it at all, using for example Monday as first day).
The proposed solution has one problem - it returns 0 for Saturdays. Assuming that we're looking for something compatible with DATEPART(WEEKDAY) this is an issue.
Nothing a simple CASE statement won't fix, though.
Make a function, and have #dbdate varchar(8) as your input variable.
Have it return the following:
RETURN (DATEDIFF(dd, -1, convert(datetime, #dbdate, 112)) % 7)+1;
The value 112 is the sql style YYYYMMDD.
This is deterministic because the datediff does not receive a string input, if it were to receive a string it would no longer work because it internally converts it to a datetime object. Which is not deterministic.
Not sure what you are looking for, but if this is part of a website, try this php function from http://php.net/manual/en/function.date.php
function weekday($fyear, $fmonth, $fday) //0 is monday
{
return (((mktime ( 0, 0, 0, $fmonth, $fday, $fyear) - mktime ( 0, 0, 0, 7, 17, 2006))/(60*60*24))+700000) % 7;
}
The day of the week? Why don't you just use DATEPART?
DATEPART(weekday, YEAR_DATE)
Can't you just select it with something like:
SELECT DATENAME(dw, GETDATE());

Resources