Salesforce business hours calculation on start and end date - salesforce

I am trying to calculate difference between to dates in formula field on start and end date. I am not getting accurate value.
I should calculate hours in weekdays that is Monday to Friday and 9 AM to 6 PM EST between given dates
below is my formula:
ROUND( 9 * (
( 5 * FLOOR( ( DATEVALUE( End_Date_Time__c ) - DATE( 1900, 1, 8) ) / 7) +
MIN(5,
MOD( DATEVALUE( End_Date_Time__c ) - DATE( 1900, 1, 8), 7) +
MIN( 1, 24 / 9 * ( MOD( End_Date_Time__c - DATETIMEVALUE( '1900-01-08 14:00:00' ), 1 ) ) )
)
)
-
( 5 * FLOOR( ( DATEVALUE( Start_Date_Time__c ) - DATE( 1900, 1, 8) ) / 7) +
MIN( 5,
MOD( DATEVALUE( Start_Date_Time__c ) - DATE( 1996, 1, 1), 7 ) +
MIN( 1, 24 / 9 * ( MOD( Start_Date_Time__c - DATETIMEVALUE( '1900-01-08 14:00:00' ), 1) ) )
)
)
),
0 )
In some scenarios I am getting expected results but in most not correct values.

Related

Calculate Salesforce Case response time

I'm new with salesforce formula and I have this requirement to compute the business hours with mins. in salesforce.
what i did is that i set-up one field in case objects (data type = formula).
and i applied the formula below. it's working however I cannot get accurate minutes value.
Case 1: I'm getting 2,088.00 but if via manual computation i should get 2088.25
Date/Time Opened
LastModifiedDate
5/24/2021 4:45 PM
4/13/2022 5:10 PM
Case 2: I'm getting 0 as result but if via manual computation i should get 0.11
Date/Time Opened
LastModifiedDate
11/22/2022 11:38 AM
11/22/2022 11:49 AM
Based from some articles salesforce is in GMT so what i did is i get the equivalent of 08:30 SGT to GMT which in this case 12:30 (if my understanding is correct)
Formula:
ROUND( 9 * (
( 5 * FLOOR( ( DATEVALUE( LastModifiedDate) - DATE( 1900, 1, 8) ) /
7) +
MIN(5,
MOD( DATEVALUE( LastModifiedDate ) - DATE( 1900, 1, 8), 7) +
MIN( 1, 24 / 8 * ( MOD( LastModifiedDate - DATETIMEVALUE( '1900-01-08 12:30:00' ), 1 ) ) )
)
)
-
( 5 * FLOOR( ( DATEVALUE( CreatedDate) - DATE( 1900, 1, 8) ) /
7) +
MIN( 5,
MOD( DATEVALUE( CreatedDate ) - DATE( 1996, 1, 1), 7 ) +
MIN( 1, 24 / 8 * ( MOD( CreatedDate - DATETIMEVALUE( '1900-01-08 12:30:00' ), 1) ) )
)
)
),2
)
Thank you in advance for your help..

fiscal year sql server

