60

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.

4
  • 1
    For what it's worth, calendar calculations are surprisingly hard, especially if you have to get into leap seconds. If you're building a library, consider acquiring one instead. Good luck! :)
    – Greg D
    Apr 7, 2009 at 11:30
  • 7
    It is not an algorithm, it is astronomical facts, adapted to human granularity...
    – PhiLho
    Apr 7, 2009 at 11:42
  • 1
    https://www.mathsisfun.com/leap-years.html gives a clear explanation.
    – Nan Xiao
    Oct 28, 2017 at 4:10
  • 3
    I'm voting to close this question as off-topic because it has nothing to do with programming.
    – BJ Myers
    Mar 7, 2018 at 3:12

27 Answers 27

111

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

4
  • 5
    You may want to mention that the small differences really do matter since the old Julian calendar did not have these corrections and by the 16th century the calendar was over 10 days off for astronomical events like solstices and (more importantly) the date of easter. Apr 13, 2009 at 21:33
  • 5
    For interest, the original source of the present leap-year calculation system is a document entitled "Inter gravissimas" issued by Pope Gregory XIII in 1582.
    – Nick Moore
    Mar 2, 2010 at 19:43
  • Leap-seconds are for an unrelated issue: that a day is not exactly 24 hours long, and without them noon would after a long time be in the middle of the night. (Whereas leap-years correct the issue that the years isn't exactly 365 days, and without correction december would be in summer in the N. hemisphere.) Oct 4, 2019 at 11:44
  • Why didn't they redefine the second instead so the calculations are more exact ? Wouldn't that still allow the hours to be approximately the same as what they are currently without needing leap years.
    – Ced
    Jul 1, 2022 at 18:25
93

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.

5
  • I just tested this and it correctly determines leap and non-leap years 1900-2200 from the list of leap years at kalender-365.de/leap-years.php. Jun 16, 2014 at 19:13
  • Thanks so much for this algorithm. It works perfectly!
    – nuccio
    Mar 9, 2017 at 17:28
  • @Kevin I disagree. You seem to argue about details of the algorithm which in my opinion are not that important. At the very least it provides a lot of useful information about other calendars etc. Dec 10, 2018 at 8:59
  • @GeorgSchölly I understand your view... I meant a terrible programming resource. The algorithm posted at Wikipedia is unvetted and subject to change and has been outright incorrect at times. I believe the details of the algorithm are overwhelmingly important. Leap Year bugs have taken down numerous systems and cost millions and millions of dollars. (en.wikipedia.org/wiki/Leap_year_bug) Yeah, I just cited Wikipedia, didn't I? Ha! ha! In any case, Leap Year code is rarely tested and date/time bugs can be devastating to systems. I'm a fan of details. Cheers! Dec 10, 2018 at 10:57
  • And you have to consider more than the leap-year-algorithm when considering leap-year-bugs. E.g, the newsreader "trn" did in the 1990s have a leap-year bug - it got the year right, but added the day at the beginning of the year. Oct 4, 2019 at 11:51
17

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

12

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";
1
  • why checking modulus with 100 is necessary Nov 25, 2022 at 10:07
8

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

isLeapYear = (year % 100 === 0) ? (year % 400 === 0) : (year % 4 === 0);
3
  • 2
    This is less efficient than an if statement. You are forcing two comparisons in every case even when one will suffice. Calculations performed on a normally distributed data set will fail the % 4 test 75% of the time, so you should do it first and bail immediately if false.
    – Anthony
    Feb 13, 2019 at 15:19
  • var isleap = !(year%4) && (!!(year%100) || !(year%400)); or if you prefer: var isleap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); Mar 16, 2019 at 11:18
  • Very astute observation Anthony. Referenced in this answer, where performance testing showed in that case your approach was 2.1x faster: stackoverflow.com/questions/73590525/… Sep 5, 2022 at 0:28
8

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).

7

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

5

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;
3

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 (PDF).

3
1

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.

3
  • by Sandeep Agrawal, again A little modification in my above suggestion:- In place of 3000, take 3200 years as no leap year(because 3000 year itself is not a leap year as it is not divisible by 400) By taking 3200 year as no leap year the calculation will be :- 364.999696 +1/3200 = 364.999696 + .0003125 = 365.0000085 and now will hardly need any further adjustment.( after around 12000 years) Dec 28, 2011 at 16:27
  • after 12000 years( taking 12000 as no leap year) the calculation will be:- Dec 28, 2011 at 16:30
  • This is not how leap years are currently calculated, thus this is an incorrect answer to the original question.
    – pts
    Jun 22, 2023 at 13:41
1

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;
    }
1

PHP:

// is number of days in the year 366?  (php days of year is 0 based)
return ((int)date('z', strtotime('Dec 31')) === 365);
1
  • 1
    the "date('L')" is more efficient Mar 4, 2017 at 21:29
1

BIS will be 1 if the year is leap, otherwise 0 in this boolean logic:

BIS = A MOD 4=0 - (A MOD 100=0 AND A>1600) + (A MOD 400=0 AND A>1600)
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.

0

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).

0

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.
0

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.

1
  • The next time a year is divisible by 400 would be in the year 2400. And we just don't care what happens if and when we get there.
    – Bo Persson
    Apr 22, 2013 at 16:21
0

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 :)

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
0

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))
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.

0

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
0

This is the most efficient way, I think.

Python:

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

C# implementation

public bool LeapYear()
{
     int year = 2016;

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

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
0

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
$
-2

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).