3 Ways to Determine Leap Year in Java

Robert Torok February 3, 2019
3 Ways to Determine Leap Year in Java

Determining if a year is a leap year as a programming task comes up form time to time. This post shows you three different ways to calculate if the given year is a leap year.

From Srcatch

The first approach is writing our own function. The following conditions must be met by the leap years:

  • The year is is divisible by 4.
  • The year is divisible by 400 OR is not divisible by 100.

Translating these rules into actual code we'd end up writing something like this:

public class LeapYearCalculator {
    public static void main(String[] args) {
        System.out.println(isLeapYear(1900));
        System.out.println(isLeapYear(2004));
        System.out.println(isLeapYear(2019));
    }

    public static boolean isLeapYear(int year) {
        return year % 4 == 0 && (year % 400 == 0 || year % 100 != 0);
    }
}

If we run the program we get the following output:

Determining if a year is leap year

Using GregorianCalendar Class

The second method to calculate leap years is using the GregorianCalendar class from the Java JDK. It has a method called isLeapYear — it's pretty starightforward to use:

import java.util.GregorianCalendar;

public class LeapYearCalculator {
    public static void main(String[] args) {
        System.out.println(new GregorianCalendar().isLeapYear(1900));
    }
}

This class has been present in the JDK for a long time (specifically @since JDK1.1) so we can leverage this class even when targeting older Java versions.

Using Year Class Since Java 8

We can also use the java.time.Year class that has been available since Java 8. It provides the isLeap method to decide whether the year is a leap year or not.

import java.time.Year;

public class LeapYearCalculator {
    public static void main(String[] args) {
        System.out.println(Year.isLeap(1900));

    }
}

It's interesting to look into the implementations of the java.util.Year and GregorianCalendar classes to find what's the difference.

Long story short, the GregorianCalendar class takes into account the cutover year (1582) while the java.util.Year class does not. This means that we might get different results for years before 1582. That's something that we'll need to consider when working on an application that deals with historical years.

In fact, the java.util.Year class' isLeap is pretty compact, it's a one-liner. Also, before going forward there's a nice trick to mention that both method leverages: They use the bitwise AND operator to check if the year can be divided by 4:

return (year & 3 == 0) && ...

This might result in some performance boost when this method is being called multiple times.

The implementations of the two methods are different yet they claim they do the same, so the question is obvious: Are they interchangeable?

Let's write a quick test to see if they're equivalent:

import org.junit.Test;

import java.time.Year;
import java.util.GregorianCalendar;

import static org.junit.Assert.assertEquals;

public class LeapYearTest {
    private static final int gregorianCutOverYear = 1582;

    @Test
    public void testLeapYearAfterCutOver() {
        GregorianCalendar gregorianCalendar = new GregorianCalendar();
        for (int i = gregorianCutOverYear; i < 2019; i++) {
            assertEquals(String.valueOf("Year: " + i), gregorianCalendar.isLeapYear(i), Year.isLeap(i));
        }
    }

    @Test
    public void testLeapYearBeforeCutOver() {
        GregorianCalendar gregorianCalendar = new GregorianCalendar();
        for (int i = 0; i < gregorianCutOverYear; i++) {
            assertEquals(String.valueOf("Year: " + i), gregorianCalendar.isLeapYear(i), Year.isLeap(i));
        }
    }

}
GregorianCalendar and java.util.Year are providing different results before the cutover year

As expected, we get the same results when testing years after the cutover year.

However, the unit test fails for the years prior to the cutover year. That said, if you want accurate results for historic years then you should be using the method provided by the GregorianCalendar class.