Forum Discussion

Anonymous's avatar
Anonymous
Not applicable
7 years ago
Solved

Calculating Network Hours / Working Hours

Hello,

 

I have been trying to calculate Network hours(i.e. working time exculding non business hours, weekends, and holidays). I have tried some other solutions(listed below). However, my dataset seems to be more complex. 

 

Withint the data we have the following coloums:

Date/Time OpenedDate/Time ClosedCase NumberExpected Output
2/11/19 3:24 PM2/11/19 3:24 PM10
2/12/19 2:21 PM 2null
2/27/19 3:17 PM2/27/19 3:17 PM30
3/14/19 10:10 AM 4null
2/16/19 7:04 PM2/18/19 9:42 AM51.7
2/15/19 4:32 PM2/16/19 11:06 AM60.966667

 

Context:
I'm trying to caluclate time to solve a customer support case. I have tried following the following solutions.

 

However, since cases can be open after business hours, be closed after business hours, or not be closed yet I have been running into a lot of problems. 

 

The reqiuirements:

  • Business Day: 8am - 5pm
  • Weekends: Saturday and Sunday
  • I would also like to include a holiday calendar.
  • Ideally would also work with a direct query[optional]

Any help or direction would be much appreciated. 

  • Anonymous's avatar
    Anonymous
    7 years ago

    Anonymous -

     

    Here's the date table that is referenced in the Calculated Column. Notice, it has a Weekend attribute to indicate whether it's a weekend. You could add OffDay, which considers both weekends and holidays.

    Date = ADDCOLUMNS(CALENDAR(DATE(2018,1,1),DATE(2020,12,31)),"Weekend",IF(WEEKDAY([Date]) IN {1,7}, TRUE(),FALSE()))

     

    Here's a Calculated Column. It works with weekends. The same logic could be used with Is OffDay instead of Weekend. You'd replace the following bits: 

    var start_date_is_weekend = LOOKUPVALUE('Date'[Weekend],'Date'[Date],INT([Date/Time Opened]))

    var end_date_is_weekend = LOOKUPVALUE('Date'[Weekend],'Date'[Date],INT([Date/Time Closed]))

    'Date'[Weekend] = FALSE()

     

    Hours Difference = 
    //Define hours of workday, in seconds
    var work_day_begins = 3600 * 9 //9AM
    var work_day_ends = 3600 * 17  //5PM
    var seconds_in_workday = work_day_ends - work_day_begins
    
    //Check whether start/end dates occurred on a weekend.
    var start_date_is_weekend = LOOKUPVALUE('Date'[Weekend],'Date'[Date],INT([Date/Time Opened]))
    var end_date_is_weekend = LOOKUPVALUE('Date'[Weekend],'Date'[Date],INT([Date/Time Closed]))
    
    //Get the seconds from midnight. 
    //If it's a weekend, always use the end of the workday value.
    //If outside working hours, snap to the start or end of day.
    var start_time = TIMEVALUE(FORMAT([Date/Time Opened],"HH:mm:ss"))*86400
    var end_time = TIMEVALUE(FORMAT([Date/Time Closed],"HH:mm:ss"))*86400
    var start_time_adj = IF(start_date_is_weekend,work_day_ends,MIN(MAX(start_time,work_day_begins),work_day_ends))
    var end_time_adj = SWITCH(
        TRUE(),
        ISBLANK(end_date_is_weekend),BLANK(),
        end_date_is_weekend,work_day_ends,
        MIN(MAX(end_time,work_day_begins),work_day_ends)
    )
    
    //Find the number of workdays
    var day_diff = COUNTROWS(
        FILTER(
            'Date',
            [Date] > INT([Date/Time Opened]) 
            && [Date] <= INT([Date/Time Closed]) 
            && 'Date'[Weekend] = FALSE()
        )
    )
    
    //Final calculation:
    var time_diff = end_time_adj - start_time_adj
    var working_seconds = (day_diff * seconds_in_workday) + time_diff
    var working_hours = working_seconds / 3600.00
    return IF(ISBLANK([Date/Time Closed]),BLANK(),working_hours)

