Forum Discussion
Calculate Date and Time difference considering the weekends and workhours
Hi everybody!
I'm still learning how to use Power BI, I search for this everywhere but I didn't found an answer!
Here is the thing,
I need to calculate the difference between dates and time, but the thing is that I need to calculate just the working days and the workhours.
Taking the first line as an example: (03/11/2016 13:57:22 - 03/01/201613:36:38) the column hours should be 63:38:38
Considering that 03/05 and 06/05 are saturday and sunday, and considering that the work hours are from 08:00AM to 18:00PM.
How can I solve that!
Thank you!
The only possibility I can think of is that there is something wrong with the Calendar'[WorkDay] formula. Could you please check this column formula is like below? The data type of this column should be “True/False”.
If it is actually same as mine, could you please upload your .pbix file to OneDrive and share it with me? In that case I can take a look at your .pbix file and try to solve the problem.
Best Regards,
Herbert
How about the result if we update the “FirstDaySecDiff” measure as below?
FirstDaySecDiff = IF ( Table1[FirstDayEndTime] >= Table1[DateTimeFrom] && Table1[FirstDayEndTime] <= Table1[DateTimeTo], DATEDIFF ( Table1[DateTimeFrom], Table1[FirstDayEndTime], SECOND ), IF ( Table1[FirstDayEndTime] >= Table1[DateTimeFrom] && Table1[FirstDayEndTime] > Table1[DateTimeTo], DATEDIFF ( Table1[DateTimeFrom], Table1[DateTimeTo], SECOND ), 0 ) )Best Regards,
Herbert
43 Replies
- v-haibl-msft
Microsoft Employee
There may be several methods to get the expected result. I’ll divide the difference to three parts, the first day, the middle days and the last day. For details, please refer to following steps.
I’ve also upload my .pbix file here for reference.
- Create a calendar table with following formula. But do not create relationship between these two tables.
Calendar = CALENDAR ( "1/1/2016", "12/31/2016" )
- Create a column in calendar table to mark the working days.
WorkDay = VAR WeekDayNum = WEEKDAY ( 'Calendar'[Date], 2 ) RETURN ( IF ( WeekDayNum = 6 || WeekDayNum = 7, FALSE (), TRUE () ) ) - Create a column to store the working end time of first day.
FirstDayEndTime = DATE ( YEAR ( Table1[DateTimeFrom] ), MONTH ( Table1[DateTimeFrom] ), DAY ( Table1[DateTimeFrom] ) ) & " 18:00:00"
- Create a column to calculate the working seconds of first day.
FirstDaySecDiff = DATEDIFF ( Table1[DateTimeFrom], Table1[FirstDayEndTime], SECOND )
- Create a column to store the working start time of last day.
LastDayStartTime = DATE ( YEAR ( Table1[DateTimeTo] ), MONTH ( Table1[DateTimeTo] ), DAY ( Table1[DateTimeTo] ) ) & " 8:00:00"
- Create a column to store the working end time of last day.
LastDayEndTime = DATE ( YEAR ( Table1[DateTimeTo] ), MONTH ( Table1[DateTimeTo] ), DAY ( Table1[DateTimeTo] ) ) & " 18:00:00"
- Create a column to calculate the working seconds of last day.
LastDaySecDiff = IF ( FORMAT ( Table1[DateTimeFrom], "Short Date" ) <> FORMAT ( Table1[DateTimeTo], "Short Date" ), IF ( Table1[DateTimeTo] >= Table1[LastDayStartTime] && Table1[DateTimeTo] <= Table1[LastDayEndTime], DATEDIFF ( Table1[LastDayStartTime], Table1[DateTimeTo], SECOND ), IF ( Table1[DateTimeTo] > Table1[LastDayEndTime], DATEDIFF ( Table1[LastDayStartTime], Table1[LastDayEndTime], SECOND ), 0 ) ), 0 ) - Create a column to calculate the working seconds of middle days.
MidDaysSecDiff = IF ( FORMAT ( Table1[DateTimeFrom], "Short Date" ) <> FORMAT ( Table1[DateTimeTo], "Short Date" ), 3600 * 10 * ( CALCULATE ( DISTINCTCOUNT ( 'Calendar'[Date] ), FILTER ( 'Calendar', 'Calendar'[Date] > Table1[FirstDayEndTime] && 'Calendar'[Date] < Table1[LastDayStartTime] && 'Calendar'[WorkDay] = TRUE () ) ) - 1 ), 0 ) - Create the final column to calculate the total working hours.
TotalHourDiff = ( Table1[FirstDaySecDiff] + Table1[LastDaySecDiff] + Table1[MidDaysSecDiff] ) / 3600
Note: The data type of columns of “FirstDayEndTime”, “LastDayStartTime” and “LastDayEndTime” should be Date/Time as below.
Best Regards,
Herbert
- dcs136
Advocate I
Herbert,
Thank you so much for your help.
But unfortunately DATEDIFF gives an error "In DATEDIFF function, the start date cannot be greater than the end date"
because some hours on "DateTimeFrom" are greater than the "FirstDayEndTime" as you can see on the picture
I tried to fix this, but I couldn't do it.
Thank you!
- v-haibl-msft
Microsoft Employee
Please update the DAX formula of FirstDaySecDiff column as below and have a try again.
FirstDaySecDiff = IF ( Table1[FirstDayEndTime] >= Table1[DateTimeFrom], DATEDIFF ( Table1[DateTimeFrom], Table1[FirstDayEndTime], SECOND ), 0 )Best Regards,
Herbert
- tmears
Helper III
thank you for contributing, this is excately what i was looking for. One quick question, we have different opening and closing hours for certain customers, do anyone have any suggestions on how i could overcome this issue:
ie customer A is 09:00 to 17:00
Customer b is 08:30 to 17:30 etc etc
Any help would be very much appriciated
- christianfcbmx
Post Patron
This solution present errors in the following cases!
Can sombody share a solution??? (the pbix and the excel data source will be available here):
- Create a calendar table with following formula. But do not create relationship between these two tables.
- AnonymousNot applicable
Really appreciate the work y'all put in to get a working DAX solution. I turned it into a custom M function so that you can easily apply it to multiple columns without having a ton of duplication. Also fixed a couple of small bugs that were in the original DAX.
To use, just copy this into a new blank query and should work out of the box.
Of course you're free to change the start/end times (currently at 8am and 5pm) etc
(from, to) => let // utility functions dateWithSetTime = (dt, t) => DateTime.From(Date.ToText(Date.From(dt)) & " " & Time.ToText(t)), max2 = (first, second) => if first > second then first else second, min2 = (first, second) => if first < second then first else second, isBetween = (t, first, last) => t >= first and t <= last, isWeekend = (day) => Date.DayOfWeek(day) = 5 or Date.DayOfWeek(day) = 6, isWorkday = (day) => not isWeekend(day), numDaysBetween = (f, t) => let start = Date.AddDays(Date.From(from), 1), length = Duration.Days(Date.From(to) - Date.From(from)) - 1, dates = List.Dates(start, length, #duration(1, 0, 0, 0)), workdays = List.Select(dates, each isWorkday(_)) in List.Count(workdays), dur = (days) => Duration.From(days), // variables and initial setup startTime = #time(8,0,0), endTime = #time(17,0,0), firstDayStartTime = dateWithSetTime(from, startTime), firstDayEndTime = dateWithSetTime(from, endTime), lastDayStartTime = dateWithSetTime(to, startTime), lastDayEndTime = dateWithSetTime(to, endTime), endsOnStartDay = Date.From(from) = Date.From(to), // get seconds for first, middle, and last days firstDaySeconds = // make sure that end date is after start date if from > to then error "The start date is greater than the end date. Make sure that you passed the arguments in the right order." else // record 0 time if weekend, starts after day ends, or ends before day starts if isWeekend(Date.From(from)) or from > firstDayEndTime or to < firstDayStartTime then dur(0) else min2( to, firstDayEndTime ) - max2( from, firstDayStartTime ), middleDaysSeconds = if endsOnStartDay then dur(0) else (endTime - startTime) * numDaysBetween(from, to), lastDaysSeconds = // record 0 time if holiday, weekend, already counted, or ends before day starts if isWeekend(Date.From(to)) or endsOnStartDay or to < lastDayStartTime then dur(0) else min2(to, lastDayEndTime) - lastDayStartTime, totalSeconds = Duration.TotalSeconds(firstDaySeconds + middleDaysSeconds + lastDaysSeconds) in totalSeconds / 3600Pro tip: enable M syntax highlighting in Preview features options, it makes it so much more pleasant to write in.- itshudak
Helper I
Sam,
I am actually running into an issue on some of the output.
FirstStartDateTime: 10/7/2017 1:20:00 PM
CreateDate: 10/10/2017 8:39:18 AM
An error occurred in the ‘’ query. Expression.Error: The 'increment' argument is out of range.
Details:
1.00:00:00I'm not sure what is happening here. I kept everything the same, and also changed the startTime, but am getting the same error. Any ideas?
- AnonymousNot applicable
itshudak looks like you passed the arguments in the wrong order (doing that reproduced the exact error for me).
I've updated my code above to give a helpful error message in that case
- AnonymousNot applicable
Anonymous this is fantastic, thank you so much! I did have to make one small change to get the hours to calculate properly - using the default Date.DayofWeek settings, the weekends are days 0 and 6 rather than 5 and 6.
- ebuchholzFrequent Visitor
Could someone help me find the error in this expression? I get blank values where there should be a count, but the 0s work for time differences on the same day.
MidDaysSecDiff =
IF (Table1[DateTimeFrom].[Date] <> Table1[DateTimeTo].[Date],
CALCULATE( DISTINCTCOUNT('Calendar2'[Date]), FILTER('Calendar2','Calendar2'[Date] > Table1[FirstDayEndTime] && 'Calendar2'[Date] < Table1[LastDayStartTime] && 'Calendar2'[WorkDay2] = TRUE())),0) - szqz_531New Member
Hello Herbert,
I am looking at your solution, but can you have a look at my data? The FirstDaySecDiff, LastDaySecDiff and MidDaysSecDiff are too large to be normal. I have no idea where is wrong. Thanks!!!
- Danilo_CastilloFrequent Visitor
Hi everyone, im recently working with power bi and dax, i have just been asked to do this calculation and im not very clear on how to implement the code, i think that understanding the model would help me, could someone help me please?.
regards,