Number of months between 2 dates - vba

I am trying to create a formula or VBA function which calculates the number of months between two dates, according to this rule: if the start date is the 15th of the month or before, or the end date is after the 15th of the month, then that month counts.
For example:
Start Date End Date Output
---------- --------- --------
1/5/2014 2/16/2014 2 months
1/17/2014 2/16/2014 1 month
1/16/2014 2/5/2014 0 months
I have already tried =DATEDIF(A2, B2, "M") + IF( DATEDIF(A2, B2, "MD")>=15, 1, 0) but this only adds a month if the distance between the days in 2 dates is over 15. For example if the start date is 5/14/13-8/16/13 it will say that there are 3 months in between these dates. However, the actual distance between these 2 dates should be 4 months according to the conditions that I specified above.
Ho do I fix this formula?

Here is a vba solution as well
Function date_diff_to_months(date1 As Date, date2 As Date) As Integer
Dim y1 As Integer
Dim y2 As Integer
Dim d1 As Integer
Dim d2 As Integer
Dim m1 As Integer
Dim m2 As Integer
Dim m_diff As Integer
Dim y_diff As Integer
Dim month_adjustment As Integer
y1 = Year(date1)
y2 = Year(date2)
m1 = Month(date1)
m2 = Month(date2)
d1 = Day(date1)
d2 = Day(date2)
m_diff = m2 - m1
y_diff = (y2 - y1) * 12
If (m_diff > 0 Or y_diff > 0) Then
If (d1 <= 15 And d2 >= 15) Then
month_adjustment = 1
ElseIf (d1 >= 15 And d2 <= 15) Then
month_adjustment = -1
End If
End If
date_diff_to_months = m_diff + y_diff + month_adjustment
End Function

EDit: account for years...
=( (YEAR(B1)*12+MONTH(B1)) - (YEAR(A1)*12+MONTH(A1)) )
+ ( IF(DAY(A1)<=15,1,0)+IF(DAY(B1)>15,1,0) )

Related

Calculate number of months away from today's date

I'm trying to subtract a user given date from today's date and return a number that represents the number of months away from today's date. Here is the code I have, it keeps returning 0 though.
Public Function QProfile(Arg1 As Date)
Dim Tdate As Date
Dim Idate As Date
Dim Rdate As Integer
Dim Result As Integer
Dim N As Integer
Tdate = Today()
Idate = Arg1
Rdate = Today() - Arg1
Result = 7
N = 30
If Rdate <= N Then
Result = 1
End If
If Rdate >= N + 1 And Rdate <= N * 2 Then
Result = 2
End If
If Rdate >= 2 * N + 1 And Rdate <= N * 3 Then
Result = 3
End If
If Rdate >= 3 * N + 1 And Rdate <= N * 4 Then
Result = 4
End If
If Rdate >= 4 * N + 1 And Rdate <= N * 5 Then
Result = 5
End If
If Rdate >= 5 * N + 1 And Rdate <= N * 6 Then
Result = 6
End If
If Rdate >= 6 * N + 1 And Rdate <= N * 7 Then
Result = 7
End If
If Rdate >= 7 * N + 1 And Rdate <= N * 8 Then
Result = 8
End If
If Rdate >= 8 * N + 1 And Rdate <= N * 9 Then
Result = 9
End If
If Rdate >= 9 * N + 1 And Rdate <= N * 10 Then
Result = 10
End If
If Rdate >= 11 * N + 1 And Rdate <= 365 Then
Result = 12
End If
If Rdate >= 365 + 1 And Rdate <= N + 365 Then
Result = 13
End If
If Rdate >= 365 + 1 * N + 1 And Rdate <= 2 * N + 365 Then
Result = 14
End If
If Rdate >= 365 + 2 * N + 1 And Rdate <= 3 * N + 365 Then
Result = 15
End If
If Rdate >= 365 + 3 * N + 1 And Rdate <= 4 * N + 365 Then
Result = 16
End If
If Rdate >= 365 + 4 * N + 1 And Rdate <= 5 * N + 365 Then
Result = 17
End If
If Rdate >= 365 + 5 * N + 1 And Rdate <= 6 * N + 365 Then
Result = 18
End If
If Rdate >= 365 + 6 * N + 1 And Rdate <= 7 * N + 365 Then
Result = 19
End If
If Rdate >= 365 + 7 * N + 1 And Rdate <= 8 * N + 365 Then
Result = 20
End If
If Rdate >= 365 + 8 * N + 1 And Rdate <= 9 * N + 365 Then
Result = 21
End If
If Rdate >= 365 + 9 * N + 1 And Rdate <= 10 * N + 365 Then
Result = 22
End If
If Rdate >= 365 + 10 * N + 1 And Rdate <= 11 * N + 365 Then
Result = 23
End If
If Rdate >= 365 + 11 * N + 1 And Rdate <= 2 * 365 Then
Result = 24
End If
If Rdate >= 365 * 2 + 1 And Rdate <= 1 * N + 2 * 365 Then
Result = 25
End If
If Rdate >= 365 * 2 + 1 * N + 1 And Rdate <= 2 * N + 2 * 365 Then
Result = 26
End If
If Rdate >= 365 * 2 + 2 * N + 1 And Rdate <= 3 * N + 2 * 365 Then
Result = 27
End If
If Rdate >= 365 * 2 + 3 * N + 1 And Rdate <= 4 * N + 2 * 365 Then
Result = 28
End If
If Rdate >= 365 * 2 + 4 * N + 1 And Rdate <= 5 * N + 2 * 365 Then
Result = 29
End If
If Rdate >= 365 * 2 + 5 * N + 1 And Rdate <= 6 * N + 2 * 365 Then
Result = 30
End If
If Rdate >= 365 * 2 + 6 * N + 1 And Rdate <= 7 * N + 2 * 365 Then
Result = 31
End If
If Rdate >= 365 * 2 + 7 * N + 1 And Rdate <= 8 * N + 2 * 365 Then
Result = 32
End If
If Rdate >= 365 * 2 + 8 * N + 1 And Rdate <= 9 * N + 2 * 365 Then
Result = 33
End If
If Rdate >= 365 * 2 + 9 * N + 1 And Rdate <= 10 * N + 2 * 365 Then
Result = 34
End If
If Rdate >= 365 * 2 + 10 * N + 1 And Rdate <= 11 * N + 2 * 365 Then
Result = 35
End If
If Rdate >= 365 * 2 + 11 * N + 1 And Rdate <= 3 * 365 Then
Result = 36
End If
If Rdate > 3 * 365 + 1 Then
Result = 37
End If
QProfile = Result
End Function

