54
votes

I am trying to make a simple calculator to determine whether or not a certain year is a leap year.

By definition, a leap year is divisible by four, but not by one hundred, unless it is divisible by four hundred.

Here is my code:

def leapyr(n):
    if n%4==0 and n%100!=0:
        if n%400==0:
            print(n, "is a leap year.")
    elif n%4!=0:
        print(n, "is not a leap year.")
print(leapyr(1900))

When I try this inside the Python IDLE, the module returns None. I am pretty sure that I should get 1900 is a leap year.

11
1900 is not a leap year. But 2000 is. Also 2000 and 1900 are both divisible by 100, so you will never get 2000 as a positive hit. - StarPilot
Even alleged experts can get this wrong: see Excel incorrectly assumes that the year 1900 is a leap year. - PM 2Ring
@PM2Ring the link you provided gives a very good explanation why Excel gets it wrong. It was not that they didn't know better, they did it for compatibility reasons. It was deliberate. For more background see joelonsoftware.com/2006/06/16/my-first-billg-review - Mark Ransom
Note that your function doesn't return anything, so trying to print the result will always print None. - Mark Ransom
@MarkRansom Sure, Excel is merely retaining compatibility with Lotus 1-2-3. I didn't claim that the Excel authors were ignorant of the correct leap year rules, I just copied the title of that Microsoft article. - PM 2Ring

11 Answers

174
votes

Use calendar.isleap:

import calendar
print(calendar.isleap(1900))
54
votes

As a one-liner function:

def is_leap_year(year):
    """Determine whether a year is a leap year."""

    return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)

It's similar to the Mark's answer, but short circuits at the first test (note the parenthesis).

Alternatively, you can use the standard library's calendar.isleap, which has exactly the same implementation:

from calendar import isleap
print(isleap(1900))  # False
17
votes

You test three different things on n:

n % 4
n % 100
n % 400

For 1900:

1900 % 4 == 0
1900 % 100 == 0
1900 % 400 == 300

So 1900 doesn't enter the if clause because 1900 % 100 != 0 is False

But 1900 also doesn't enter the else clause because 1900 % 4 != 0 is also False

This means that execution reaches the end of your function and doesn't see a return statement, so it returns None.

This rewriting of your function should work, and should return False or True as appropriate for the year number you pass into it. (Note that, as in the other answer, you have to return something rather than print it.)

def leapyr(n):
    if n % 400 == 0:
        return True
    if n % 100 == 0:
        return False
    if n % 4 == 0:
        return True
    return False
print leapyr(1900)

(Algorithm from Wikipedia)

9
votes

The whole formula can be contained in a single expression:

def is_leap_year(year):
    return (year % 4 == 0 and year % 100 != 0) or year % 400 == 0

print n, " is a leap year" if is_leap_year(n) else " is not a leap year"
4
votes

Your function doesn't return anything, so that's why when you use it with the print statement you get None. So either just call your function like this:

leapyr(1900)

or modify your function to return a value (by using the return statement), which then would be printed by your print statement.

Note: This does not address any possible problems you have with your leap year computation, but ANSWERS YOUR SPECIFIC QUESTION as to why you are getting None as a result of your function call in conjunction with your print.

Explanation:

Some short examples regarding the above:

def add2(n1, n2):
    print 'the result is:', n1 + n2  # prints but uses no *return* statement

def add2_New(n1, n2):
    return n1 + n2    # returns the result to caller

Now when I call them:

print add2(10, 5)

this gives:

the result is: 15
None

The first line comes form the print statement inside of add2(). The None from the print statement when I call the function add2() which does not have a return statement, causing the None to be printed. Incidentally, if I had just called the add2() function simply with (note, no print statement):

add2()

I would have just gotten the output of the print statement the result is: 15 without the None (which looks like what you are trying to do).

Compare this with:

print add2_New(10, 5)

which gives:

15

In this case the result is computed in the function add2_New() and no print statement, and returned to the caller who then prints it in turn.

2
votes

A leap year is exactly divisible by 4 except for century years (years ending with 00). The century year is a leap year only if it is perfectly divisible by 400. For example,

if( (year % 4) == 0):
    if ( (year % 100 ) == 0):

        if ( (year % 400) == 0):

            print("{0} is a leap year".format(year))
        else:

            print("{0} is not a leap year".format(year))
    else:

        print("{0} is a leap year".format(year))

