Counting Overlapping days in Apex/Flow in Salesforce

Counting Overlapping days in Apex/Flow in Salesforce
Photo by Towfiqu barbhuiya / Unsplash

I saw a question on Trailhead that I thought was particularly interesting. This user had an issue quantifying the total number of days tasks were open on Cases. To do this, they were looking to take the Start and End dates for each Task and subtract to find the amount of time it was open. The twist here was that this user's Tasks could be open concurrently. Let's say I have a task that is open from 1/1/2023 through 1/15/2023. I also have another task that was open from 1/10/2023 through 1/20/2023. If I were using the subtraction approach, I'd get a total of 25 days that tasks were open, when in reality, the number is 20. What if, instead, we focused on taking the earliest Task's opening time and the final Task's closing time? The in-between time would be appropriate, but what if there are gaps in task dates? That also would not accurately represent the total number of open days.

Solution

I solved this by breaking down what we would need and leaning on some out-of-the-box Apex offerings. I converted the solution to something that can run as an Invokable action from within a Flow.

The Flow runs as follows:

  1. Enters with all Cases as entry criteria
    1. This should probably be refined in some way, where only Cases that have had tasks open or close are considered, or perhaps Cases that the logic has never run for.
  2. Gathers task records
  3. Calls the Invokable Apex
    1. Loops the now batched list of Task lists
    2. Runs a while loop that cycles dates, adding them to a set
      1. Sets automatically dedupe, so we are effectively getting all dates independently
    3. Returns a list of day-between counts
  4. Updates a custom field on the Case record

Here is the Apex code for the invokable

💡
Note: I have this set up in a way where both the start and end dates are considered as days to calculate duration. For instance, going from 1/1/24 (Monday) to 1/5/24 (Friday) would result in 5 days of duration as returned by the current config. If you want to change this to make it not include the final day of the set, remove the '=' from the '<=' operator on line 10.
// Written by Davis
public with sharing class countDaysBetween {
    @InvocableMethod(label='Count Days Between' description='Returns a list of ints with the number of total task days open.' category='Account')
    public static List<Integer> countDaysBetween(List<List<Task>> taskListofLists) {
        List<Integer> returnList = new List<Integer>();
        for(List<Task> taskList : taskListofLists){
            if(taskList != null){
                Set<Date> datesBetweenStartEnd = new Set<Date>();
                for(Task t : taskList){
                    while (t.Start_Date__c <= t.End_Date__c) {
                        datesBetweenStartEnd.add(t.Start_Date__c);
                        t.Start_Date__c = t.Start_Date__c.addDays(1);
                    }
                    System.debug(datesBetweenStartEnd);
                }
                returnList.add(datesBetweenStartEnd.size());
            }
            else{
                returnList.add(0);
            }
        }
        return returnList;
    }
}

And that's it! I included debugging information so you can validate that the set of dates the code outputs is accurate. Here are some screenshots:

Here is a Task on a given Case record
Here is another Task on the same Case
The start of the Output set

While there are 34 total days of open tasks, there are only 32 exclusive days of tasks, which is the value that gets reflected on the Case record.

32 exclusive open days of Tasks on the Case

And that's it! You could easily convert the code provided for 1-off use cases, or any place where you need to count exclusive days between in bulk, or quickly!