Admins / Consultants / Developers

Leap Year Pitfalls and Solutions in Salesforce

By Andrew Cook

For many admins out there like myself, a new year means time for some tidying up in our Salesforce orgs. A chance to clean up technical debt and make our orgs as fresh as possible.

This year is also a leap year, which I have realized is something many don’t take into consideration when creating things like formula fields and validation rules in Salesforce.

What Is a Leap Year?

A leap year is a year that has one extra day, making it 366 days instead of the usual 365. This extra day is added to help our calendars stay in sync with how long it takes the Earth to go around the Sun. Normally, it takes about 365.25 days for the Earth to complete its orbit, so the leap year with the extra day is a way to catch up and keep our calendars accurate.

You may be wondering, how can a Leap Year affect your Salesforce orgs? The reality is that formula fields, validation rules, and even Apex, can all be affected by a leap year depending on the syntax used.

Example Validation Rule

Let’s take the following example of a validation rule whose purpose is to make sure the date entered in a custom field called Followup_Date__c is within the next 365 days. 

Followup_Date__c - TODAY() > 365

This rule doesn’t take into account leap years, so at some point it would let someone enter a date outside a year. Granted, we are only talking about a day, but when it comes to things like sales, missing a follow-up task by a single day could easily jeopardize the chances of closing a deal.

To alleviate this, we can re-write the validation rule as follows:

IF(
   MOD(YEAR(TODAY()), 400) = 0 || 
   (MOD(YEAR(TODAY()), 4) = 0 && MOD(YEAR(TODAY()), 100) != 0),
   Followup_Date__c - TODAY() > 366,
   Followup_Date__c - TODAY() > 365
)

This rule now checks if the current year is a leap year and then decides whether to use 366 or 365 as the threshold, futureproofing it for all eventualities.

Example Formula Field

Now let’s look at an example formula field. The following is a field you could have on the Contact object to show their age.

TODAY() - Birthdate__c

Again, this doesn’t take leap years into account. This could cause issues, especially with marketing automation, if you have for example an email campaign that fires on someone’s birthday.

We can re-write this in the following way to alleviate any potential problem:

FLOOR(MOD(Date(YEAR(TODAY()), MONTH(Birthdate__c), DAY(Birthdate__c)) - Birthdate__c, 365.2425))

This formula now calculates the days difference between Birthdate__c and the current date, adjusted for the average number of days per year (including leap year probability). FLOOR rounds down the age to whole years.

Example Apex Class

The following Apex Class fetches an account record, extracts its birthdate field, and calculates the current age based on that date.

public class AccountAgeCalculator {

    public static Integer calculateAge(Id accountId) {
        Account account = [SELECT Birthdate FROM Account WHERE Id = :accountId];
        Date today = System.today();
        if (account.Birthdate == null) {
            return null; // handle missing birthdate
        }
        Integer age = today.yearsSince(account.Birthdate);
        return age;
    }
}

This class demonstrates:

  • Accessing a Date field (Birthdate) from an Account record.
  • Using System.today() to get the current date.
  • Calling yearsSince on the Date object to calculate the age in years.

While the yearsSince method on Date objects generally handles calendar differences accurately, it’s still good practice to be precise with date calculations involving age or birthdays. We can rewrite this in the following way to take this into account:

public static Integer calculateAge(Id accountId) {
    Account account = [SELECT Birthdate FROM Account WHERE Id = :accountId];
    Date today = System.today();
    if (account.Birthdate == null) {
        return null; // handle missing birthdate
    }
    Integer daysSinceBirth = today.daysBetween(account.Birthdate);
    Integer yearsBasedOnDays = daysSinceBirth / 365; // Approximate value
    Integer leapYearsBetween = 0;
    // Calculate total leap years from birth year to this year excluding the birth year itself
    for (Integer year = account.Birthdate.year() + 1; year <= today.year(); year++) {
        if (Date.isLeapYear(year)) {
            leapYearsBetween++;
        }
    }
    return yearsBasedOnDays + leapYearsBetween;
}

This version calculates the difference in days between birthdate and today using daysBetween and then adjusts for leap years by iterating through the years in between and counting those that are leap years.

There are libraries available like System.DateUtil in Apex Labs that offer comprehensive date calculations with leap-year adjustments. You can utilize such libraries for even more granular age calculations.

Summary

When we’re sprucing up our Salesforce setups for the new year, it’s easy to miss how leap years can throw a little wrench into our formula fields, validation rules, and Apex classes. Not giving leap years a second thought might mess with your dates, like letting validation rules slide past a year. 

But hey, the fix is simple – tweak your code to work with leap years. Take a peek at tweaks like a revamped validation rule or a more savvy formula field for calculating ages. Throwing leap years into the mix helps devs keep their Salesforce solutions sturdy and spot-on when it comes to date-related stuff.

The Author

Andrew Cook

Andrew is 14x certified and has worked in the ecosystem for 12 years.

Comments:

    John Smith
    July 31, 2024 8:04 pm
    I don't think the last tidbit of code is accurate. Let's say that daysSinceBirth = 14,700 daysSinceBirth / 365 = 40 (since we're using integers here) Let's assume there were 10 leap years over those 40 years, give or take Your method will return a value of 50, adding 10 years to the person's age, not 10 days.

Leave a Reply