[algorithm] Leap year calculation

In order to find leap years, why must the year be indivisible by 100 and divisible by 400?

I understand why it must be divisible by 4. Please explain the algorithm.

This question is related to algorithm calendar leap-year

The answer is


In Java Below code calculates leap year count between two given year. Determine starting and ending point of the loop.

Then if parameter modulo 4 is equal 0 and parameter modulo 100 not equal 0 or parameter modulo 400 equal zero then it is leap year and increase counter.

static int calculateLeapYearCount(int year, int startingYear) {
        int min = Math.min(year, startingYear);
        int max = Math.max(year, startingYear);
        int counter = 0;
        for (int i = min; i < max; i++) {
            if ((i % 4 == 0 && i % 100 != 0) || i % 400 == 0) {
                counter = counter + 1;
            }
        }
        return counter;
    }

I'm sure Wikipedia can explain it better than I can, but it is basically to do with the fact that if you added an extra day every four years we'd get ahead of the sun as its time to orbit the sun is less than 365.25 days so we compensate for this by not adding leap days on years that are not divisible by 400 eg 1900.

Hope that helps


In general terms the algorithm for calculating a leap year is as follows...

A year will be a leap year if it is divisible by 4 but not by 100. If a year is divisible by 4 and by 100, it is not a leap year unless it is also divisible by 400.

Thus years such as 1996, 1992, 1988 and so on are leap years because they are divisible by 4 but not by 100. For century years, the 400 rule is important. Thus, century years 1900, 1800 and 1700 while all still divisible by 4 are also exactly divisible by 100. As they are not further divisible by 400, they are not leap years


Here comes a rather obsqure idea. When every year dividable with 100 gets 365 days, what shall be done at this time? In the far future, when even years dividable with 400 only can get 365 days.

Then there is a possibility or reason to make corrections in years dividable with 80. Normal years will have 365 day and those dividable with 400 can get 366 days. Or is this a loose-loose situation.


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:

Python:

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

The length of a year is (more or less) 365.242196 days. So we have to subtract, more or less, a quarter of a day to make it fit :

365.242196 - 0.25 = 364.992196 (by adding 1 day in 4 years) : but oops, now it's too small!! lets add a hundreth of a day (by not adding that day once in a hundred year :-))

