Leap Year
Why we need leap years
Analogy
- Our calendar marks a year as exactly 365 days. But the solar system does not keep time in round numbers.
- It takes Earth approximately 365.2422 days—roughly 365 days, 5 hours, 48 minutes, and 46 seconds—to complete one orbit around the sun.
- If we ignored those extra ~6 hours, our calendar would drift by about one day every four years. After a single century, seasons would shift by nearly a month, with summer drifting into autumn and winter.
- Adding one day (February 29) every four years fixes most of the drift. But that slight overcorrection adds up over centuries, requiring two extra rules to keep human calendars in sync with the stars.
Definition
A leap year is a calendar year containing 366 days instead of 365, with an added 29th day in February.
In software, determining a leap year requires checking divisibility by 4, with special rules for century years divisible by 100 and 400.
The three Gregorian rules
Adding a leap day every 4 years adds 24 hours every 4 years (an average of 6 extra hours per year). But the solar excess is only 5 hours, 48 minutes, and 46 seconds.
That means adding a leap day every 4 years overshoots the solar orbit by about 11 minutes each year. Over 400 years, that small surplus accumulates to roughly three extra days.
The Gregorian calendar uses three filters to correct the error:
- The 4-year rule: Any year evenly divisible by 4 is a leap year.
- The 100-year exception: If the year is also divisible by 100 (a century year like 1800 or 1900), it is not a leap year. This removes three surplus days every four centuries.
- The 400-year correction: If the year is divisible by 400 (like 1600 or 2000), it is a leap year after all. This restores one day to fine-tune the calendar.
The Java implementation
In Java, all three rules combine into a single boolean expression:
public class LeapYear { public static boolean isLeapYear(int year) { return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); }}This expression leverages short-circuit evaluation:
- For an ordinary year like 2023:
2023 % 4 == 0evaluates tofalse. Because&&requires both sides to be true, Java never checksyear % 100. It moves directly to the right side of||, sees2023 % 400 == 0isfalse, and returnsfalse. - For a normal leap year like 2024:
2024 % 4 == 0istrue, and2024 % 100 != 0istrue. The left side of||is satisfied, so Java never evaluates the right side at all and immediately returnstrue. - For a century year like 1900:
1900 % 100 != 0isfalse, failing the left side. The right side1900 % 400 == 0is alsofalse, correctly identifying 1900 as a common year. - For a quad-century like 2000: The left side fails because 2000 is divisible by 100, but the right side
2000 % 400 == 0succeeds, correctly returningtrue.
The if-else pattern: checking the exception first
If you prefer explicit if statements instead of a compound boolean expression, you can write the checks by handling the most specific condition first:
public static boolean isLeapYear(int year) { if (year % 400 == 0) { return true; } if (year % 100 == 0) { return false; } return year % 4 == 0;}Because each return immediately exits the function, testing year % 400 == 0 first guarantees that no 400-year exception can be accidentally rejected by the 100-year check below it.
Common mistakes to watch for
Watch out
- Checking only
year % 4 == 0: This is the most common bug in date-handling code. It causes years like 1900, 2100, and 2200 to be falsely treated as leap years. - Confusing AND and OR: Writing
year % 4 == 0 || year % 400 == 0 && year % 100 != 0changes the evaluation grouping. Always group the century check explicitly with parentheses:(year % 4 == 0 && year % 100 != 0) || (year % 400 == 0).
Quick check
Key takeaway
Divisible by 4 adds an extra day; century years take three of them back; every 400 years restores one. In code: (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0).