Problem overview
A leap year is a year with an extra day. The rule has two parts. A year is a leap year when it is divisible by 4. The exception is a century year, one divisible by 100 such as 1800 or 1900, which is a leap year only when it is also divisible by 400. The task is a function isLeapYear(year: Int): Boolean inside class Solution that returns true or false.
Examples:
- 2020 is divisible by 4 and is not a century year, so the answer is
true. - 2021 is not divisible by 4, so the answer is
false. - 1968 is divisible by 4, so the answer is
true. - 1900 is divisible by 100 but not by 400, so the answer is
false.
Approach
The rule has a main case and two exceptions, and they overlap: 2000 is divisible by 4, by 100 and by 400. If you test for 4 first, you answer true for 1900 too, which is wrong. The way out is to test from the most specific condition to the most general one and return as soon as one applies:
- Divisible by 400? Leap year.
- Divisible by 100 (and so not by 400)? Not a leap year.
- Divisible by 4 (and so not by 100)? Leap year.
- Anything else? Not a leap year.
Each check can assume that the earlier ones failed. That is why the second test does not need to say "divisible by 100 but not 400".
Step by step
- Check 400 first. The condition
year % 400 == 0uses the remainder operator: a remainder of 0 means the division is exact. Such a year is also divisible by 100 and by 4, so catching it first means a year like 2000 is accepted before the century rule can reject it. - Reject other centuries. A year that reaches the check
year % 100 == 0was not divisible by 400, so it is a century year that is not a leap year, like 1900. Returnfalse. - Accept multiples of 4. Only non-century years get here, and for those, divisibility by 4 is the whole rule. 2020 and 1968 return
truehere. - Return
falseat the end. A year such as 2021 passes none of the three checks.
The solution in Kotlin
class Solution {
fun isLeapYear(year: Int): Boolean {
if (year % 400 == 0) {
return true
}
if (year % 100 == 0) {
return false
}
if (year % 4 == 0) {
return true
}
return false
}
}
Kotlin details worth knowing:
- The
%operator.year % 4is the remainder after dividingyearby 4. For positive years it is between 0 and 3. - Each
returnends the function. The code needs noelse. A later check runs only when the earlier ones did not return. - Integer division and remainder. Both truncate toward zero, so a negative year gives a remainder that is zero or negative, never positive. Testing for
== 0works for any sign. - A shorter form. The function body can be reduced to one expression:
return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0). It says the same thing, but the ordered checks are easier to follow and to extend.
Which line decides each year
| year | deciding line | result |
|---|---|---|
2000 | year % 400 == 0 | true |
1900 | year % 100 == 0 | false |
2020 | year % 4 == 0 | true |
2021 | the final return false | false |
Every year reaches exactly one of the four return statements, and the order of the checks decides which one. If you move a check, run these four years again to see what changed.
Edge cases and common mistakes
- Checking 4 first. This returns
truefor 1900. Whenever a rule has exceptions to exceptions, test the most specific one first. - Forgetting 400. Without it, 2000 is rejected as an ordinary century year, but it is a leap year.
- Using
=instead of==. In a condition, a single=is an assignment and Kotlin rejects it. Use==to compare. - Dividing instead of taking the remainder.
year / 4 == 0is true only for years from -3 to 3. Use%for divisibility tests. - Century years. 1800 and 1900 are not leap years, while 1600 and 2000 are.
Why the calendar needs this rule
A year lasts about 365.2425 days. Adding one day every fourth year is slightly too much, since it assumes 365.25 days. Dropping the day in century years corrects most of the excess, and keeping it in years divisible by 400 corrects the rest. Over 400 years the rule produces 97 leap years, which averages to exactly 365.2425 days per year. Knowing where the rule comes from also helps you remember the order of the three tests: the 400 exception sits on top of the 100 exception, and the 100 exception sits on top of the basic rule of 4.
Testing
isLeapYear(2020)returnstrue.isLeapYear(2021)returnsfalse.isLeapYear(1968)returnstrue.isLeapYear(1900)returnsfalse.isLeapYear(2000)returnstrue.