364.992196 + 0,01 = 365.002196 (oops, a bit too big, let's add that day anyway one time in about 400 years)

365.002196 - 1/400 = 364.999696

Almost there now, just play with leapseconds now and then, and you're set.

(Note : the reason no more corrections are applied after this step is because a year also CHANGES IN LENGTH!!, that's why leapseconds are the most flexible solution, see for examlple here)

That's why i guess


If you're interested in the reasons for these rules, it's because the time it takes the earth to make exactly one orbit around the sun is a long imprecise decimal value. It's not exactly 365.25. It's slightly less than 365.25, so every 100 years, one leap day must be eliminated (365.25 - 0.01 = 365.24). But that's not exactly correct either. The value is slightly larger than 365.24. So only 3 out of 4 times will the 100 year rule apply (or in other words, add back in 1 day every 400 years; 365.25 - 0.01 + 0.0025 = 365.2425).


There are on average, roughly 365.2425 days in a year at the moment (the Earth is slowing down but let's ignore that for now).

The reason we have leap years every 4 years is because that gets us to 365.25 on average [(365+365+365+366) / 4 = 365.25, 1461 days in 4 years].

The reason we don't have leap years on the 100-multiples is to get us to 365.24 `[(1461 x 25 - 1) / 100 = 365.24, 36,524 days in 100 years.

Then the reason we once again have a leap year on 400-multiples is to get us to 365.2425 [(36,524 x 4 + 1) / 400 = 365.2425, 146,097 days in 400 years].

I believe there may be another rule at 3600-multiples but I've never coded for it (Y2K was one thing but planning for one and a half thousand years into the future is not necessary in my opinion - keep in mind I've been wrong before).

So, the rules are, in decreasing priority:

  • multiple of 400 is a leap year.
  • multiple of 100 is not a leap year.
  • multiple of 4 is a leap year.
  • anything else is not a leap year.

This is the most efficient way, I think.

Python:

def leap(n):
    if n % 100 == 0:
        n = n / 100
    return n % 4 == 0

Leap years are arbitrary, and the system used to describe them is a man made construct. There is no why.

What I mean is there could have been a leap year every 28 years and we would have an extra week in those leap years ... but the powers that be decided to make it a day every 4 years to catch up.

It also has to do with the earth taking a pesky 365.25 days to go round the sun etc. Of course it isn't really 365.25 is it slightly less (365.242222...), so to correct for this discrepancy they decided drop the leap years that are divisible by 100.


I found this problem in the book "Illustrated Guide to Python 3". It was in a very early chapter that only discussed the math operations, no loops, no comparisons, no conditionals. How can you tell if a given year is a leap year?

Below is what I came up with:

y = y % 400
a = y % 4
b = y % 100
c = y // 100
ly = (0**a) * ((1-(0**b)) + 0**c)   # ly is not zero for leap years, else 0

Simply Because year 2000 is a leap year and it is divisible by 100 and dividable by 4. SO to guarantee it is correct leap we need to ensure it is divisible by 400. 2000 % 4 = 0 2000 % 100 = 0 According to algorithm it's not leap, but it is dividable by 400 2000 % 400 = 0 so it is leap.


this is enough to check if a year is a leap year.

if( (year%400==0 || year%100!=0) &&(year%4==0))
    cout<<"It is a leap year";
else
    cout<<"It is not a leap year";

Python 3.5

def is_leap_baby(year):
    if ((year % 4 is 0) and (year % 100 is not 0)) or (year % 400 is 0):
        return "{0}, {1} is a leap year".format(True, year)
    return "{0} is not a leap year".format(year)

print(is_leap_baby(2014))
print(is_leap_baby(2012))

PHP:

// is number of days in the year 366?  (php days of year is 0 based)
return ((int)date('z', strtotime('Dec 31')) === 365);

Here is a simple implementation of the wikipedia algorithm, using the javascript ternary operator:

isLeapYear = (year % 100 === 0) ? (year % 400 === 0) : (year % 4 === 0);

In the Gregorian calendar 3 criteria must be taken into account to identify leap years:

  1. The year is evenly divisible by 4;
  2. If the year can be evenly divided by 100, it is NOT a leap year, unless;
  3. The year is also evenly divisible by 400. Then it is a leap year. Why the year divided by 100 is not leap year

Return true if the input year is a leap year

Basic modern day code:

  If year mod 4 = 0, then leap year
  if year mod 100 then normal year
  if year mod 400 then leap year
  else normal year

Todays rule started 1582 AD Julian calendar rule with every 4th year started 46BC but is not coherent before 10 AD as declared by Cesar. They did however add some leap years every 3rd year now and then in the years before: Leap years were therefore 45 BC, 42 BC, 39 BC, 36 BC, 33 BC, 30 BC, 27 BC, 24 BC, 21 BC, 18 BC, 15 BC, 12 BC, 9 BC, 8 AD, 12 AD Before year 45BC leap year was not added.

The year 0 do not exist as it is ...2BC 1BC 1AD 2AD... for some calculation this can be an issue.

function isLeapYear(year: Integer): Boolean;
begin
  result := false;
  if year > 1582 then // Todays calendar rule was started in year 1582 
    result := ((year mod 4 = 0) and (not(year mod 100 = 0))) or (year mod 400 = 0)
  else if year > 10 then // Between year 10 and year 1582 every 4th year was a leap year 
    result := year mod 4 = 0
  else //Between year -45 and year 10 only certain years was leap year, every 3rd year but the entire time
    case year of
      -45, -42, -39, -36, -33, -30, -27, -24, -21, -18, -15, -12, -9:
        result := true;
    end;
end;

In shell you can use cal -j YYYY which prints the julian day of the year, If the last julian day is 366, then it is a leap year.

$ function check_leap_year
 {
  year=$1
   if [ `cal -j $year | awk 'NF>0' | awk 'END { print $NF } '` -eq 366 ];
   then
      echo "$year -> Leap Year";
   else
      echo "$year -> Normal Year" ;
   fi
 }
$ check_leap_year 1900
1900 -> Normal Year
$ check_leap_year 2000
2000 -> Leap Year
$ check_leap_year 2001
2001 -> Normal Year
$ check_leap_year 2020
2020 -> Leap Year
$

Using awk, you can do

$ awk -v year=1900 ' BEGIN { jul=strftime("%j",mktime(year " 12 31 0 0 0 ")); print jul } '
365
$ awk -v year=2000 ' BEGIN { jul=strftime("%j",mktime(year " 12 31 0 0 0 ")); print jul } '
366
$ awk -v year=2001 ' BEGIN { jul=strftime("%j",mktime(year " 12 31 0 0 0 ")); print jul } '
365
$ awk -v year=2020 ' BEGIN { jul=strftime("%j",mktime(year " 12 31 0 0 0 ")); print jul } '
366
$

just wrote this in Coffee-Script:

is_leap_year = ( year ) ->
  assert isa_integer year
  return true   if year % 400 == 0
  return false  if year % 100 == 0
  return true   if year %   4 == 0
  return false

# parseInt? that's not even a word. 
# Let's rewrite that using real language:
integer = parseInt 

isa_number = ( x ) ->
  return Object.prototype.toString.call( x ) == '[object Number]' and not isNaN( x )

isa_integer = ( x ) ->
  return ( isa_number x ) and ( x == integer( x ) )

of course, the validity checking done here goes a little further than what was asked for, but i find it a necessary thing to do in good programming.

note that the return values of this function indicate leap years in the so-called proleptic gregorian calendar, so for the year 1400 it indicates false, whereas in fact that year was a leap year, according to the then-used julian calendar. i will still leave it as such in the datetime library i'm writing because writing correct code to deal with dates quickly gets surprisingly involved, so i will only ever support the gregorian calendar (or get paid for another one).


C# implementation

public bool LeapYear()
{
     int year = 2016;

     return year % 4 == 0 && year % 100 != 0 || year % 400 == 0 ;
}

Will it not be much better if we make one step further. Assuming every 3200 year as no leap year, the length of the year will come

364.999696 + 1/3200 = 364.999696 + .0003125 = 365.0000085

and after this the adjustment will be required after around 120000 years.


a) The year is 365.242199 days.

b) If every year was 365 days, in 100 years we would lose 24.2199 days. That's why we add 24 days per century (every 4 years EXCEPT when divisible by 100)

