leap day 2016

Happy Leap Day, friends! It’s that magical extra day that occurs on years divisible by 4… except if the year is divisible by 100. Oh, but if the year is divisible by 400, then we do celebrate Leap Day. So easy to remember, right? No? How about we make a function to determine if this year is a leap year.

function isLeapYear(year) {
  var leap = false;
  // is the year divisible by 4?
  if (year % 4 === 0) {
     leap = true;
     // but is it divisible by 100??
     if (year % 100 === 0) {
        leap = false;
        // but is it divisible by 400?!?!
        if (year % 400 === 0) {
           leap = true;
 return leap;

So there you go. 2016 is a leap year. 2017 is not (it’s not divisible by 4). 1900 is not (it’s divisible by 4 and 100, but not 400). 2000 is (it’s divisible by 4, 100, and 400). It’s incredible how much code is required to determine if Leap Day William is bringing you candy this year. Sadly, we as developers can fall victim to errors in calculating dates, but it does make for some good stories. Here are a few of our favorite Leap Day bugs:

  • Back in 2012, there was a bug in Gmail where all chats saved on February 29th showed up as December 31st, 1969.
  • There’s a known bug in Microsoft Excel where it thinks the date 2/29/1900 is real! This means the WEEKDAY function too is incorrect for dates before 2/28/1900.
  • Users of Microsoft Azure experienced an outage on February 29th, 2012 because the service was creating certificates that expired on February 29th, 2013, which is not a valid date.

Fortunately, there are libraries such as moment.js that can help avoid these types of bugs. For instance, you can create dates and determine if they are valid just by doing the following:

moment('2012-02-29').isValid(); // true
moment('2013-02-29').isValid(); // false

However, sometimes you have to be careful with using libraries such as moment.js to calculate dates! One example is determining the monthly billing period for a customer, since days of the month can vary. We define a user’s billing period relative to the date of the month they signed up for a paid account. For instance, if you sign up for an account on January 15th, then you will be billed on January 15th, February 15th, and so on and so forth. Easy, right? But what happens if you sign up on January 31st? February 31st is not a valid date (not even in leap years). In this case, we decided the logical choice is for you to be billed on the last day of the month. So if you sign up on January 31st, you should be billed on January 31st, February 29th, March 31st, April 30th. We use moment.js to handle date calculation, but we have to be careful with it. Check out the following ways to calculate what February 31st might be. (Note: months range from 0-11 in JavaScript, which is why set the month to 1 for February).

moment('2016/01/31').add(1, 'month').toString(); // "Mon Feb 29 2016 00:00:00 GMT-0500"
moment('2016/01/31').month(1).toString(); // "Mon Feb 29 2016 00:00:00 GMT-0500"
moment().set({year: 2016, month: 1, date: 31}).toString(); // "Wed Mar 02 2016 11:22:52 GMT-0500"

In the last line, moment.js assumes you mean “31 days from February 1st,” which turns out to be March 2nd! Not the result we were looking for.

So on this Leap Day (or better yet, before Leap Day), we encourage you to take a moment and test your code for date-related bugs. Does your code know about February 29, 2016? What does it think the date is a year from now? Is that the result you expected? Or maybe it’s time for all of us to switch to that metric calendar system! Whatever happens, just don’t tell your users it’s December 31st, 1969.