Start learning free

LESSON 86 OF 415 · CONDITIONAL STATEMENTS - CHALLENGES

Leap Year in Swift: Checking Divisibility 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:

year = 2020 ➞ true

year = 2021 ➞ false

year = 1968 ➞ true

year = 1900 ➞ false

Video Solution:

STEP 1 OF 4
    Ctrl + Enter to check

    SWIFT · CONDITIONAL STATEMENTS - CHALLENGES

    Leap Year in Swift, explained

    Problem overview

    The method isLeapYear(_ year: Int) -> Bool in the class Solution returns true when the year is a leap year. The rule has two layers. A year divisible by 4 is a leap year. But a year that is a multiple of 100, such as 1800 or 1900, is a leap year only when it is also divisible by 400.

    So 2020 and 1968 are leap years, 2021 is not, and 1900 is not, because it is a multiple of 100 but not of 400. The year 2000 is a leap year, because 400 divides it.

    Approach

    The remainder operator % tests divisibility: year % 4 == 0 means 4 divides the year evenly. The difficulty is that the rules override each other: 400 overrides 100, which overrides 4. The simplest way to handle that is to test the most specific rule first and return as soon as a rule decides the answer.

    1. Divisible by 400: always a leap year.
    2. Otherwise, divisible by 100: not a leap year.
    3. Otherwise, divisible by 4: a leap year.
    4. Otherwise: not a leap year.

    Because each step runs only when the earlier ones did not return, each check can rely on what the previous ones ruled out.

    Step by step

    1. Check 400 first. If year % 400 == 0, return true. Such a year is also divisible by 100 and 4, so testing it first stops the century rule from rejecting a year like 2000.
    2. Reject the other centuries. If year % 100 == 0, return false. Any century year that reaches this line was not divisible by 400, so it is not a leap year. 1900 stops here.
    3. Accept multiples of 4. If year % 4 == 0, return true. Only years that are not centuries get this far, and for them divisibility by 4 is the whole rule. 2020 and 1968 return here.
    4. Everything else is false. After the three checks, return false. 2021 reaches this line.

    The solution in Swift

    class Solution {
        func isLeapYear(_ year: Int) -> Bool {
            if year % 400 == 0 {
                return true
            }
            if year % 100 == 0 {
                return false
            }
            if year % 4 == 0 {
                return true
            }
            return false
        }
    }
    

    Details worth knowing:

    • No else needed. Every if body ends in a return, so execution never falls from one branch into the next. Each later check only runs for years the earlier checks did not settle.
    • The remainder operator. In Swift, % works on integers and takes the sign of the left operand, so -7 % 4 is -3. For a test against zero the sign doesn't matter, because a remainder is zero exactly when the division is exact.
    • No parentheses. Swift conditions don't need them, but the braces are mandatory.
    • A single expression also works. year % 400 == 0 || (year % 4 == 0 && year % 100 != 0) expresses the same rule. The chain of early returns is longer but reads in the same order as the rule's description.

    Tracing three years

    Follow the checks for 2000, 1900 and 2021 to see why the order works.

    • 2000. The remainder after dividing by 400 is 0, so the first check returns true. The century rule is never consulted.
    • 1900. 1900 divided by 400 leaves a remainder of 300, so the first check is skipped. The remainder by 100 is 0, so the second check returns false. The check for 4 is never reached, which is what keeps 1900 from being accepted by the "divisible by 4" rule.
    • 2021. It is not divisible by 400, 100 or 4, so all three checks are skipped and the last line returns false.

    The method does at most three remainder operations, so its running time doesn't depend on the size of the year.

    Trying it

    let s = Solution()
    for y in [2020, 2021, 1968, 1900, 2000] {
        print(y, s.isLeapYear(y))
    }
    

    The output is:

    2020 true
    2021 false
    1968 true
    1900 false
    2000 true
    

    Edge cases and common mistakes

    • Checking 4 first. If the first test is year % 4 == 0 returning true, then 1900 is wrongly accepted, because 4 divides it.
    • Forgetting the 400 exception. With only the 100 and 4 checks, 2000 would be rejected, but it is a leap year.
    • Writing = instead of ==. A single equals sign assigns, and Swift rejects an assignment used as a condition.
    • Century years. 1800, 1900 and 2100 are not leap years. 1600, 2000 and 2400 are.
    • Unusual inputs. The method applies the rule mechanically to any Int. Year 0 is divisible by 400, so it returns true. The rule as stated is about modern calendar years, so tests stick to ordinary positive years such as 1900, 2000 and 2021.
    • Dropping the final return. Without return false at the end, Swift reports a missing return for the case where none of the checks match.
    Keep going with SwiftAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free