Great work!

XP to next level

BugEater

The Leap Year Algorithm: Divisibility Traps

Learning Objectives

  • Memorize the exact three-condition Gregorian leap year algorithm and understand why each condition exists
  • Identify the most common implementation errors: the 1900/2000 confusion and the century exception omission
  • Design a minimal test suite that distinguishes correct from broken leap year implementations

The Complete Algorithm

The Gregorian leap year rule has exactly three conditions, applied in order:

isLeapYear(year):
  if year % 400 == 0: return true    // 400-year exception
  if year % 100 == 0: return false   // century exception
  if year % 4 == 0: return true      // basic rule
  return false

This is not the same as year % 4 == 0. That simplified rule is wrong for century years. The correct algorithm requires checking divisibility by 400 before checking divisibility by 100.

The Most Common Implementation Bug

The most frequent mistake is implementing only the first condition:

// WRONG — misses the century exception
boolean isLeap = (year % 4 == 0);

// WRONG — gets centuries right, misses the 400-year override
boolean isLeap = (year % 4 == 0) && (year % 100 != 0);

// CORRECT
boolean isLeap = (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0);

The second wrong implementation will correctly identify 2100 as non-leap, but will incorrectly identify 2000 as non-leap — a bug that was invisible for 400 years and briefly visible in the year 2000.

The 1900 vs 2000 Test Case

The canonical test case for leap year bugs is the 1900/2000 pair:

Year Correct answer Simple (÷4 only) No-400 rule
1900 NOT leap Leap (wrong) NOT leap (correct)
2000 Leap Leap (correct) NOT leap (wrong)

A system that accepts 1900-02-29 has the simple bug. A system that rejects 2000-02-29 has the no-400 rule bug. These are different bugs with different root causes.

Off-By-One Errors in Date Calculations

Leap year bugs don't only affect February 29 input validation. They also appear in date arithmetic:

  • "What is one year after 2000-02-29?" — a correct implementation returns 2001-02-28; a broken one may return March 1 or crash
  • "How many days between 1999-01-01 and 2001-01-01?" — must include the 366 days of leap year 2000

Testing Strategy

A minimal test suite for leap year validation needs exactly five cases:

Input Expected Tests
2024-02-29 Valid Basic divisible-by-4 case
2023-02-29 Invalid Non-leap year
2000-02-29 Valid 400-year override (catches no-400 bug)
1900-02-29 Invalid Century exception (catches simple-rule bug)
2100-02-29 Invalid Future century exception

These five cases form a decision table that uniquely identifies which variant of the leap year bug is present in a system.

Quiz

A developer writes: boolean isLeap = (year % 4 == 0);. Which test case will expose this as incorrect?

What does the correct leap year algorithm check FIRST?

A system correctly rejects 1900-02-29 but incorrectly rejects 2000-02-29. Which bug does this indicate?

How many test cases are needed to distinguish all common variants of the leap year bug?