[SalesForce] Add days to created date excluding weekend and holidays

I have written the following code . But it's not giving result nor any error. My requirement is to add 3 days to CreatedDate, excluding weekend and holidays.

Datetime createdDate = system.now();

Datetime dueDate = createdDate.addDays(3);

Integer holidaysCount = 0;
BusinessHours bh;

List<BusinessHours> temp = [SELECT ID, Name, IsDefault, IsActive From BusinessHours 
                WHERE IsDefault = true ];

for (BusinessHours b : temp) {
    if (b.IsDefault) {
        bh = b;
    }
}

while (createdDate != dueDate) {

    Datetime now = Datetime.newInstance(createdDate.year(), createdDate.month(), createdDate.day(), 0, 0, 0);
    Boolean isHoliday = !BusinessHours.isWithin(bh.Id, now);

    if ((isHoliday) || createdDate.format('E') == 'Sat' || createdDate.format('E') == 'Sun') {
        createdDate = createdDate.addDays(1);
    }
}    

Best Answer

You're doing more work than you have to in a lot of this code, but the core issue is your approach itself. You should create an Integer counter variable for the number of days elapsed, then increment a Datetime pointer.

You also seem to have a fundamental misunderstanding about how the BusinessHours class works. You don't need to check for yourself if the day is a weekend, that's the job of your hours definition in the first place. If you specify hours for Monday-Friday and none on Saturday or Sunday, then the isWithin method will always return false on those weekend days. It doesn't just check holidays.

Here's how I would write a utility class to calculate business days:

public class BusinessDays
{
    public static BusinessHours defaultHours
    {
        get
        {
            if (defaultHours == null)
                defaultHours = [SELECT Id FROM BusinessHours WHERE IsDefault = true];
            return defaultHours;
        }
        private set;
    }

    public static Datetime add(Datetime pointer, Integer days)
    {
        pointer = BusinessHours.nextStartDate(defaultHours.Id, pointer);
        // make sure you're starting at a Datetime within BusinessHours

        for (Integer elapsed = 0; elapsed < days; elapsed++)
        {
            pointer = pointer.addDays(1);
            if (!BusinessHours.isWithin(defaultHours.Id, pointer))
                pointer = BusinessHours.nextStartDate(defaultHours.Id, pointer);
        }
        return pointer;
    }
}

Then your code would simplify to:

Datetime dueDate = BusinessDays.add(createdDate, 3);

Note that the output is in GMT, so you may need to adjust it to include timezone offsets.

Related Topic