Problem overview
The method isLeapYear takes a year and returns a boolean: true for a leap year and false otherwise. The rule has two layers. A leap year is divisible by 4. But a year that is a multiple of 100, such as 1800 or 1900, must also be divisible by 400 to count.
The examples: 2020 is divisible by 4 and not a multiple of 100, so it is a leap year. 2021 is not divisible by 4, so it is not. 1968 is divisible by 4, so it is. 1900 is a multiple of 100 but not of 400, so it is not.
Approach
You could write the rule as one expression with && and ||. This lesson instead uses a sequence of checks, each ending the method with a return. Because each check only runs when the earlier ones didn't fire, the order of the checks carries part of the rule.
The order is from the most special case to the most general:
- Divisible by 400: always a leap year. This also catches 2000, which is divisible by 100 as well.
- Divisible by 100 (and so not by 400): never a leap year.
- Divisible by 4 (and so not by 100): a leap year.
- Anything else: not a leap year.
If you tested 100 first, 2000 would be rejected wrongly. Testing 400 first is what lets the century rule say "false" safely.
Step by step
- Check 400. If
year % 400 === 0, returntrue. The remainder operator%gives what is left after division, and a remainder of 0 means the division is exact. Compare with===, which tests equality without converting types. - Check 100. If
year % 100 === 0, returnfalse. A year that reaches this line is a multiple of 100 but wasn't a multiple of 400, like 1900. - Check 4. If
year % 4 === 0, returntrue. Century years are already handled, so for the years that arrive here, divisibility by 4 is the whole rule. 2020 and 1968 return here. - Return
falseafter the three checks. The year isn't divisible by 4, as with 2021. Noelseis needed, because every earlier branch has already returned.
The solution in TypeScript
class Solution {
isLeapYear(year: number): boolean {
if (year % 400 === 0) {
return true;
}
if (year % 100 === 0) {
return false;
}
if (year % 4 === 0) {
return true;
}
return false;
}
}
Notes on the TypeScript details:
%works on anynumber. There is no separate integer type. The remainder keeps the sign of the dividend:-7 % 4is-3. Negative years are rare, but-4 % 4evaluates to-0, and-0 === 0istrue, so the checks still behave.- Non-integer years are not rejected.
2021.5 % 4is1.5, so it is not a leap year, but the method makes no special effort to treat it as an error. Types do not check this:numberallows decimals, and the annotation is erased at run time. - The return type is
boolean. The method could also return the condition directly, but separate returns keep each rule visible.
Following four years through the checks
Tracing a few years shows how each one leaves the method.
- 2000.
2000 % 400is 0, so the first check returnstrue. The later checks never run. - 1900.
1900 % 400is 300, so the first check fails.1900 % 100is 0, so the second check returnsfalse. - 2020.
2020 % 400is 20 and2020 % 100is 20, so both checks fail.2020 % 4is 0, so the third check returnstrue. - 2021. All three remainders are non-zero (
2021 % 4is 1), so the method reaches the finalreturn false;.
The one-expression alternative
The same rule can be written as a single return: return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;. It gives the same answers for the years 2020, 2021, 1968, 1900, 2000, 1800, 2024 and 2100. The chain of checks is easier to read line by line, which is why the lesson builds it that way.
Complexity
The method does at most three remainder operations and returns. Time and extra space are both O(1).
Edge cases and common mistakes
- Wrong order. Checking 4 first would answer
truefor 1900, since 1900 is divisible by 4 too. The checks must go from 400 to 100 to 4. - Forgetting the 400 exception. The year 2000 is a leap year even though it is a multiple of 100. A solution that only says "multiples of 100 are not leap" fails on it.
- Using
/instead of%. In TypeScript2021 / 4is505.25, not a remainder, so the test=== 0would be false for almost every year. Use the remainder operator. - Using a single
=.year % 4 = 0is an error, since it tries to assign. Comparison needs===.
Testing
Good inputs and their expected results:
2020givestrue, and2021givesfalse.1968givestrue.1900givesfalse, and2000givestrue.1800and2100givefalse, and2024givestrue.