c) But still we lose 0.21299 days/century. So in 4 centuries we lose 0.8796 days. That's why we add 1 day per 4 centuries (every fourth century we DO count a leap year).

d) But that means we lose -0.1204 days (we go forward) per quadricentennial (4 centuries). So in 8 quadricentennial (3200 years) we DO NOT count a leap year.

e) But that means we lose 0.0368 days per 3200 years. So in 24x3200 years (=76800years) we lose 0.8832 days. That's why we DO count a leap year.

and so on... (by then we will have destroyed the planet, so it doesn't matter)

What I cannot understand though, is why we don't count a leap year every 500 years instead of 400. In that way we would converge more rapidly to the correct time (we would lose 2.3 hours/500 years).


There's an algorithm on wikipedia to determine leap years:

function isLeapYear (year):
    if ((year modulo 4 is 0) and (year modulo 100 is not 0))
    or (year modulo 400 is 0)
        then true
    else false

There's a lot of information about this topic on the wikipedia page about leap years, inclusive information about different calendars.


You could just check if the Year number is divisible by both 4 and 400. You dont really need to check if it is indivisible by 100. The reason 400 comes into question is because according to the Gregorian Calendar, our "day length" is slightly off, and thus to compensate that, we have 303 regular years (365 days each) and 97 leap years (366 days each). The difference of those 3 extra years that are not leap years is to stay in cycle with the Gregorian calendar, which repeats every 400 years. Look up Christian Zeller's congruence equation. It will help understanding the real reason. Hope this helps :)


You really should try to google first.

Wikipedia has a explanation of leap years. The algorithm your describing is for the Proleptic Gregorian calendar.

More about the math around it can be found in the article Calendar Algorithms.