Formatting Variable as Date (mm/dd)

I am trying to create a script that identifies the date range of the previous month (Cell G9 is today's date). It is working; however, the output is formatting the date much differently than I am intending. The [J36] output is "01/08" when it should be "08/01" and the [J37] output is "6:11:37 AM" (interpretting it as a time), when it should be "08/31". I am only coding for the output in particular cells because I want to see that it is working. However, it is the variable I want to format, not the cell. I'm going to be using the startdate and enddate variable in future code and those variables on their own won't be visable in any cells.
I attempted to use the format(startdate,mm/dd), but I got an overflow error.
Here is my script:
Dim thismonth As Integer
thismonth = Month(Sheet1.[G9])
Dim startdate As Date
Dim enddate As Date
Select Case thismonth
Case 1
startdate = 12 / 1
enddate = 12 / 31
Case 2
startdate = 1 / 1
enddate = 1 / 31
Case 3
startdate = 2 / 1
enddate = 2 / 28
Case 4
startdate = 3 / 1
enddate = 3 / 31
Case 5
startdate = 4 / 1
enddate = 4 / 30
Case 6
startdate = 5 / 1
enddate = 5 / 31
Case 7
startdate = 6 / 1
enddate = 6 / 30
Case 8
startdate = 7 / 1
enddate = 7 / 31
Case 9
startdate = 8 / 1
enddate = 8 / 31
Case 10
startdate = 9 / 1
enddate = 9 / 31
Case 11
startdate = 10 / 1
enddate = 10 / 31
Case 12
startdate = 11 / 1
enddate = 11 / 30
End Select
Sheet1.[J36] = startdate
Sheet1.[J37] = enddate
How about a much simpler solution:
Dim startDate as Date
Dim endDate as Date
startDate = Application.WorksheetFunction.EOMONTH(Sheet1.[G9],-2) + 1
endDate = Application.WorksheetFunction.EOMONTH(Sheet1.[G9],-1)
You can get rid of VBA altogether and write this worksheet formula directly in J36 and J37 respectively.
J36: =EOMONTH(G9,-2)+1
J37: =EOMONTH(G9,-1)

SQL Server - Set Canadian Business Holiday Date

Note: this is for Canadian Holidays:
I need to populate a table with Canadian business holidays.
I have the following query that sets up business holiday dates and also considers the movement of the holiday to the next weekday in the event of the holiday occurring on a weekend. ie if the holiday occurs on Sunday then Monday becomes a holiday. I also identify weekdays with a flag and add an English description of the day.
I add a few calculated columns to make the job a bit easier, these are removed again after all the data has been created. I cannot drop the table; the table maybe partly populated and I cannot overwrite existing data. This is the root of my problem. Because of the 'Were' the query is very slow.
The query I am using to populate the table is:
DECLARE #StartDate DATE = '20000101'
,#EndDate DATE = '21631231';
WITH N10(n)
AS (SELECT 1
FROM ( VALUES ( 0), ( 1), ( 2), ( 3), ( 4), ( 5), ( 6), ( 7), ( 8), ( 9) ) v (n)),
N100(n)
AS (SELECT 1
FROM N10
,N10 n),
N10000(n)
AS (SELECT 1
FROM N100
,N100 n),
N100000(n)
AS (SELECT 1
FROM N10
,N10000 n),
N AS (SELECT TOP (DATEDIFF(DAY,#StartDate,#EndDate) + 1)
n = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1
FROM N100000)
INSERT INTO [dbo].[BusinessCalendarDetails2] (BusinessDate,CreatedById,CreatedTime,BusinessCalendarId,IsWeekday,
IsHoliday)
(SELECT InsertDate,1,CURRENT_TIMESTAMP,1,1,0
FROM N
CROSS APPLY (SELECT DATEADD (DAY,n,#StartDate)) d (InsertDate)
WHERE NOT EXISTS ( SELECT 1 FROM [BusinessCalendarDetails2]
WHERE [BusinessDate] = InsertDate ));
This works but the problem is the 'WHERE' to identify if the date already exists in the table is severely slowing it down (14 mins). So I am wondering if someone has a faster solution for identifying already existing dates?
Thanks in advance for your assistance.
The following is the entire script, it works fairly well; others may find it useful.
-- Populate table with business holidays
-- If the table is missing add it
IF OBJECT_ID('BusinessCalendarDetails2') IS NULL
BEGIN
CREATE TABLE dbo.BusinessCalendarDetails2 (
Id BIGINT IDENTITY(1,1)
NOT NULL
,BusinessDate [DATE] NOT NULL
,Day AS DAY(BusinessDate)
,Week AS DATEPART(WEEK,BusinessDate)
,[Month] AS MONTH(BusinessDate)
,Quarter AS DATEPART(QUARTER,BusinessDate)
,[Year] AS YEAR(BusinessDate)
,DayOfWeek AS DATEPART(WEEKDAY,BusinessDate)
,CreatedById BIGINT NOT NULL
,CreatedTime DATETIMEOFFSET(7) NOT NULL
,UpdatedById BIGINT NULL
,UpdatedTime DATETIMEOFFSET(7) NULL
,BusinessCalendarId BIGINT NOT NULL
,RowVersion TIMESTAMP NOT NULL
,IsWeekday BIT NOT NULL
,IsHoliday BIT NOT NULL
,Description NVARCHAR(50)
,PRIMARY KEY CLUSTERED (Id ASC)
WITH (PAD_INDEX = OFF,STATISTICS_NORECOMPUTE = OFF,IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY])
ON [PRIMARY];
END;
-- Check if this col exists,
IF COL_LENGTH('dbo.BusinessCalendarDetails2','Day') IS NULL
BEGIN
ALTER TABLE dbo.BusinessCalendarDetails2
ADD -- Add calculated fields
Day AS DAY(BusinessDate), Week AS DATEPART(WEEK, BusinessDate), [Month] AS MONTH(BusinessDate), Quarter AS DATEPART(QUARTER, BusinessDate), [Year] AS YEAR(BusinessDate), DayOfWeek AS DATEPART(WEEKDAY, BusinessDate);
END;
GO
-- Date range to populate
DECLARE #StartDate DATE= '20000101' ,#EndDate DATE= '21631231';
WITH N10(n)
AS (SELECT 1
FROM ( VALUES ( 0 ), ( 1 ), ( 2 ), ( 3 ), ( 4 ), ( 5 ), ( 6 ), ( 7 ), ( 8 ), ( 9 ) ) AS v (n)),
N100(n)
AS (SELECT 1
FROM N10
,N10 AS n),
N10000(n)
AS (SELECT 1
FROM N100
,N100 AS n),
N100000(n)
AS (SELECT 1
FROM N10
,N10000 AS n),
N AS (SELECT TOP (DATEDIFF(DAY,#StartDate,#EndDate) + 1)
n = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1
FROM N100000)
INSERT INTO dbo.BusinessCalendarDetails2 (BusinessDate,CreatedById,CreatedTime,BusinessCalendarId,IsWeekday,
IsHoliday)
(SELECT InsertDate,1,CURRENT_TIMESTAMP,1,1,0
FROM N
CROSS APPLY (SELECT DATEADD (DAY,n,#StartDate)) AS d (InsertDate)
WHERE NOT EXISTS ( SELECT 1
FROM BusinessCalendarDetails2
WHERE BusinessDate = InsertDate ));
-- Set Descriptions
UPDATE dbo.BusinessCalendarDetails2
SET Description = DATENAME(dw,BusinessDate)
FROM dbo.BusinessCalendarDetails2;
-- Set weekdays
UPDATE dbo.BusinessCalendarDetails2
SET IsWeekday = 0,Description = Description + ' Weekend'
FROM dbo.BusinessCalendarDetails2 AS c1
WHERE DATEPART(WEEKDAY,c1.BusinessDate) IN (1,7);
-- New Years Day
UPDATE dbo.BusinessCalendarDetails2
SET IsHoliday = 1,Description = Description + ' New Years Day'
FROM dbo.BusinessCalendarDetails2
WHERE BusinessCalendarDetails2.[Month] = 1
AND (SELECT CASE (##DATEFIRST + DATEPART(DW,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-01-01')) % 7
WHEN 0 THEN 3 -- SAT
WHEN 1 THEN 2 -- Sunday
ELSE 1
END) = BusinessCalendarDetails2.Day;
-- Family Day Day -- 3rd Monday in February
UPDATE dbo.BusinessCalendarDetails2
SET IsHoliday = 1,Description = Description + ' Family Day'
FROM dbo.BusinessCalendarDetails2 AS c1
WHERE c1.[Month] = 2
AND c1.DayOfWeek = 2
AND c1.Day BETWEEN 15 AND 21;
-- Canada Day
UPDATE dbo.BusinessCalendarDetails2
SET IsHoliday = 1,Description = Description + ' Canada Day'
FROM dbo.BusinessCalendarDetails2
WHERE BusinessCalendarDetails2.[Month] = 7
AND (SELECT CASE (##DATEFIRST + DATEPART(DW,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-07-01')) % 7
WHEN 0 THEN 3 -- SAT
WHEN 1 THEN 2 -- Sunday
ELSE 1
END) = BusinessCalendarDetails2.Day;
-- Civic Holiday
UPDATE dbo.BusinessCalendarDetails2
SET IsHoliday = 1,Description = Description + ' Civic Holiday'
FROM dbo.BusinessCalendarDetails2 AS c1
WHERE c1.[Month] = 8
AND c1.DayOfWeek = 2
AND c1.Day BETWEEN 1 AND 7;
-- Good Friday
UPDATE BusinessCalendarDetails2
SET IsHoliday = 1,Description = Description + ' Good Friday'
FROM dbo.BusinessCalendarDetails2 AS dimdate
CROSS APPLY (SELECT dimdate.Year AS y) _y
CROSS APPLY (SELECT y / 100 AS c,y - 19 * y / 19 AS n) _nc
CROSS APPLY (SELECT (c - 17) / 25 AS k) _k
CROSS APPLY (SELECT c - c / 4 - (c - k) / 3 + 19 * n + 15 AS i1) _i1
CROSS APPLY (SELECT i1 - 30 * i1 / 30 AS i2) _i2
CROSS APPLY (SELECT i2 - (i2 / 28) * (1 - (i2 / 28) * 29 / (i2 + 1) * (21 - n) / 11) AS i) _i
CROSS APPLY (SELECT y + y / 4 + i + 2 - c + c / 4 AS j1) _j1
CROSS APPLY (SELECT j1 - 7 * j1 / 7 AS j) _j
CROSS APPLY (SELECT i - j AS el) _el
CROSS APPLY (SELECT 3 + (el + 40) / 44 AS m) _m
CROSS APPLY (SELECT el + 28 - 31 * m / 4 AS d) _d
CROSS APPLY (SELECT DATEFROMPARTS (y,m,d) AS EasterSunday) _Easter
WHERE dimdate.BusinessDate = DATEADD(DAY,-2,EasterSunday);
-- Easter Sunday
UPDATE BusinessCalendarDetails2
SET IsHoliday = 0,Description = Description + ' Easter Sunday'
FROM dbo.BusinessCalendarDetails2 AS dimdate
CROSS APPLY (SELECT dimdate.Year AS y) _y
CROSS APPLY (SELECT y / 100 AS c,y - 19 * y / 19 AS n) _nc
CROSS APPLY (SELECT (c - 17) / 25 AS k) _k
CROSS APPLY (SELECT c - c / 4 - (c - k) / 3 + 19 * n + 15 AS i1) _i1
CROSS APPLY (SELECT i1 - 30 * i1 / 30 AS i2) _i2
CROSS APPLY (SELECT i2 - (i2 / 28) * (1 - (i2 / 28) * 29 / (i2 + 1) * (21 - n) / 11) AS i) _i
CROSS APPLY (SELECT y + y / 4 + i + 2 - c + c / 4 AS j1) _j1
CROSS APPLY (SELECT j1 - 7 * j1 / 7 AS j) _j
CROSS APPLY (SELECT i - j AS el) _el
CROSS APPLY (SELECT 3 + (el + 40) / 44 AS m) _m
CROSS APPLY (SELECT el + 28 - 31 * m / 4 AS d) _d
CROSS APPLY (SELECT DATEFROMPARTS (y,m,d) AS EasterSunday) _Easter
WHERE dimdate.BusinessDate = EasterSunday;
-- Labour Day -- first Monday of September
UPDATE dbo.BusinessCalendarDetails2
SET IsHoliday = 1,Description = Description + ' Labour Day'
FROM dbo.BusinessCalendarDetails2 AS c1
WHERE c1.[Month] = 9
AND c1.DayOfWeek = 2
AND c1.Day BETWEEN 1 AND 7;
-- Set Thanksgiving
UPDATE dbo.BusinessCalendarDetails2
SET IsHoliday = 1,Description = Description + ' Thanksgiving'
FROM dbo.BusinessCalendarDetails2 AS c1
WHERE c1.[Month] = 10
AND c1.DayOfWeek = 2
AND c1.Day BETWEEN 8 AND 14;
---- Christmas
UPDATE dbo.BusinessCalendarDetails2
SET IsHoliday = 1,Description = Description + ' Christmas Holidays'
FROM dbo.BusinessCalendarDetails2
WHERE [Month] = 12
AND Day BETWEEN 26 AND 28
AND (DATEPART(WEEKDAY,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-12-25') IN (1,7) -- Is Christmas on a weekend this year
OR DATEPART(WEEKDAY,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-12-26') IN (1,7)) -- Is Boxingday on a weekend this year
AND DATEPART(dw,BusinessDate) <> 4 -- NOT ON Wednesday!
AND DATEPART(DAY,(SELECT CASE (##DATEFIRST + DATEPART(dw,BusinessDate)) % 7
WHEN 0 THEN DATEADD(DAY,2,BusinessDate) -- Saturday
WHEN 1 THEN DATEADD(DAY,1,BusinessDate) -- Sunday
ELSE BusinessDate
END AS Weekday)) BETWEEN 26
AND 28
OR [Month] = 12 -- Is christmas a week day?
AND Day BETWEEN 25 AND 26
AND IsWeekday = 1;
GO
-- Return table to orig state
ALTER TABLE dbo.BusinessCalendarDetails2 DROP COLUMN Day, Week, [Month], Quarter, [Year], DayOfWeek;
If there's no index on BusinessDate and the table is big enough, that would certainly slow things down. If it is indexed, you may need to rebuild/reorganize it, or even just redo the statistics.
Otherwise, depending on your disks, it may be quicker to insert the distinct BusinessDate column values into a temp table with an index on it and compare against that.
It could be many things. If you haven't already, you should look at the ACTUAL execution plan and statistics for your query to get a better idea of what exactly is causing the issue.
This is the final answer using a Temp table it runs much faster
-- Populate table with business holidays
CREATE TABLE #BusCalDet (
Id BIGINT IDENTITY(1,1)
NOT NULL
,BusinessDate [DATE] NOT NULL
,Day AS DAY(BusinessDate)
,Week AS DATEPART(WEEK,BusinessDate)
,[Month] AS MONTH(BusinessDate)
,Quarter AS DATEPART(QUARTER,BusinessDate)
,[Year] AS YEAR(BusinessDate)
,DayOfWeek AS DATEPART(WEEKDAY,BusinessDate)
,CreatedById BIGINT NOT NULL
,CreatedTime DATETIMEOFFSET(7) NOT NULL
,UpdatedById BIGINT NULL
,UpdatedTime DATETIMEOFFSET(7) NULL
,BusinessCalendarId BIGINT NOT NULL
,RowVersion TIMESTAMP NOT NULL
,IsWeekday BIT NOT NULL
,IsHoliday BIT NOT NULL
,Description NVARCHAR(50)
,PRIMARY KEY CLUSTERED (Id ASC)
WITH (PAD_INDEX = OFF,STATISTICS_NORECOMPUTE = OFF,IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY])
ON [PRIMARY];
-- Date range to populate
DECLARE #StartDate DATE= '20000101'
,#EndDate DATE= '21631231';
WITH N10(n)
AS (SELECT 1
FROM ( VALUES ( 0 ), ( 1 ), ( 2 ), ( 3 ), ( 4 ), ( 5 ), ( 6 ), ( 7 ), ( 8 ), ( 9 ) ) AS v (n)),
N100(n)
AS (SELECT 1
FROM N10
,N10 AS n),
N10000(n)
AS (SELECT 1
FROM N100
,N100 AS n),
N100000(n)
AS (SELECT 1
FROM N10
,N10000 AS n),
N AS (SELECT TOP (DATEDIFF(DAY,#StartDate,#EndDate) + 1)
n = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1
FROM N100000)
INSERT INTO #BusCalDet (BusinessDate,CreatedById,CreatedTime,BusinessCalendarId,IsWeekday,IsHoliday)
SELECT InsertDate,1,CURRENT_TIMESTAMP,1,1,0
FROM N
CROSS APPLY (SELECT DATEADD (DAY,n,#StartDate)) AS d (InsertDate);
-- Set Descriptions
UPDATE #BusCalDet
SET Description = DATENAME(dw,BusinessDate)
FROM #BusCalDet;
-- Set weekdays
UPDATE #BusCalDet
SET IsWeekday = 0,Description = Description + ' Weekend'
FROM #BusCalDet AS c1
WHERE DATEPART(WEEKDAY,c1.BusinessDate) IN (1,7);
-- New Years Day
UPDATE #BusCalDet
SET IsHoliday = 1,Description = Description + ' New Years Day'
FROM #BusCalDet
WHERE #BusCalDet.[Month] = 1
AND (SELECT CASE (##DATEFIRST + DATEPART(DW,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-01-01'))
% 7
WHEN 0 THEN 3 -- SAT
WHEN 1 THEN 2 -- Sunday
ELSE 1
END) = #BusCalDet.Day;
-- Family Day Day -- 3rd Monday in February
UPDATE #BusCalDet
SET IsHoliday = 1,Description = Description + ' Family Day'
FROM #BusCalDet AS c1
WHERE c1.[Month] = 2
AND c1.DayOfWeek = 2
AND c1.Day BETWEEN 15 AND 21;
-- Canada Day
UPDATE #BusCalDet
SET IsHoliday = 1,Description = Description + ' Canada Day'
FROM #BusCalDet
WHERE #BusCalDet.[Month] = 7
AND (SELECT CASE (##DATEFIRST + DATEPART(DW,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-07-01'))
% 7
WHEN 0 THEN 3 -- SAT
WHEN 1 THEN 2 -- Sunday
ELSE 1
END) = #BusCalDet.Day;
-- Civic Holiday
UPDATE #BusCalDet
SET IsHoliday = 1,Description = Description + ' Civic Holiday'
FROM #BusCalDet AS c1
WHERE c1.[Month] = 8
AND c1.DayOfWeek = 2
AND c1.Day BETWEEN 1 AND 7;
-- Good Friday
UPDATE #BusCalDet
SET IsHoliday = 1,Description = Description + ' Good Friday'
FROM #BusCalDet AS dimdate
CROSS APPLY (SELECT dimdate.Year AS y) _y
CROSS APPLY (SELECT y / 100 AS c,y - 19 * y / 19 AS n) _nc
CROSS APPLY (SELECT (c - 17) / 25 AS k) _k
CROSS APPLY (SELECT c - c / 4 - (c - k) / 3 + 19 * n + 15 AS i1) _i1
CROSS APPLY (SELECT i1 - 30 * i1 / 30 AS i2) _i2
CROSS APPLY (SELECT i2 - (i2 / 28) * (1 - (i2 / 28) * 29 / (i2 + 1) * (21 - n) / 11) AS i) _i
CROSS APPLY (SELECT y + y / 4 + i + 2 - c + c / 4 AS j1) _j1
CROSS APPLY (SELECT j1 - 7 * j1 / 7 AS j) _j
CROSS APPLY (SELECT i - j AS el) _el
CROSS APPLY (SELECT 3 + (el + 40) / 44 AS m) _m
CROSS APPLY (SELECT el + 28 - 31 * m / 4 AS d) _d
CROSS APPLY (SELECT DATEFROMPARTS (y,m,d) AS EasterSunday) _Easter
WHERE dimdate.BusinessDate = DATEADD(DAY,-2,EasterSunday);
-- Easter Sunday
UPDATE #BusCalDet
SET IsHoliday = 0,Description = Description + ' Easter Sunday'
FROM #BusCalDet AS dimdate
CROSS APPLY (SELECT dimdate.Year AS y) _y
CROSS APPLY (SELECT y / 100 AS c,y - 19 * y / 19 AS n) _nc
CROSS APPLY (SELECT (c - 17) / 25 AS k) _k
CROSS APPLY (SELECT c - c / 4 - (c - k) / 3 + 19 * n + 15 AS i1) _i1
CROSS APPLY (SELECT i1 - 30 * i1 / 30 AS i2) _i2
CROSS APPLY (SELECT i2 - (i2 / 28) * (1 - (i2 / 28) * 29 / (i2 + 1) * (21 - n) / 11) AS i) _i
CROSS APPLY (SELECT y + y / 4 + i + 2 - c + c / 4 AS j1) _j1
CROSS APPLY (SELECT j1 - 7 * j1 / 7 AS j) _j
CROSS APPLY (SELECT i - j AS el) _el
CROSS APPLY (SELECT 3 + (el + 40) / 44 AS m) _m
CROSS APPLY (SELECT el + 28 - 31 * m / 4 AS d) _d
CROSS APPLY (SELECT DATEFROMPARTS (y,m,d) AS EasterSunday) _Easter
WHERE dimdate.BusinessDate = EasterSunday;
-- Labour Day -- first Monday of September
UPDATE #BusCalDet
SET IsHoliday = 1,Description = Description + ' Labour Day'
FROM #BusCalDet AS c1
WHERE c1.[Month] = 9
AND c1.DayOfWeek = 2
AND c1.Day BETWEEN 1 AND 7;
-- Set Thanksgiving
UPDATE #BusCalDet
SET IsHoliday = 1,Description = Description + ' Thanksgiving'
FROM #BusCalDet AS c1
WHERE c1.[Month] = 10
AND c1.DayOfWeek = 2
AND c1.Day BETWEEN 8 AND 14;
---- Christmas
UPDATE #BusCalDet
SET IsHoliday = 1,Description = Description + ' Christmas Holidays'
FROM #BusCalDet
WHERE [Month] = 12
AND Day BETWEEN 26 AND 28
AND (DATEPART(WEEKDAY,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-12-25') IN (1,7) -- Is Christmas on a weekend this year
OR DATEPART(WEEKDAY,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-12-26') IN (1,7)) -- Is Boxingday on a weekend this year
AND DATEPART(dw,BusinessDate) <> 4 -- NOT ON Wednesday!
AND DATEPART(DAY,(SELECT CASE (##DATEFIRST + DATEPART(dw,BusinessDate)) % 7
WHEN 0 THEN DATEADD(DAY,2,BusinessDate) -- Saturday
WHEN 1 THEN DATEADD(DAY,1,BusinessDate) -- Sunday
ELSE BusinessDate
END AS Weekday)) BETWEEN 26
AND 28
OR [Month] = 12 -- Is christmas a week day?
AND Day BETWEEN 25 AND 26
AND IsWeekday = 1;
-- Match orig table Schema
ALTER TABLE #BusCalDet DROP COLUMN Day, Week, [Month], Quarter, [Year], DayOfWeek;
-- Insert in to the main table'
INSERT INTO dbo.BusinessCalendarDetails (BusinessDate,CreatedById,CreatedTime,BusinessCalendarId,IsWeekday,
IsHoliday,[Description])
-- Find Rows that are missing
SELECT DT.BusinessDate,DT.CreatedById,DT.CreatedTime,DT.BusinessCalendarId,DT.IsWeekday,DT.IsHoliday,
DT.[Description]
FROM #BusCalDet DT
LEFT JOIN BusinessCalendarDetails ON BusinessCalendarDetails.BusinessDate = DT.BusinessDate
WHERE BusinessCalendarDetails.Id IS NULL
ORDER BY DT.BusinessDate;-- id

Extract quarters from time

Is is possible to breakdown hour into quarters and extract them?
For example:
7:00-7:15 = 1
7:15-7:30 = 2
etc
I have time column with values such as 09:30
and I want to extract:
hour = 6
quarter = 2
I can take out the hour, but how do I take out the quarter.
Use the datepart function:
select
case
when TimeColumn is null then null
when datepart(minute, TimeColumn) < 15 then 1
when datepart(minute, TimeColumn) < 30 then 2
when datepart(minute, TimeColumn) < 45 then 3
else 4
end
from MyTable

Check if given month+date is present in the data containing range of month+date

Here is my query:
DECLARE #MM INT -- Current month
DECLARE #DD INT -- Current date
SET #MM = 1 -- For testing, set it to January
SET #DD = 1 -- For testing, set it to 01
SELECT xxxID, xxxFK, StartMonth, StartDate, StopMonth, StopDate, NULL AS OKorNOT
FROM xxxTable
ORDER BY xxxFK
And here is the data:
xxxID xxxFK StartMonth StartDate StopMonth StopDate OKorNOT
---------------- ----------- ----------- ----------- ----------- ----------- -----------
8 2287 11 15 1 2 NULL
4 2290 2 1 2 21 NULL
2 2306 9 15 10 31 NULL
3 2306 1 3 1 20 NULL
9 2661 11 15 1 3 NULL
10 2661 5 5 5 31 NULL
5 3778 6 2 9 5 NULL
6 3778 1 1 3 31 NULL
7 3778 5 10 5 31 NULL
1 3778 12 10 12 31 NULL
I need to populate OKorNot column with 1/0 depending on whether the given month-date lies between StartMonth-StartDate and StopMonth-StopDate. This is SQL Server 2000 by the way.
EDIT
The thing here to note that is that there are no years stored in the data and the months-dates may start in, say Nov-15 and end in Jan-15 so on Dec-31 and Jan-1 this case should return true.
Using integer operations only and an imaginary 384-days calendar
Since your dates are combinations of month and day, I tried to create an integer for every such combination, an integer that is unique and also preserves order. To have calculations as simple as possible, we invent a new calendar where all months have exactly 32 days and we act as if our dates are from this calendar. Then to get how many days have past since 1st of January, we have the formula:
DaysPast = 32 * month + day
(OK, it should be 32 * (month-1) + (day-1) but this way it's simpler and we only want to compare dates relatively to one another, not to January 1st. And the result is still unique for every date).
Therefore, we first calculate the DaysPast for our check date:
SET #CHECK = 32 * #MM + #DD
Then, we calculate the DaysPast for all dates (both start and stop ones) in our table:
( SELECT *
, (32 * StartMonth + StartDate) AS Start
, (32 * StopMonth + StopDate ) AS Stop
FROM xxxTable
) AS temp
Then, we have two cases.
First case, when Start = (8-Feb) and Stop = (23-Nov).
Then, the first condition #CHECK BETWEEN Start AND Stop will be true and the dates between Start and Stop will be OK.
The second condition will be False, so no more dates will be OK.
Second case, when Start = (23-Nov) and Stop = (8-Feb). :
Then, the first condition #CHECK BETWEEN Start AND Stop will be false because Start is bigger than Stop so no dates can match this condition.
The second condition Stop < Start will be true, so we also test if
#CHECK is NOT BETWEEN (9-Feb) AND (22-Nov)
to match the dates that are before (9-Feb) or after (22-Nov).
DECLARE #CHECK INT
SET #CHECK = 32 * #MM + #DD
SELECT *
, CASE WHEN
#CHECK BETWEEN Start AND Stop
OR ( Stop < Start
AND #CHECK NOT BETWEEN Stop+1 AND Start-1
)
THEN 1
ELSE 0
END
AS OKorNOT
FROM
( SELECT *
, (32 * StartMonth + StartDate) AS Start
, (32 * StopMonth + StopDate ) AS Stop
FROM xxxTable
) AS temp
ORDER BY xxxFK
It would be easier if you'd stored dates as, well, dates...
Anyway, something like this. I haven't tested. And you need to deal with year boundary which I've done
SELECT
xxxID, xxxFK, StartMonth, StartDate, StopMonth, StopDate,
CASE
WHEN
FullStart <= FullStop AND
DATEADD(month, #MM-1, DATEADD(day, #DD-1, 0)) BETWEEN FullStart AND FullStop
THEN 1
WHEN
FullStart > FullStop AND
DATEADD(month, #MM-1, DATEADD(day, #DD-1, 0)) BETWEEN
FullStart AND DATEADD(year, 1, FullStop)
THEN 1
ELSE 0
END AS OKOrNot
FROM
(
SELECT
xxxID, xxxFK, StartMonth, StartDate, StopMonth, StopDate,
DATEADD(month, StartMonth-1, DATEADD(day, StartDate-1, 0)) AS FullStart,
DATEADD(month, StopMonth-1, DATEADD(day, StopDate-1, 0)) AS FullStop
FROM xxxTable
) foo
ORDER BY xxxFK
edit: added "-1" to all values: if we're already Jan don't add another month...
SELECT *
FROM xxxTable
WHERE (StartMonth < StopMonth OR StartMonth = StopMonth AND StartDate<=StopDate)
AND (#MM > StartMonth OR #MM = StartMonth AND #DD >= StartDate)
AND (#MM < StopMonth OR #MM = StopMonth AND #DD <= StopDate)
OR (StartMonth > StopMonth OR StartMonth = StopMonth AND StartDate>StopDate)
AND ((#MM > StartMonth OR #MM = StartMonth AND #DD >= StartDate)
OR (#MM < StopMonth OR #MM = StopMonth AND #DD <= StopDate))