Start learning free

LESSON 90 OF 348 · CONDITIONAL STATEMENTS - CHALLENGES

Leap Year in TypeScript: The Divisibility Rules in Order

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:

leapYear(2020) ➞ true

leapYear(2021) ➞ false

leapYear(1968) ➞ true

leapYear(1900) ➞ false

Video Solution:

STEP 1 OF 4
    Ctrl + Enter to check

    TYPESCRIPT · CONDITIONAL STATEMENTS - CHALLENGES

    Leap Year in TypeScript, explained

    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:

    1. Divisible by 400: always a leap year. This also catches 2000, which is divisible by 100 as well.
    2. Divisible by 100 (and so not by 400): never a leap year.
    3. Divisible by 4 (and so not by 100): a leap year.
    4. 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

    1. Check 400. 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. Compare with ===, which tests equality without converting types.
    2. Check 100. If year % 100 === 0, return false. A year that reaches this line is a multiple of 100 but wasn't a multiple of 400, like 1900.
    3. Check 4. If year % 4 === 0, return true. Century years are already handled, so for the years that arrive here, divisibility by 4 is the whole rule. 2020 and 1968 return here.
    4. Return false after the three checks. The year isn't divisible by 4, as with 2021. No else is 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 any number. There is no separate integer type. The remainder keeps the sign of the dividend: -7 % 4 is -3. Negative years are rare, but -4 % 4 evaluates to -0, and -0 === 0 is true, so the checks still behave.
    • Non-integer years are not rejected. 2021.5 % 4 is 1.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: number allows 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 % 400 is 0, so the first check returns true. The later checks never run.
    • 1900. 1900 % 400 is 300, so the first check fails. 1900 % 100 is 0, so the second check returns false.
    • 2020. 2020 % 400 is 20 and 2020 % 100 is 20, so both checks fail. 2020 % 4 is 0, so the third check returns true.
    • 2021. All three remainders are non-zero (2021 % 4 is 1), so the method reaches the final return 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 true for 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 TypeScript 2021 / 4 is 505.25, not a remainder, so the test === 0 would be false for almost every year. Use the remainder operator.
    • Using a single =. year % 4 = 0 is an error, since it tries to assign. Comparison needs ===.

    Testing

    Good inputs and their expected results:

    • 2020 gives true, and 2021 gives false.
    • 1968 gives true.
    • 1900 gives false, and 2000 gives true.
    • 1800 and 2100 give false, and 2024 gives true.
    Keep going with TypeScriptAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free