Many people remember to figure out whether a year is a leap year or not by checking if a year is divisible by four. If it is, it’s a leap year, meaning, the month of February of that year has 29 days and the year has 366 days, and if not, the month of February of that year has 28 days and the year has 365 days. But that’s not accurate as it’s only a part of the rule. Here’s the formal definition of a leap year: “A year is a leap year if it is divisible by 4, except for years divisible by 100, unless they are also divisible by 400.”

In this post, I’ll explain further what that means and share an embedded program that you can run on this page to find out all the leap years in a given time range. For example, if you want to know how many leap days or years occurred since 1980 to 2025, simply enter 1980 as the starting year, and 2025 as the ending year range. To check if a specific year is a leap year, enter the starting year and ending year as the same in the program below. The program will show all the years, if any, that were leap years during the time period you entered and the count of occurrences. To spice things up, I’ll discuss an Excel’s bug that exists to this day (and one that’ll probably never be fixed), and its workaround.
Interative Leap Year Checker
Click (Run icon) to run the program and follow the prompts for inputs. To run it again with different time range, click Run again. You can enter older time range starting from 1900, or even a future year range.
Simple Explanation of the Leap Year Rule and Code Translation
As stated above the formal definition means that if a year is divisible by 400, it’s a leap year, period! If a year is divisible by 4, it’s not necessarily a leap year, because for it to be a leap year, it also must not be divisible by 100.
So in my code, my custom function, I take two arguments, the start year and end year to calculate all possible leap years in that time range, and after calculations it returns an array of years that fully meet the leap year rule. The function code is:def leap_years(start, end):
leap_year_list = [year for year in range(start, end + 1) if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)]
return leap_year_list
It appends the integer (year in this scenario) to a list (an array) only when a year is divisible by 4 and not by 100, or if a year is divisible by 400 and it iterates for each year from the start year variable value until the end variable (ending year) value + 1 (we need to +1 for end argument because Python range’s end parameter is exclusive).
The rest of the code is the main driver that prompts the user for inputs, and another custom function called get_valid_year_input() that validates the inputs with some basic error-checking before calling the leap_years() function above in a while loop until the input is a valid numeric input that is greater or equal to 1900.
That custom function looks like this:def get_valid_year_input(message):
startyear_str = input(message)
if startyear_str.isdigit():
startyear = int(startyear_str)
return startyear
else:
return -1
It checks if the input is alphanumeric, purely alphabetic, or numeric characters. If not all of it is numeric, it will return -1 to the caller (driver code) which checks it and if it is not 1900 or greater, will loop back to calling the above function to prompt the user again for a valid input. This goes on forever until user enters a valid input (or aborts the program).
Once a valid input is received, it then converts the user-input to an integer (for year) and proceeds to ask the user for the ending year (last year in the desired range). The same custom function get_valid_year_input() is called but this time with a different message to show to the user (this parameter is passed to the function, which is displayed by that function in prompt). Same validation applies for the ending year input as well.
The complete driver code looks like this:start_year_num = 0
while start_year_num < 1900:
start_year_num = get_valid_year_input("Enter the first year (whole number, 1900 or later): ")end_year_num = 0
while end_year_num < 1900:
end_year_num = get_valid_year_input("Enter the last year in range (whole number, 1900 or later): ")
# Call the leap year determining function
years = leap_years(start_year_num, end_year_num)
The custom function leap_years() returns a list of leap years in the range (if any; if none, it will return an empty list which appears as “[]”) that can be used to display to the user the final results (i.e. the leap years separated by comma, along with number of leap year occurrences in that range).
Excel Bug We’re Stuck With
You may think this is a simple fact, but did you know that Excel has a well-known leap year bug that dates back to its early days? To this day, Excel incorrectly assumes that the year 1900 was a leap year, even though it wasn’t. By now, you’ve figured out why 1900 was not a leap year as per definition.
This mistake originated from Lotus 1-2-3, an older spreadsheet program that Excel aimed to be compatible with. To maintain consistency, Microsoft deliberately kept the error in Excel, even though fixing it would have been more accurate. The result? Excel recognizes February 29, 1900, as a valid date, even though that day never existed 🙂
As a result, it can cause incorrect date calculations for historical data before March 1, 1900. For example, in Excel, In Excel, we can use a formula to check whether a given year is a leap year. It looks like this:=IF(OR(AND(MOD(A1, 4) = 0, MOD(A1, 100) <> 0), MOD(A1, 400) = 0), "Leap Year", "Not a Leap Year")
The problem is, this formula will incorrectly report 1900 as a leap year due to the bug. You can work around this bug by rewriting the formula to explicitly handle year 1900 as this:=IF(YEAR(A1)=1900, "Not a Leap Year", IF(OR(AND(MOD(A1, 4) = 0, MOD(A1, 100) <> 0), MOD(A1, 400) = 0), "Leap Year", "Not a Leap Year"))
The MOD function in Excel returns the remainder after number is divided by divisor.
To test this in Excel with both formulas, enter this value in A1: 2/29/1900 and make sure A1 is formatted as Date type. You’ll get “Leap Year” with the first formula (which is incorrect because 2/29/1900 never existed), and get “Not a Leap Year” with the second formula (correct result) with the second formula that works around the bug.
Fortunately, my program above does not ail from this bug and does not mistakenly consider 1900 as a leap year. Try it! (run it and enter 1900 for both the first and last year in range and see how many occurrences it shows…it should be zero)
To help me continue to produce and share more content that’s informative, and ad-free, please consider making a one-time or a recurring small donation by clicking here (securely transacted via PayPal™).