10 Replies

  • Anonymous's avatar
    Anonymous
    Not applicable

    Anonymous -

     

    Here's the date table that is referenced in the Calculated Column. Notice, it has a Weekend attribute to indicate whether it's a weekend. You could add OffDay, which considers both weekends and holidays.

    Date = ADDCOLUMNS(CALENDAR(DATE(2018,1,1),DATE(2020,12,31)),"Weekend",IF(WEEKDAY([Date]) IN {1,7}, TRUE(),FALSE()))

     

    Here's a Calculated Column. It works with weekends. The same logic could be used with Is OffDay instead of Weekend. You'd replace the following bits: 

    var start_date_is_weekend = LOOKUPVALUE('Date'[Weekend],'Date'[Date],INT([Date/Time Opened]))

    var end_date_is_weekend = LOOKUPVALUE('Date'[Weekend],'Date'[Date],INT([Date/Time Closed]))

    'Date'[Weekend] = FALSE()

     

    Hours Difference = 
    //Define hours of workday, in seconds
    var work_day_begins = 3600 * 9 //9AM
    var work_day_ends = 3600 * 17  //5PM
    var seconds_in_workday = work_day_ends - work_day_begins
    
    //Check whether start/end dates occurred on a weekend.
    var start_date_is_weekend = LOOKUPVALUE('Date'[Weekend],'Date'[Date],INT([Date/Time Opened]))
    var end_date_is_weekend = LOOKUPVALUE('Date'[Weekend],'Date'[Date],INT([Date/Time Closed]))
    
    //Get the seconds from midnight. 
    //If it's a weekend, always use the end of the workday value.
    //If outside working hours, snap to the start or end of day.
    var start_time = TIMEVALUE(FORMAT([Date/Time Opened],"HH:mm:ss"))*86400
    var end_time = TIMEVALUE(FORMAT([Date/Time Closed],"HH:mm:ss"))*86400
    var start_time_adj = IF(start_date_is_weekend,work_day_ends,MIN(MAX(start_time,work_day_begins),work_day_ends))
    var end_time_adj = SWITCH(
        TRUE(),
        ISBLANK(end_date_is_weekend),BLANK(),
        end_date_is_weekend,work_day_ends,
        MIN(MAX(end_time,work_day_begins),work_day_ends)
    )
    
    //Find the number of workdays
    var day_diff = COUNTROWS(
        FILTER(
            'Date',
            [Date] > INT([Date/Time Opened]) 
            && [Date] <= INT([Date/Time Closed]) 
            && 'Date'[Weekend] = FALSE()
        )
    )
    
    //Final calculation:
    var time_diff = end_time_adj - start_time_adj
    var working_seconds = (day_diff * seconds_in_workday) + time_diff
    var working_hours = working_seconds / 3600.00
    return IF(ISBLANK([Date/Time Closed]),BLANK(),working_hours)
    • Anonymous's avatar
      Anonymous
      Not applicable

      Anonymous 

       

      Thank you so much for your help. I did get this query to work with a static data set.

       

      However, in direct query mode the following code does not work:

      //Find the number of workdays
      var day_diff = COUNTROWS(
          FILTER(
              'Date',
              [Date] > INT([Date/Time Opened]) 
              && [Date] <= INT([Date/Time Closed]) 
              && 'Date'[Weekend] = FALSE()
          )
      )

      Error Message: Function 'COUNTROWS' is not allowed as part of calculated column DAX expressions on DirectQuery models.

       

      Is there any work around to this or am I just out of luck?

       

      • Anonymous's avatar
        Anonymous
        Not applicable

        Anonymous - We can either change it to a measure, or re-work the logic. Changing to measure makes sense for DirectQuery.

        Hours Difference Measure = 
        //Define hours of workday, in seconds
        var work_day_begins = 3600 * 9 //9AM
        var work_day_ends = 3600 * 17  //5PM
        var seconds_in_workday = work_day_ends - work_day_begins
        
        //Put Opened, Closed into variables.
        var opened = MAX([Date/Time Opened])
        var closed = MAX([Date/Time Closed])
        
        //Check whether start/end dates occurred on a weekend.
        var start_date_is_weekend = LOOKUPVALUE('Date'[Weekend],'Date'[Date],INT(opened))
        var end_date_is_weekend = LOOKUPVALUE('Date'[Weekend],'Date'[Date],INT(closed))
        
        //Get the seconds from midnight. 
        //If it's a weekend, always use the end of the workday value.
        //If outside working hours, snap to the start or end of day.
        var start_time = TIMEVALUE(FORMAT(opened,"HH:mm:ss"))*86400
        var end_time = TIMEVALUE(FORMAT(closed,"HH:mm:ss"))*86400
        var start_time_adj = IF(start_date_is_weekend,work_day_ends,MIN(MAX(start_time,work_day_begins),work_day_ends))
        var end_time_adj = SWITCH(
            TRUE(),
            ISBLANK(end_date_is_weekend),BLANK(),
            end_date_is_weekend,work_day_ends,
            MIN(MAX(end_time,work_day_begins),work_day_ends)
        )
        
        //Find the number of workdays
        var day_diff = COUNTROWS(
            FILTER(
                'Date',
                [Date] > INT(opened) 
                && [Date] <= INT(closed) 
                && 'Date'[Weekend] = FALSE()
            )
        )
        
        //Final calculation:
        var time_diff = end_time_adj - start_time_adj
        var working_seconds = (day_diff * seconds_in_workday) + time_diff
        var working_hours = working_seconds / 3600.00
        return IF(ISBLANK(closed),BLANK(),working_hours)

        However, this only works for single rows - the total doesn't work. We can get an Additive Measure by wrapping a SUMX:

         

        Hours Difference Measure With Total = SUMX('DateTime Test', [Hours Difference Measure])

        Unfortunately, these may not perform well for large data sets. I'd love to hear about what type of performance you experience!

         

        Regards,

        Nathan

    • jimminy's avatar
      jimminy
      Frequent Visitor

      Thanks for this, I've recently used and slightly adjusted this code for my own report and it brings the desired result. However, I am working with millions of rows, and it becomes SUPER slow. I am wondering if there is another way.. 🙂

       

      Best,

      J