Start learning free

LESSON 86 OF 414 · CONDITIONAL STATEMENTS - CHALLENGES

Leap Year in Scala: Ordered Checks with %

Practice Read the full explanation

A leap year happens every four years, so it's a year that is perfectly divisible by four.

However, if the year is a multiple of 100 (1800, 1900, etc), the year must be divisible by 400 to be leap.

Write a function that determines if the year is a leap year or not.

Examples:

isLeapYear(2020) ➞ true

isLeapYear(2021) ➞ false

isLeapYear(1968) ➞ true

isLeapYear(1900) ➞ false

Video Solution:

STEP 1 OF 4
    Ctrl + Enter to check

    SCALA · CONDITIONAL STATEMENTS - CHALLENGES

    Leap Year in Scala, explained

    Problem overview

    The method isLeapYear receives a year as an Int and returns true if it is a leap year and false otherwise. The rules are:

    • a year divisible by 4 is a leap year;
    • except that a year divisible by 100 (1800, 1900, ...) is a leap year only if it is also divisible by 400.

    The examples: 2020 is divisible by 4 and not by 100, so it is a leap year. 2021 is not divisible by 4, so it is not. 1968 is divisible by 4 and not by 100, so it is a leap year. 1900 is divisible by 100 but not by 400, so it is not. And 2000, divisible by 400, is a leap year.

    Approach: check the most specific rule first

    The three rules pile exceptions on top of each other: 4 is the general rule, 100 is an exception to it, and 400 is an exception to the exception. Testing the rules in the wrong order gives wrong answers. If you test divisibility by 4 first, 1900 is accepted at once, although it is not a leap year.

    The safe order is from the most specific condition to the most general one:

    1. divisible by 400: leap;
    2. otherwise, divisible by 100: not leap;
    3. otherwise, divisible by 4: leap;
    4. otherwise: not leap.

    Because each check returns when it matches, a year only reaches a later check if all the earlier ones failed. That is what lets each check be simple: when the code asks about 100, it already knows the year is not divisible by 400, and when it asks about 4, it knows the year is not a multiple of 100.

    Step by step

    1. Check 400 first. If year % 400 == 0, return true. The remainder operator % gives what is left after division, and a remainder of 0 means the division is exact. Such a year is also divisible by 100 and by 4, so testing it first lets 2000 count as a leap year before the century rule can reject it.
    2. Check 100 next. If year % 100 == 0, return false. A year that reaches this line is a multiple of 100 but not of 400, like 1900.
    3. Check 4. If year % 4 == 0, return true. Only years that are not multiples of 100 get here, and for them divisibility by 4 is the whole rule. 2020 and 1968 return at this line.
    4. Fall through to false. A year that passed none of the checks, such as 2021, is not divisible by 4. No else is needed, because every earlier branch has already returned.

    The solution in Scala

    class Solution {
        def isLeapYear(year: Int): Boolean = {
            if (year % 400 == 0) {
                return true
            }
            if (year % 100 == 0) {
                return false
            }
            if (year % 4 == 0) {
                return true
            }
            return false
        }
    }
    

    The Scala details worth knowing:

    • The remainder operator. % works on Int like in most languages. For negative numbers the result takes the sign of the left operand, so -4 % 400 is -4, but a remainder of exactly 0 is still 0.
    • Early return. Each return ends the method, and a method that uses return needs its result type, Boolean here.
    • A shorter form. The four checks collapse into one expression: year % 4 == 0 && (year % 100 != 0 || year % 400 == 0). It reads "divisible by 4, and not a century unless divisible by 400". It is equivalent, and you can write it without any return.

    Complexity

    Time: O(1). The method does at most three remainder operations and comparisons, whatever the year is. Space: O(1). It stores no extra data.

    The three-level rule exists because the average calendar year should be close to the real solar year, about 365.2425 days long. Adding a day every fourth year gives 365.25 on average, which is a little too much. Skipping three leap days every 400 years, the century years not divisible by 400, brings the average to exactly 365.2425, since 97 of every 400 years are leap years.

    Edge cases and common mistakes

    • Century years. 1900 and 2100 are not leap years. 2000 and 2400 are.
    • Testing 4 first. Writing the checks in the order 4, 100, 400 returns true for 1900 straight away, which is wrong.
    • Testing 100 before 400. This returns false for 2000, which is a leap year.
    • Using = for comparison. The remainder test needs ==. A single = is assignment, not comparison, and the compiler rejects it as a condition.
    • Forgetting the last return. The method must produce a Boolean on every path, including years that fail all three checks.

    Testing

    • isLeapYear(2020) gives true.
    • isLeapYear(2021) gives false.
    • isLeapYear(1968) gives true.
    • isLeapYear(1900) gives false.
    • isLeapYear(2000) gives true.
    Keep going with ScalaAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free