else:

    print("{0} is not a leap year".format(year))
0
votes

If you don't want to import calendar and apply .isleap method you can try this:

def isleapyear(year):
    if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0):
        return True
    return False
0
votes

In the Gregorian calendar, three conditions are used to identify leap years:

  • The year can be evenly divided by 4, is a leap year, unless:
    • The year can be evenly divided by 100, it is NOT a leap year, unless:
      • The year is also evenly divisible by 400. Then it is a leap year.

This means that in the Gregorian calendar, the years 2000 and 2400 are leap years, while 1800, 1900, 2100, 2200, 2300 and 2500 are NOT leap years. source

def is_leap(year):
    leap = False
    if year % 4 == 0:
        leap = True
        if year % 4 == 0 and year % 100 == 0:
            leap = False
            if year % 400 == 0:
                leap = True 
    return leap

year = int(input())
leap = is_leap(year)

if leap:
  print(f"{year} is a leap year")
else:
  print(f"{year} is not a leap year")
0
votes

The logic in the "one-liner" works fine. From personal experience, what has helped me is to assign the statements to variables (in their "True" form) and then use logical operators for the result:

A = year % 4 == 0
B = year % 100 == 0
C = year % 400 == 0

I used '==' in the B statement instead of "!=" and applied 'not' logical operator in the calculation:

leap = A and (not B or C)

This comes in handy with a larger set of conditions, and to simplify the boolean operation where applicable before writing a whole bunch of if statements.

0
votes

An alternative one liner:

((((y % 4) + (int((y - (y % 100)) / y) * ((y % 400) / 100))) - 1) < 0)

This was something that I put together for fun (?) that is also 1:1 compatible with C.

(y % 4) >>>It first checks if the year is a leap year via the typical mod-4 check.

(int((y - (y % 100)) / y) >>>It then accounts for those years divisible by 100. If the year is evenly divisible by 100, this will result in a value of 1, otherwise it will result in a value of 0.

((y % 400) / 100))) >>>Next, the year is divided by 400 (and subsequently 100, to return 1, 2, or 3 if it is not.

These two values

(int(y - (y % 100)) / y)

&

((y % 400) / 100)))

are then multiplied together. If the year is not divisible by 100, this will always equal 0, otherwise if it is divisible by 100, but not by 400, it will result in 1, 2, or 3. If it is divisible by both 100 and 400, it will result in 0.

This value is added to (y % 4), which will only be equal to 0 if the year is a leap year after accounting for the edge-cases.

Finally, 1 is subtracted from this remaining value, resulting in -1 if the year is a leap year, and either 0, 1, or 2 if it is not. This value is compared against 0 with the less-than operator. If the year is a leap year this will result in True (or 1, if used in C), otherwise it will return False (or 0, if used in C).

Please note: this code is horribly inefficient, incredibly unreadable, and a detriment to any code attempting to follow proper practices. This was an exercise of mine to see if I could do so, and nothing more.

Further, be aware that ZeroDivisionErrors are a consequence of the input year equaling 0, and must be accounted for.

For example, a VERY basic timeit comparison of 1000 executions shows that, when compared against an equivalent codeblock utilizing simple if-statements and the modulus operator, this one-liner is roughly 5 times slower than its if-block equivalent.

That being said, I do find it highly entertaining!

-1
votes

From 1700 to 1917, official calendar was the Julian calendar. Since then they we use the Gregorian calendar system. The transition from the Julian to Gregorian calendar system occurred in 1918, when the next day after January 31st was February 14th. This means that 32nd day in 1918, was the February 14th.

In both calendar systems, February is the only month with a variable amount of days, it has 29 days during a leap year, and 28 days during all other years. In the Julian calendar, leap years are divisible by 4 while in the Gregorian calendar, leap years are either of the following:

Divisible by 400.

Divisible by 4 and not divisible by 100.

So the program for leap year will be:

def leap_notleap(year):

    yr = ''
    if year <= 1917:
        if year % 4 == 0:
            yr = 'leap'
        else:
            yr = 'not leap'
    elif year >= 1919:
        if (year % 400 == 0) or (year % 4 == 0 and year % 100 != 0):
            yr = 'leap'
        else:
            yr = 'not leap'
    else:
        yr = 'none actually, since feb had only 14 days'

    return yr