Occasionally, I like updating old articles that I’ve written on Simple-Talk. Since I used to help Robyn Page with hers, I now and then do the same for her articles as well, with the kind permission of Simple-Talk’s editor. I was rather amused by this DateTime function, that was just derived from one of her examples in her classic ‘Robyn Page’s SQL Server DATE/TIME Workbench’. I extended it to calculate the date, for any year of the first, second, third or fourth weekday (Sunday, Monday, Tuesday etc) in any month. Actually, it will calculate the date of the ‘n’th occurrence of any particular day of the week after the first of the month, even if it occurs in subsequent months. This calculation can cause difficulties because the day of the week is numbered 1-7, and can start at any day, depending on the language setting. To make things more complicated, this setting (@@Datefirst) can be changed independently. No calculations on the day of the week can be dependent on this alarmingly mutable setting. Robyn was struggling with this and asked for my help. I had drunk a rather potent cup of coffee and so, with my temples thumping, I keyed out what seemed a simple expression to get around the problem. I’m not sure that I can explain it now but, mercifully, it seems to work, and works independently of the @@Datefirst setting. I decided to do it as a single query so that the basic expression could also be used ‘in line’ to avoid using a scalar function, but I’ve done it as a scalar function here just to keep things simple. Speed-freaks may prefer to redo it as an inline table-valued function to avoid the performance problems of scalar functions.
I expect that you will find this function rather simple. If so, would you please do a similar function, independent of your @@DateFirst setting, to calculate such holidays as Arbor Day (Last Friday in April) or Memorial Day (Last Monday in May)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
IF OBJECT_ID (N'NthDayOfWeekOfMonth') IS NOT NULL DROP FUNCTION NthDayOfWeekOfMonth GO CREATE FUNCTION NthDayOfWeekOfMonth /** summary: > This is a simple query that calculates the date of holidays that are held on a particular day of the week in a month, such as the first Sunday, third Wednesday and so on. It is designed to give the correct result whatever your definition of the first day of the week, set in the global variable @@DateFirst. This setting determines what is considered the first day of the week. Mysteriously, it is related to language rather than nation or culture. If you alter the language setting, the @@Datefirst value is changed. Author: Phil Factor Revision: 1.3 date: 20 May 2014 example: code: | --Martin Luther King Day (Third Monday in January) Select dbo.NthDayOfWeekOfMonth ('2014','Jan','Mon',3) --President's Day (Third Monday in February) Select dbo.NthDayOfWeekOfMonth ('2014','Feb','Mon',3) -- Mother's Day (Second Sunday in May) Select dbo.NthDayOfWeekOfMonth ('2014','May','Sun',2) --Father's Day (Third Sunday in June) Select dbo.NthDayOfWeekOfMonth ('2014','Jun','Sun',3) --Labor Day (First Monday in September) Select dbo.NthDayOfWeekOfMonth ('2014','Sep','Mon',1) --Columbus Day (Second Monday in October) Select dbo.NthDayOfWeekOfMonth ('2014','Oct','Mon',2) --Thanksgiving (Fourth Thursday in November) Select dbo.NthDayOfWeekOfMonth ('2014','Nov','Thu',4) --Black Friday (Friday after Thanksgiving) Select DateAdd(Day,1,dbo.NthDayOfWeekOfMonth ('2014','Nov','Thu',4)) returns: > The date specified as a DATETIME **/ ( @TheYear CHAR(4), --the year as four characters (e.g. '2014') @TheMonth CHAR(3), --in english (Sorry to our EU colleagues) e.g. Jun, Jul, Aug @TheDayOfWeek CHAR(3), -- one of Mon, Tue, Wed, Thu, Fri, Sat, Sun @Nth INT) --1 for the first date, 2 for the second occurrence, 3 for the third RETURNS DATETIME WITH EXECUTE AS CALLER AS BEGIN RETURN DATEADD(MONTH, DATEDIFF(MONTH, 0, CONVERT(date,'1 '+@TheMonth+' '+@TheYear,113)), 0)+ (7*@Nth)-1 -(DATEPART (Weekday, DATEADD(MONTH, DATEDIFF(MONTH, 0, CONVERT(date,'1 '+@TheMonth+' '+@TheYear,113)), 0)) +@@DateFirst+(CHARINDEX(@TheDayOfWeek,'FriThuWedTueMonSunSat')-1)/3)%7 END GO IF (SELECT dbo.NthDayOfWeekOfMonth ('2012','jun','Mon',1))<> '2012-06-04' RAISERROR('''NthDayOfWeekOfMonth'' stopped working (1)',16,1) IF (SELECT dbo.NthDayOfWeekOfMonth ('2014','Sep','Mon',1))<> '2014-09-01' RAISERROR('''NthDayOfWeekOfMonth'' stopped working (2)',16,1) IF (SELECT dbo.NthDayOfWeekOfMonth ('2015','Mar','Wed',1))<> '2015-03-04' RAISERROR('''NthDayOfWeekOfMonth'' stopped working (3)',16,1) IF (SELECT dbo.NthDayOfWeekOfMonth ('1987','Dec','Sun',1))<> '1987-12-06' RAISERROR('''NthDayOfWeekOfMonth'' stopped working (4)',16,1) --thanksgiving (4th thursday in november) IF (SELECT dbo.NthDayOfWeekOfMonth ('2014','Nov','Thu',4))<> '2014-11-27' RAISERROR('''NthDayOfWeekOfMonth'' stopped working (5)',16,1) IF (SELECT dbo.NthDayOfWeekOfMonth ('2015','Nov','Thu',4))<> '2015-11-26' RAISERROR('''NthDayOfWeekOfMonth'' stopped working (6)',16,1) --Father's Day UK (Third Sunday of June) IF (SELECT dbo.NthDayOfWeekOfMonth ('2014','Jun','Sun',3))<> '2014-6-15' RAISERROR('''NthDayOfWeekOfMonth'' stopped working (7)',16,1) IF (SELECT dbo.NthDayOfWeekOfMonth ('2015','Jun','Sun',3))<> '2015-6-21' RAISERROR('''NthDayOfWeekOfMonth'' stopped working (8)',16,1) |
Load comments