I want to have a function that calculates the fiscal year. The fiscal year must begin on the first Monday in March. Thank you!
example:
CREATE FUNCTION dbo.fnc_FiscalYear( #AsOf DATETIME )
RETURNS INT
AS BEGIN
DECLARE #Answer INT
SET DATEFIRST 1
IF ( MONTH(#AsOf) < 3 )
or MONTH(#AsOf=3) and datename(weekday, #AsOf) = 'Monday' and datepart(day, #AsOf)>=1 and datepart(day, #AsOf)<=7;
SET #Answer = YEAR(#AsOf) - 1
ELSE SET #Answer = YEAR(#AsOf)
RETURN #Answer
END
GO
but it's not working
It looks as though there are a number of syntax errors with your script. Try this instead. I've removed the SETs and returned at the point of the if statements. Also, note the grouping of the if statements.
CREATE FUNCTION dbo.fnc_FiscalYear( #AsOf DATETIME )
RETURNS INT
AS BEGIN
DECLARE #Answer INT
IF (( MONTH(#AsOf) < 3 )
OR (MONTH(#AsOf) = 3
AND DATENAME(weekday, #AsOf) = 'Monday'
AND datepart(day, #AsOf) >= 1
AND datepart(day, #AsOf)<=7))
RETURN (YEAR(#AsOf) - 1)
RETURN YEAR(#AsOf)
END
GO
The logic for this is tricky -- especially for the first week of March:
CREATE FUNCTION dbo.fnc_FiscalYear (
#AsOf DATETIME
)
RETURNS INT AS
BEGIN
RETURN( CASE WHEN MONTH(#AsOf) < 3 THEN YEAR(#AsOf) - 1
WHEN MONTH(#AsOf) > 3 THEN YEAR(#AsOf)
WHEN DAY(#AsOf) >= 7 THEN YEAR(#AsOf)
WHEN DATENAME(#AsOf) = 'Monday' OR
DATENAME(#AsOf) = 'Tuesday' AND DAY(#AsOf) >= 2 OR
DATENAME(#AsOf) = 'Wednesday' AND DAY(#AsOf) >= 3 OR
DATENAME(#AsOf) = 'Thursday' AND DAY(#AsOf) >= 4 OR
DATENAME(#AsOf) = 'Friday' AND DAY(#AsOf) >= 5 OR
DATENAME(#AsOf) = 'Saturday' AND DAY(#AsOf) >= 26
THEN YEAR(#AsOf)
ELSE YEAR(#AsOf) - 1
END);
END ;
GO
( ##DateFirst + DatePart( weekday, SampleDate ) - 1 ) % 7 + 1 will always return an integer from 0 to 6 with 0 corresponding to Sunday regardless of the setting of DateFirst or Language and without string manipulation.
Tweaking the expression to ( ##DateFirst + DatePart( weekday, SampleDate ) - 2 ) % 7 shifts the start of the cycle from Sunday to Monday and the range to 0 to 6 which, in turn, simplifies the following code:
create function dbo.FiscalYear( #Date as Date )
returns Int
begin
return Year( #Date ) - case
when Month( #Date ) <= 2 then 1 -- January and February are always part of the prior year.
-- In March it is the prior year only in the first week and if the calculated
-- DoW is less than the day of the month.
when Month( #Date ) = 3 and Day( #Date ) < 7 and
( ##DateFirst + DatePart( weekday, #Date ) - 2 ) % 7 >= Day( #Date ) then 1
else 0 end;
end;
Test the function with sample data:
with
-- Generate sample dates for 20 years.
YearOffsets as (
select 0 as YearOffset
union all
select YearOffset + 1
from YearOffsets
where YearOffset < 20 ),
SampleDates as (
select Cast( DateAdd( year, YearOffset, '2000-02-28' ) as Date ) as SampleDate, 1 as Counter
from YearOffsets
union all
select DateAdd( day, 1, SampleDate ), Counter + 1
from SampleDates
where Counter < 10 )
-- Calculate the Fiscal Year for each sample date.
select SampleDate, DateName( weekday, SampleDate ) as WeekDay,
( ##DateFirst + DatePart( weekday, SampleDate ) - 2 ) % 7 as DayOfWeek, -- 0 = Monday.
dbo.FiscalYear( SampleDate ) as FiscalYear
from SampleDates
order by SampleDate;

Can't convert time to mm:ss

I have a table with 7 columns:
Start_hour | Start_minute | Start_second | End_hour | End_minute | end_second | date
My task is to sum the differences between start and end times on current day. And my query already does that. Sadly I need the output to be in minutes:seconds only. For Example the total sum of time differences is 2 hour 26 minutes and 52 seconds.
I need my output to like this:
126:52
My query right now looks like this:
SELECT
RIGHT(CONVERT(CHAR(8),
DATEADD(SECOND,
SUM(DATEDIFF(SECOND,
Timefromparts(start_hour, start_minute, start_second, 0, 0),
Timefromparts(end_hour, end_minute, end_second, 0, 0))
), 0), 108), 5)
FROM
opoznienia
WHERE
YEAR(data) = YEAR(GETDATE())
AND MONTH(data) = MONTH(GETDATE())
AND DAY(data) = DAY(GETDATE())
Sample data:
Start_hour | Start_minute | Start_second | End_hour | End_minute | end_second | date
10 15 0 10 30 30 2018-11-27 14:40:53.680
10 15 0 10 30 30 2018-11-30 10:16:20.610
6 10 30 6 23 45 2018-12-02 01:00:27.243
8 10 0 8 53 45 2018-12-02 14:42:48.663
10 5 13 10 55 23 2018-12-02 14:53:03.560
Output of the query above:
47:13 (without RIGHT command it would be 01:47:13)
The wanted output:
107:13
You need to calculate the number of seconds and then format it as you want. From number of seconds you can calculate the minutes as division by 60, where the remainder are the seconds:
declare #NumberOfSeconds int = 7612
-- Returns 126:52
select concat(#NumberOfSeconds / 60, ':', FORMAT(#NumberOfSeconds % 60, 'D2'))
Here is one option:
SELECT
numSeconds,
CASE WHEN numSeconds / 60 <= 100
THEN RIGHT('00' + CONVERT(VARCHAR(20), numSeconds / 60), 2)
ELSE CONVERT(VARCHAR(20), numSeconds / 60) END
+ ':' +
CASE WHEN numSeconds / 60 <= 100
THEN RIGHT('00' + CONVERT(VARCHAR(20), numSeconds % 60), 2)
ELSE CONVERT(VARCHAR(20), numSeconds % 60) END AS output
FROM yourTable;
Demo
The ugliness in the code has to do with that you expect a minimum of two digits for the minute and second components. So, we have to pad each component with zeroes in the case where minutes or seconds happens to be just a single digit.
The other answers are better, but to understand what may be missing in what you're trying to do - you can look at following where DatePart is used.
-->"
..(without RIGHT command it would be 01:47:13)
" This is your clue that you that you need to work with the parts and not the whole...
SELECT Cast
(
Datepart
(
hour,
DATEADD(second,sum(datediff(second,
TIMEFROMPARTS ( Start_hour, Start_minute, Start_second, 0, 0),
TIMEFROMPARTS ( End_hour, End_minute, End_second, 0, 0))),0)
) * 60
+
Datepart
(
minute,
DATEADD(second,sum(datediff(second,
TIMEFROMPARTS ( Start_hour, Start_minute, Start_second, 0, 0),
TIMEFROMPARTS ( End_hour, End_minute, End_second, 0, 0))),0)
) As varchar)
+
':'
+
Cast
(
Datepart
(
second,
DATEADD(second,sum(datediff(second,
TIMEFROMPARTS ( Start_hour, Start_minute, Start_second, 0, 0),
TIMEFROMPARTS ( End_hour, End_minute, End_second, 0, 0))),0)
) As varchar) as result
from minsec
Try the following
CREATE TABLE T(
StartHour INT,
StartMinute INT,
StartSecond INT,
EndHour INT,
EndMinute INT,
EndSecond INT,
[Date] DATE
);
INSERT INTO T VALUES
(10, 15, 0 , 10, 30, 30, '2018-11-27'),
(10, 15, 0 , 10, 30, 30, '2018-11-30'),
(6 , 10, 30, 6 , 23, 45, '2018-12-02'),
(8 , 10, 0 , 8 , 53, 45, '2018-12-02'),
(10, 5 , 13, 10, 55, 23, '2018-12-02');
SELECT *,
CAST( (DATEDIFF(Hour, StartTime, EndTime) * 60) +
(DATEDIFF(Minute, StartTime, EndTime) % 60) AS VARCHAR
) + ':' +
CAST(DATEDIFF(Second, StartTime, EndTime) % 60 AS VARCHAR)
FROM
(
SELECT [Date],
TIMEFROMPARTS(StartHour, StartMinute, StartSecond, 0, 0) StartTime,
TIMEFROMPARTS(EndHour, EndMinute, EndSecond, 0, 0) EndTime
FROM T
) TT
You can also SUM() and GROUP BY [Date] if you want to.
SELECT [Date],
CAST( SUM( (DATEDIFF(Hour, StartTime, EndTime) * 60) +
(DATEDIFF(Minute, StartTime, EndTime) % 60)
) AS VARCHAR
) + ':' +
CAST(SUM(DATEDIFF(Second, StartTime, EndTime) % 60) AS VARCHAR)
FROM
(
SELECT [Date],
TIMEFROMPARTS(StartHour, StartMinute, StartSecond, 0, 0) StartTime,
TIMEFROMPARTS(EndHour, EndMinute, EndSecond, 0, 0) EndTime
FROM T
) TT
GROUP BY [Date]
Demo
UPDATE
It seems like you are looking for
SELECT [Date],
CAST(SUM(DATEDIFF(Second, StartTime, EndTime)) / 60 AS VARCHAR) + ':' +
CAST(SUM(DATEDIFF(Second, StartTime, EndTime)) % 60 AS VARCHAR) [MM:SS]
FROM
(
SELECT [Date],
TIMEFROMPARTS(StartHour, StartMinute, StartSecond, 0, 0) StartTime,
TIMEFROMPARTS(EndHour, EndMinute, EndSecond, 0, 0) EndTime
FROM T
) TT
GROUP BY [Date];
Returns:
+---------------------+--------+
| Date | MM:SS |
+---------------------+--------+
| 27/11/2018 00:00:00 | 15:30 |
| 30/11/2018 00:00:00 | 15:30 |
| 02/12/2018 00:00:00 | 107:10 |
+---------------------+--------+
Demo

T-SQL : find the employee count as average by the number of months in the year

I have table called Employee with these columns:
Id (identity)
EmploymentStartDate (datetime),
EmploymentEndDate (nullable datetime),
My query:
DECLARE #FromYear int = 2010, #ToYear int = 2017;
WITH YEARS AS
(
SELECT #FromYear As TheYear
UNION ALL
SELECT TheYear + 1
FROM YEARS
WHERE TheYear < #ToYear
)
SELECT
Y.TheYear,
SUM(CASE
WHEN YEAR(EmploymentStartDate) <= Y.TheYear
AND (EmploymentEndDate IS NULL OR YEAR(EmploymentEndDate) >= Y.TheYear)
THEN 1
ELSE 0
END) WorkingEmployeeCount,
SUM(CASE
WHEN YEAR(EmploymentStartDate) = Y.TheYear
THEN 1
ELSE 0
END) StartedEmployeeCount,
SUM(CASE
WHEN YEAR(EmploymentEndDate) = Y.TheYear
THEN 1
ELSE 0
END) SeparatedEmployeeCount
FROM
YEARS Y
CROSS JOIN
Employees E
GROUP BY
Y.TheYear
When I run this query, I get these results:
TheYear - WorkingEmployeeCount - StartedEmployeeCount - SeparatedEmployeeCount
---------------------------------------------------------------
2010 - 1 - 1 - 0
2011 - 2 - 1 - 0
2012 - 2 - 0 - 0
2013 - 2 - 0 - 0
2014 - 2 - 0 - 0
2015 - 4 - 2 - 1
2016 - 3 - 0 - 0
2017 - 6 - 3 - 2
Question:
I need to use below formula. First I want to find every year's months count then AVG of EmployeeCount per year.
EmployeeCount / MonthsCountPerYear then get AVG
If I try below query it is not working for me (I can not create a solution)
(AVG(EmployeeCount / (CASE WHEN TheYear = DATE(GETUTCDATE) THAN 2 ELSE 12 END))) AS AvgEmployeeCount
What I want should be as below
TheYear - WorkingEmployeeCount - StartedEmployeeCount -SeperatedEmployeeCount - AvgEmployeeCount
2010 - 1 - 1 - 0 - 1,30
2011 - 2 - 1 - 0 - 1,20
2012 - 2 - 0 - 0 - 1,00
2013 - 2 - 0 - 0 - 3,50
2014 - 2 - 0 - 0 - 5,33
2015 - 4 - 2 - 1 - 7-33
2016 - 3 - 0 - 0 - 9-34
2017 - 6 - 3 - 2 - 1,15
How can I find employee count avg for every year according to months in a year? Any help will be appreciated. Thank you
Solution:
DECLARE #MONTH INT = -100
DECLARE #ENDDATE DATE = CAST(GETUTCDATE() AS DATE)
DECLARE #STARTDATE DATETIME = DATEADD(M, #MONTH, #ENDDATE);
WITH CALENDAR AS
(
SELECT #STARTDATE F, YEAR(#STARTDATE) Y, MONTH(#STARTDATE) M
UNION ALL
SELECT DATEADD(MONTH, 1, F), YEAR(DATEADD(MONTH, 1, F)), MONTH(DATEADD(MONTH, 1, F))
FROM CALENDAR
WHERE DATEADD(MONTH, 1, F) <= #ENDDATE
)
SELECT
X.Y AS 'Year',
COUN
T(X.Y) AS 'MonthCountInYear',
CAST(AVG(X.EmployeeCount) AS DECIMAL(18,2)) AS AvgEmployeeCount,
SUM(X.StartedEmployeeCount) AS StartedEmployeeCount,
SUM(X.EndedEmployeeCount) AS EndedEmployeeCount,
CASE WHEN AVG(X.EmployeeCount) != 0 THEN SUM(X.EndedEmployeeCount) / AVG(X.EmployeeCount) ELSE 0.0 END AS ConversionRate
FROM
(
SELECT
C.Y,
C.M,
SUM
(
CASE WHEN
(YEAR(EmploymentStartDate) < C.Y OR (YEAR(EmploymentStartDate) = C.Y AND MONTH(EmploymentStartDate) <= C.M))
AND (EmploymentEndDate IS NULL OR (YEAR(EmploymentEndDate) > C.Y OR (YEAR(EmploymentEndDate) = C.Y AND MONTH(EmploymentEndDate) >= C.M)))
THEN 1.0
ELSE 0.0
END
) EmployeeCount,
SUM
(
CASE WHEN
YEAR(EmploymentStartDate) = C.Y AND MONTH(EmploymentStartDate) =
C.M
THEN 1
ELSE 0
END
) StartedEmployeeCount,
SUM
(
CASE WHEN
YEAR(EmploymentEndDate) = C.Y AND MONTH(EmploymentEndDate) = C.M
THEN 1
ELSE 0
END
) EndedEmployeeCount
FROM
CALENDAR C
CROSS JOIN
Employees E
WHERE
IsDeleted = 0 AND EmploymentStartDate IS NOT NULL
GROUP BY
C.Y, C.M
) X
GROUP BY
X.Y

Rounding Datetime to nearest 15 minutes in SQL Server

I have a problem in SQL Server with rounding datetime. I got datetime in column rec_datetime, but I want to round this datetime in a new column r_datetime, which has to be rounded to nearest 15 min, for the whole column rec_datetime.
Example:
[2015-11-24 19:06:00.000] - expected result -> [2015-11-24 19:00:00.000]
[2015-11-24 19:09:00.000] - expected result -> [2015-11-24 19:15:00.000]
Is it possible to round it via select for whole column? Something like :
select round(rec_datetime.......
Round-down, round-nearest & round-up to nearest 15mins
DATEADD( minute, ( DATEDIFF( minute, 0, dateTimeX ) / 15 ) * 15, 0 ) AS dateTimeRoundDown
DATEADD( minute, ( DATEDIFF( minute, 0, DATEADD( second, ( 15 * 60 ) / 2, dateTimeX ) ) / 15 ) * 15, 0 ) AS dateTimeRoundNearest
DATEADD( minute, ( DATEDIFF( minute, 0, DATEADD( minute, 15 , dateTimeX ) ) / 15 ) * 15, 0 ) AS dateTimeRoundUp
Round Down
DATEADD( minute, ( DATEDIFF( minute, 0, dateTimeX ) / 15 ) * 15, 0 ) AS dateTimeRoundDown
Get the offset in minutes (number of minutes since the base-date):
DATEDIFF( minute, 0, dateTimeX )
Round-down to 15 minute block by integer dividing:
DATEDIFF( minute, 0, dateTimeX ) / 15 ) * 15
Add the base-date back in minutes:
DATEADD( minute, ( DATEDIFF( minute, 0, dateTimeX ) / 15 ) * 15, 0 )
Round Nearest
DATEADD( minute, ( DATEDIFF( minute, 0, DATEADD( second, ( 15 * 60 ) / 2, dateTimeX ) ) / 15 ) * 15, 0 ) AS dateTimeRoundNear
15 / 2 minutes is added to the offset.
Needs to be in seconds due to the integer division.
Round Up
DATEADD( minute, ( DATEDIFF( minute, 0, DATEADD( minute, 15, dateTimeX ) ) / 15 ) * 15, 0 ) AS dateTimeRoundUp
15 minutes is added to the offset
Base Date
I generally use a base date of 0 which is the SQL Server 'epoch'
SELECT DATEADD( minute, 0, 0 ) -- '1900-01-01 00:00:00.000'
Because DATEADD() & DATEDIFF() use the SQL Server data type of INT (32 bits) for the parameters, for dates in the very far future, this may cause an overflow.
Using another fixed date, eg '2010-01-01', will avoid the overflow.
The chosen base-date must have a time part of 00:00:00
Using a base-date and integer division, no casting & no floating point operations are required.
Unit Testing
DECLARE #start DATETIME = '2017-04-20 21:00:00'
DECLARE #end DATETIME = '2017-04-20 22:00:00'
;WITH CTE_dateTimes AS
(
SELECT #start AS dateTimeX
UNION ALL
SELECT DATEADD( minute, 1, dateTimeX )
FROM CTE_dateTimes
WHERE DATEADD( minute, 1, dateTimeX ) <= #end
)
SELECT dateTimeX,
DATEADD( minute, ( DATEDIFF( minute, 0, dateTimeX ) / 15 ) * 15, 0 ) AS dateTimeRoundDown,
DATEADD( minute, ( DATEDIFF( minute, 0, DATEADD( second, ( 15 * 60 ) / 2, dateTimeX ) ) / 15 ) * 15, 0 ) AS dateTimeRoundNearest,
DATEADD( minute, ( DATEDIFF( minute, 0, DATEADD( minute, 15 , dateTimeX ) ) / 15 ) * 15, 0 ) AS dateTimeRoundUp
FROM CTE_dateTimes
Try something like this:
SELECT
dateadd(minute, datediff(minute, '1999-12-31 23:52:30', col) / 15*15, '2000-01-01')
FROM (values('2015-11-24 19:06:00.000'),('2015-11-24 19:09:00.000')) x(col)
Result:
2015-11-24 19:00:00.000
2015-11-24 19:15:00.000
In this case you need to round the minute to nearest 15
try query like this:
SELECT
original_datetime
--: this will give you the minute part
, datepart(minute, original_datetime) AS minuteFromDate
--: now get the nearest minute part to 15min, round it nearest to 15
, round(datepart(minute, original_datetime) * 1.0 / 15 , 0) * 15.0 AS roundedToNearest15
-- now remove the minute from original datetime, & add the rounded minute to the resultant datetime value
-- this will give you expected result
, dateadd(minute
, (round(datepart(minute, original_datetime) * 1.0 / 15 , 0) * 15.0)
, dateadd(minute, -datepart(minute, original_datetime), original_datetime)
) as rec_datetime
FROM (values('2015-11-24 19:06:00.000')
,('2015-11-24 19:09:00.000')
,('2015-11-24 19:56:00.000')
,('2015-11-24 19:48:00.000')
) x(original_datetime)
Nearest 15 minutes:
SELECT DateTimeX = GETDATE(), RoundedDateTimeX = CAST(CAST(CAST(GETDATE() as float) * (24 * 60/15) + 0.5 as bigint) / (24.0 * 60/15) as datetime)
Nearest 15 minutes rounded up:
SELECT DateTimeX = GETDATE(), RoundedDateTimeX = CAST(CAST(CAST(GETDATE() as float) * (24 * 60/15) + 1 as bigint) / (24.0 * 60/15) as datetime)
Note: The decimal 24.0 in devisor changes type back to float.

Resources