27
votes

How can I get the code below to work when I have a month of february? Currently it is getting to the day and then stopping before getting to the if to determine whether it is a leap year.

 if (month == 2) {
    if (day == 29) {
        if (year % 4 != 0 || year % 100 == 0 && year % 400 != 0) {
            field.focus();
             field.value = month +'/' +  '';
        }
    }
    else if (day > 28) {
        field.focus();
             field.value = month +'/' +  '';
    }
}
13
"Stopping" how? Is there an error?Pekka
it never evaluates the year to see if it is a leap year it goes straight to the field.focus and field.value whether it is a leap year or notJuan Almonte
Your conditions look a little odd - as they're currently written now, you only check day for values of 29 or greater (based on the day == 29 and day > 28 if clauses). I'm assuming that you meant to write day <= 28, but if that's the case, you could drop the second else if clause and use an else clause directly. It might also be safer to add an additional set of parenthesis to your leap year clause: if (year % 4 != 0 || (year % 100 == 0 && year % 400 != 0))JW8
You might need to show some of the surrounding code for how those variables are being set. If you're using a Date object at all remember that it uses zero-based months.nnnnnn

13 Answers

118
votes

It's safer to use Date objects for datetime stuff, e.g.

isLeap = new Date(year, 1, 29).getMonth() == 1

Since people keep asking about how exactly this works, it has to do with how JS calculates the date value from year-month-day (details here). Basically, it first calculates the first of the month and then adds N -1 days to it. So when we're asking for the 29th Feb on a non-leap year, the result will be the 1st Feb + 28 days = 1st March:

> new Date(2015, 1, 29)
< Sun Mar 01 2015 00:00:00 GMT+0100 (CET)

On a leap year, the 1st + 28 = 29th Feb:

> new Date(2016, 1, 29)
< Mon Feb 29 2016 00:00:00 GMT+0100 (CET)

In the code above, I set the date to 29th Feb and look if a roll-over took place. If not (the month is still 1, i.e. February), this is a leap year, otherwise a non-leap one.

18
votes

Compared to using new Date() this is is around 100 times faster!

Update:

This latest version uses a bit test of the bottom 3 bits (is it a multiple of 4), as well as a check for the year being a multiple of 16 (bottom 4 bits in binary is 15) and being a multiple of 25.

ily = function(y) {return !(y & 3 || !(y % 25) && y & 15);};

http://jsperf.com/ily/15

It is slightly faster again than my previous version (below):

ily = function(yr) {return !((yr % 4) || (!(yr % 100) && (yr % 400)));};

http://jsperf.com/ily/7

It is also 5% faster, compared to the already fast conditional operator version by broc.seib

Speed Test results: http://jsperf.com/ily/6

Expected logic test results:

alert(ily(1900)); // false
alert(ily(2000)); // true
alert(ily(2001)); // false
alert(ily(2002)); // false
alert(ily(2003)); // false
alert(ily(2004)); // true
alert(ily(2100)); // false
alert(ily(2400)); // true
6
votes
isLeap = !(new Date(year, 1, 29).getMonth()-1)

...subtraction by one should work even faster than compare on most CPU architectures.

6
votes

Correct and Fast:

ily = function(yr) { return (yr%400)?((yr%100)?((yr%4)?false:true):false):true; }

If you are in a loop or counting the nanoseconds, this is two magnitudes faster than running your year through a new Date() object. Compare the performance here: http://jsperf.com/ily

2
votes

Better historical computation of leap years.

The code below takes into account that leap years were introduced in 45BC with the Julian calendar, and that the majority of the Western world adopted the Gregorian calendar in 1582CE, and that 0CE = 1BC.

isLeap = function(yr) {
  if (yr > 1582) return !((yr % 4) || (!(yr % 100) && (yr % 400)));
  if (yr >= 0) return !(yr % 4);
  if (yr >= -45) return !((yr + 1) % 4);
  return false;
};

Britain and its colonies adopted the Gregorian calendar in 1752, so if you are more Anglo centric this version is better (We'll assume Britain adopted the Julian calendar with Roman conquest starting in 43CE).

isLeap = function(yr) {
  if (yr > 1752) return !((yr % 4) || (!(yr % 100) && (yr % 400)));
  if (yr >= 43) return !(yr % 4);
  return false;
};
1
votes

I use this because I hate having to keep referring to January as 0 and February as 1. To me and PHP and readable dates, February=2. I know it doesn't really matter as the number never changes but it just keeps my brain thinking the same across different code.

var year = 2012;
var isLeap = new Date(year,2,1,-1).getDate()==29;
1
votes

You can easily make this to work calling .isLeapYear() from momentjs:

var notLeapYear = moment('2018-02-29')
console.log(notLeapYear.isLeapYear()); // false

var leapYear = moment('2020-02-29')
console.log(leapYear.isLeapYear()); // true
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.21.0/moment.min.js"></script>
1
votes

all in one line 😉

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

console.log(isLeapYear(2016)); // true
console.log(isLeapYear(2000)); // true
console.log(isLeapYear(1700)); // false
console.log(isLeapYear(1800)); // false
console.log(isLeapYear(2020)); // true
1
votes
function isLeap(year) {   
    if ( (year % 4 === 0 && year % 100 !== 0) || (year % 4 === 0 && year % 100 === 0 && year % 400 === 0) ) {
        return 'Leap year.'
    } else {
        return 'Not leap year.';
    }
}
0
votes

Pseudo code

if year is not divisible by 4 then not leap year
else if year is not divisible by 100 then leap year
else if year is divisible by 400 then leap year
else not leap year

JavaScript

function isLeapYear (year) {
    return year % 4 == 0 && ( year % 100 != 0 || year % 400 == 0 )
}

Using the above code insures you do only one check per year if the year is not divisible by 4 Just by adding the brackets you save 2 checks per year that is not divisible by 4

0
votes

Another alternative is to see if that year has the date of February 29th. If it does have this date, then you know it is a leap year.

ES6

// Months are zero-based integers between 0 and 11, where Febuary = 1
const isLeapYear = year => new Date(year, 1, 29).getDate() === 29;

Tests

> isLeapYear(2016);
< true
> isLeapYear(2019);
< false
0
votes
function leapYear(year){
    if((year%4==0) && (year%100 !==0) || (year%400==0)){
        return true;
    }
    else{
        return false;
    }
}
var result = leapYear(1700);
console.log(result);
0
votes

JavaScript is expected to be getting a new Date/Time API which exposes a new global object - Temporal. This global object provides JS devs with a nicer way to deal with dates/times. It is currently a stage 3 proposal and should hopefully be available for use shortly.

The temporal api exposes a nice property for checking for leap years - inLeapYear. This returns true if a particular date is a leap year, otherwise false. Below we're using with() to convert the date returned by plainDateISO to one with our particular year:

const isLeap = year => Temporal.now.plainDateISO().with({year}).inLeapYear;
console.log(isLeap(2020)); // true
console.log(isLeap(2000)); // true
console.log(isLeap(1944)); // true

console.log(isLeap(2021)); // false
console.log(isLeap(1999)); // false

If you just want to check if your current system date time is a leap year, you can omit the .with():

// true if this year is a leap year, false if it's not a leap year
const isLeap = Temporal.now.plainDateISO().inLeapYear;