SQL Sever - Datediff, hours, but exclude Saturday/ Sundays - sql-server

I'm wondering how to calculate the "number of hours" between two timestamps (2016-02-24 17:30:00 and another, for instance) in SQL server- but excluding Saturday and Sunday's full 48 hour period, if crossed.
This isn't quite the same as pure business hours, but sort of. The reason for this is long-winded and unnecessary.
EDIT: I can also say that the end-date will always be during the week. So really ... the "start date" can simply be transmuted to Monday midnight, if on Sat/ Sun ... then maybe a function include the total week count...

DATEDIFF(Week, date, date2) will return the number of week boundaries that are crossed between the two dates. For SQL Server, this means how many Sundays are between the dates (as opposed to the number of 7 day periods are between them). This means, that if you can indeed assume that start and end date will not be a saturday or sunday, you can subtract 48 X DATEDIFF(Week, date, date2) from your normal DATEDIFF call and that should give you what are after.

I would use the below code
declare #NumberOfHours int
declare #StartTime datetime
declare #EndTime datetime
set #StartTime = '2017-02-02 17:30:00.000'
set #EndTime = '2017-02-07 00:00:00.000'
set #NumberOfHours = DATEDIFF(HOUR,#StartTime,#EndTime)
if(datepart(WEEKDAY, #StartTime)=1)
begin
set #NumberOfHours = #NumberOfHours DATEDIFF(HH,#StartTime,#EndTime)%24
end
else if(datepart(WEEKDAY, #StartTime)=7)
begin
set #NumberOfHours = #NumberOfHours - DATEDIFF(HH,#StartTime,#EndTime)%24
set #NumberOfHours = #NumberOfHours - 24
end
else
begin
set #NumberOfHours = #NumberOfHours - datediff(ww,#StartTime,#EndTime)*48
end
print #NumberOfHours

I would use a calendar table (ex. dbo.DateDimension, ref https://www.mssqltips.com/sqlservertip/4054/creating-a-date-dimension-or-calendar-table-in-sql-server/):
CREATE TABLE dbo.DateDimension
(
DateKey INT NOT NULL PRIMARY KEY,
[Date] DATE NOT NULL,
[Day] TINYINT NOT NULL,
DaySuffix CHAR(2) NOT NULL,
[Weekday] TINYINT NOT NULL,
WeekDayName VARCHAR(10) NOT NULL,
IsWeekend BIT NOT NULL,
IsHoliday BIT NOT NULL,
...
)
and, also, following query:
SELECT SUM(
CASE
WHEN dd.[Date] = CONVERT(DATE, #StartDate) THEN DATEDIFF(MINUTE, #StarDate, DATEADD(DAY, 1, dd.[Date]))
WHEN dd.[Date] = CONVERT(DATE, #EndDate) THEN DATEDIFF(MINUTE, dd.[Date], #EndDate)
ELSE 24 * 60 -- Full day
END) / 60 AS SumOfHours
FROM dbo.DateDimension dd
WHERE dd.[Date] >= CONVERT(DATE, #StartDate) AND dd.[Date] <= CONVERT(DATE, #EndDate)
AND dd.IsWeekend = 0
Above query will compute total amount of minutes for requested period of time and then it will divide by 60 to get number of hours.

Related

Creating a time dimension with specific dates on SQL

I'm making an analysis project on football transfers. I have a model with one Fact Table called FactTransfers and I need to link it to a time dimension but i need a specific range of dates, namely the dates where it's possible to transfer players (from June 1st to September 1st and from January 1st to January 31st).
I have seen some posts related to the matter but they all have code and attributes that I don't need.
Basically what i want is:
Date as primary key,
Day of the month,
Name of the month,
Transfer window (summer or winter),
Year.
I'm not too familiarized with sql code and I have spent hours trying to figure it out without the results I need.
Thank you in advance for all your help!
Here is the code to create and populate your Dim table for Dates. Hope this helps.
CREATE TABLE [dbo].[DimDate]
(
[DateKey] INT primary key,
[Date] DATETIME,
[DayofMonth] TINYINT, -- Field will hold day number of Month
[NameofMonth] VARCHAR(9),--January, February etc
[TransferWindow] VARCHAR(20), -- Summer & Winter
)
--Specify Start Date and End date here
--Value of Start Date Must be Less than Your End Date
DECLARE #StartDate DATETIME = '01/01/2015' --Starting value of Date Range
DECLARE #EndDate DATETIME = '12/31/2025' --End Value of Date Range
DECLARE #CurrentDate AS DATETIME = #StartDate
WHILE #CurrentDate < #EndDate
BEGIN
INSERT INTO [dbo].[DimDate]
SELECT
CONVERT (char(8),#CurrentDate,112) as DateKey,
#CurrentDate AS Date,
DATEPART(DD, #CurrentDate) AS [DayOfMonth],
DATENAME(MM, #CurrentDate) AS [MonthName],
CASE WHEN (MONTH(#CurrentDate) BETWEEN 6 AND 8) OR ( MONTH(#CurrentDate) =9 AND DATEPART(DD, #CurrentDate)=1) THEN 'Summer'
WHEN MONTH(#CurrentDate) =1 THEN 'Winter'
ELSE ''
END AS [TransferWindow]
SET #CurrentDate = DATEADD(DD, 1, #CurrentDate)
END
SELECT * FROM [DimDate]
--DROP TABLE [DimDate]

SQL Server : random date in specific range (including random hours, minutes,...)

I would like to create a random date for a SQL Server update query. I found a lot examples for random days or something similar but I couldn't find something which creates a random date with random date, hours, minutes, seconds AND milliseconds.
This is what I use to create the date randomly but it always gives me 00 as hour, minute, seconds and milliseconds and I don't know how I can randomize them as well.
This is my query:
declare #FromDate date = GETDATE()-2
declare #ToDate date = GETDATE()-1
UPDATE ACCOUNTS
SET dateFinished=
dateadd(day, rand(checksum(newid())) * (1 + datediff(day, #FromDate, #ToDate)), #FromDate)
This is how I'd do it:
Work out the number of seconds between from and to
Get a random number between zero and the number of seconds
Add that random number to the FromDate
Finally randomise the number of milliseconds
DECLARE #FromDate DATETIME = DATEADD(DAY, -2, GETDATE())
DECLARE #ToDate DATETIME = DATEADD(DAY, -1, GETDATE())
DECLARE #Seconds INT = DATEDIFF(SECOND, #FromDate, #ToDate)
DECLARE #Random INT = ROUND(((#Seconds-1) * RAND()), 0)
DECLARE #Milliseconds INT = ROUND((999 * RAND()), 0)
SELECT DATEADD(MILLISECOND, #Milliseconds, DATEADD(SECOND, #Random, #FromDate))
declare #FromDate dateTIME = '2014-01-01'
declare #ToDate dateTIME = '2014-12-31'
select top 100 dateadd(day,rand(checksum(newid()))*(1+datediff(day, #FromDate, #ToDate)), #FromDate) FROM Tabled(give your table name)
SELECT dateaddDATEADD(second,
second, (rand()*60+1),
DATEADD(minute,
(rand()*60+1) ,
DATEADD(day,
(rand()*365+1),
DATEADD(year,
-1,
getdate()))) )

How do I calculate total minutes between start and end times?

How do I calculate total minutes between start and end times? The Start/End times columns are nvarchar and I am declaring them as datetime. I'm not sure if that is my first step or not, I am new to SQL and to declaring.
The final goal is to take Total Minutes, subtract Lunch and Recess (both are minutes) and then multiply by 5 to get total instructional minutes for the week per school.
DECLARE #StartTime datetime, #Endtime datetime
SELECT --[School]
[GradeLevel]
,[StartTime]
,[EndTime]
,(#Endtime - #StartTime) AS 'TotalMinutes'
,[Lunch]
,[Resess]
,[Passing]
FROM [dbo].[StartEndTimes]
Current Output:
GradeLevel StartTime EndTime TotalMinutes Lunch Resess Passing
2-5 7:50 14:20 NULL 20 10 NULL
K-5 7:45 14:20 NULL 20 10 NULL
K-5 7:50 14:20 NULL 20 10 NULL
Maybe something like this is what you want?
select (datediff(minute, starttime, endtime) -lunch -recess) * 5 AS TotalInstruct
from YourTable
If you want to sum it up for all rows then try:
select sum((datediff(minute, starttime, endtime) -lunch -recess) * 5) AS TotalInstruct
from YourTable
If you want to get the number of hours per school you would have to include the schoolfield in the query and use it in the group byclause, and then the query becomes this:
select school, sum((datediff(minute, starttime, endtime) -lunch -recess) * 5) AS TotalInstruct
from YourTable
group by school
Sample SQL Fiddle for the above queries.
If all you want is to find the difference between two dates then you can use DATEDIFF function (http://msdn.microsoft.com/en-us/library/ms189794.aspx)
Example:
DECLARE #startdate datetime2
SET #startdate = '2007-05-05 12:10:09.3312722';
DECLARE #enddate datetime2 = '2007-05-04 12:10:09.3312722';
SELECT DATEDIFF(MINUTE, #enddate, #startdate);
If however your values are in string format you need to convert them prior to passing them to the DATEDIFF function.
Example:
DECLARE #starttexttime nvarchar(100)
SET #starttexttime = '7:50'
DECLARE #starttime datetime2
SET #starttime = CONVERT(datetime2, #starttexttime, 0)
DECLARE #endtexttime nvarchar(100)
SET #endtexttime = '17:50'
DECLARE #endtime datetime2
SET #endtime = CONVERT(datetime2, #endtexttime, 0)
SELECT DATEDIFF(MINUTE, #starttime, #endtime);

Calculating the working hours in a given period

I'm trying to create a function where I can calculate the total working hours in a given period.
Explanation:
inputs are in mm/dd/yyyy hh:mm format.
For example user Inputs:
startDateTime: 01/10/2013 9:30am endDateTime: 12/12/2013 5pm
Daily working hours from 7am to 5pm. For example, if someone starts working at 9am and ends at 8pm them is working hours will calculate as 8 hours (only calculate the working hours between 7am to 5pm).
I do have a holiday list:
Every week's Saturday and Sunday are holiday
and bellow are the public holidays
HOLIDAY LIST:
07/04/yyyy
12/25/yyyy
12/31/yyyy
Memorial Day:- Last Monday of every May
Labor Day:- First Monday of Every September
Thanksgiving Day:- Fourth Thursday of every November
Now my problem is how to find out Memorial Day, Labor Day and Thanksgiving Day with in any given time period.
Final output will be the total working hours in the given period substituting the hours of holiday.
Bellow function I'm using only to calculate working hours eliminating weekends
CREATE FUNCTION [dbo].[WorkTime]
(
#StartDate DATETIME,
#FinishDate DATETIME
)
RETURNS BIGINT
AS
BEGIN
DECLARE #Temp BIGINT
SET #Temp=0
DECLARE #FirstDay DATE
SET #FirstDay = CONVERT(DATE, #StartDate, 112)
DECLARE #LastDay DATE
SET #LastDay = CONVERT(DATE, #FinishDate, 112)
DECLARE #StartTime TIME
SET #StartTime = CONVERT(TIME, #StartDate)
DECLARE #FinishTime TIME
SET #FinishTime = CONVERT(TIME, #FinishDate)
DECLARE #WorkStart TIME
SET #WorkStart = '07:00'
DECLARE #WorkFinish TIME
SET #WorkFinish = '17:00'
DECLARE #DailyWorkTime BIGINT
SET #DailyWorkTime = DATEDIFF(HOUR, #WorkStart, #WorkFinish)
IF (#StartTime<#WorkStart)
BEGIN
SET #StartTime = #WorkStart
END
IF (#FinishTime>#WorkFinish)
BEGIN
SET #FinishTime=#WorkFinish
END
DECLARE #CurrentDate DATE
SET #CurrentDate = #FirstDay
DECLARE #LastDate DATE
SET #LastDate = #LastDay
WHILE(#CurrentDate<=#LastDate)
BEGIN
IF (DATEPART(dw, #CurrentDate)!=1 AND DATEPART(dw, #CurrentDate)!=7)
BEGIN
IF (#CurrentDate!=#FirstDay) AND (#CurrentDate!=#LastDay)
BEGIN
SET #Temp = #Temp + #DailyWorkTime
END
--IF it starts at startdate and it finishes not this date find diff between work finish and start as minutes
ELSE IF (#CurrentDate=#FirstDay) AND (#CurrentDate!=#LastDay)
BEGIN
SET #Temp = #Temp + DATEDIFF(HOUR, #StartTime, #WorkFinish)
END
ELSE IF (#CurrentDate!=#FirstDay) AND (#CurrentDate=#LastDay)
BEGIN
SET #Temp = #Temp + DATEDIFF(HOUR, #WorkStart, #FinishTime)
END
--IF it starts and finishes in the same date
ELSE IF (#CurrentDate=#FirstDay) AND (#CurrentDate=#LastDay)
BEGIN
SET #Temp = DATEDIFF(HOUR, #StartTime, #FinishTime)
END
END
SET #CurrentDate = DATEADD(day, 1, #CurrentDate)
END
-- Return the result of the function
IF #Temp<0
BEGIN
SET #Temp=0
END
RETURN #Temp
END
Please help me how to find out the Thanksgiving Day, Labor Day and Memorial Day in a given period.

convert datetime to UTC value mssql

I have the query like this:
SELECT table.a, table.b, table.c from Table table
How i can cast "table.b" to UTC time just adding CAST into the query?
SELECT
table.a,
**%CAST_TO_UTC_FUNCTION to table.b%**,
table.c
from Table table
I'm not able to cast it separately and declaring local variables.
You can write your query as follows:
SELECT
table.a,
dateAdd(
second,
dateDiff(second, getDate(), getUtcDate()),
table.b) as b_converted_to_UTC,
table.c
from Table table
This converts the values in column bto UTC, by adding to those values the tmie difference that currently exists between the local date (getDate()) and the UTC date (getUtcDate()).
In order for the above query to work, the following must be true:
the date(s) stored in column b must be expressed in local time
the server date/time zone should be properly configured
I solved the problem by creating a temporary table that stores the date range and the current UTC offset.
Here is the partial solution that works for US starting at year 2007, for regions that observe DST.
It can be easily modified to for years before 2007.
Please see the end of the solution for a sample usage
-- GET UTC for time in US for region that observe Daylight Saving Time (meaning will not work for part of Arizona and Hawaii)
DECLARE
#DSTStartDay datetime = '2007-03-1 02:00:00',
#DSTEndDay datetime = '2007-11-1 02:00:00',
#i int = 0,
#currDate datetime,
#offset int
DECLARE
#DST TABLE (StartDate datetime, EndDate datetime, DSTOffset int)
-- Insert into #DST DSTOffset of 0 if not DST date range, and -1 if DST date as temporary offset value
-- Then when we get Time Zone Offset, we can update DSTOffset to actual current offset (hours)
WHILE #i < 20
BEGIN
INSERT #DST
SELECT DATEADD(d,
15 - CASE DATEPART(dw, #DSTStartDay)
WHEN 1 THEN 8
ELSE DATEPART(dw, #DSTStartDay)
END,
#DSTStartDay),
DATEADD(d,
8 - CASE DATEPART(dw, #DSTEndDay)
WHEN 1 THEN 8
ELSE DATEPART(dw, #DSTEndDay)
END,
#DSTEndDay),
-1;
SET #DSTStartDay = DATEADD(yy,1,#DSTStartDay)
INSERT #DST
SELECT DATEADD(d,
8 - CASE DATEPART(dw, #DSTEndDay)
WHEN 1 THEN 8
ELSE DATEPART(dw, #DSTEndDay)
END,
#DSTEndDay),
DATEADD(d,
15 - CASE DATEPART(dw, #DSTStartDay)
WHEN 1 THEN 8
ELSE DATEPART(dw, #DSTStartDay)
END,
#DSTStartDay),
0;
SET #DSTEndDay = DATEADD(yy,1,#DSTEndDay)
SET #i = #i + 1
END
-- Get temporary offset for current date
SET #currDate = GETDATE()
SELECT #Offset = DSTOffset FROM #DST
WHERE StartDate < #currDate AND EndDate >= #currDate
-- Calculate Time Zone Offset (ignore DST) and update DSTOffset in #DST table from temporary to actual offset
SET #Offset = DATEDIFF(hh, GETDATE(), GETUTCDATE()) - #Offset
UPDATE #DST
SET DSTOffset = DSTOffset + #Offset
--SELECT * FROM #DST - look at the table
--Sample usage
DECLARE #myDateNoDST datetime = '2014-03-08 06:00',
#myDateWithDST datetime = '2014-03-09 06:00'
SELECT #myDateNoDST LocalDateNoDST,
(SELECT DATEADD(hh,DSTOffset,#myDateNoDST) FROM #DST d WHERE StartDate < #myDateNoDST AND EndDate >= #myDateNoDST) UTCDateNoDST,
#myDateWithDST LocalDateWithDST,
(SELECT DATEADD(hh,DSTOffset,#myDateWithDST) FROM #DST d WHERE StartDate < #myDateWithDST AND EndDate >= #myDateWithDST) UTCDateWithDST

Resources