Algorithm for Determining the Number of Days in a Month
2012-04-12
In summary:
The number of days in a month can be calculated as:
var daysInMonth = (month === 2) ? (28 + isLeapYear) : 31 - (month - 1) % 7 % 2;
Where the month
is represented as a number between 1 and 12 and
isLeapYear
is 1 if the year is a leap year.
Also, make sure you read my article on how to calculate whether a year is a leap year.
Introduction
Thirty days hath September,
April, June, and November;
All the rest have thirty-one…
http://en.wikipedia.org/wiki/Thirty_days_hath_September
Some people may have learned this rhyme in school as a way of remembering how many days there are in each month. Of course, you cannot teach rhymes to computers as easily as you can to children, so we face the problem of how to write a program to determine the number of days in a month.
The following table shows the month number (1-12), name of the month and number of days in that month:
Month | Month Name | Number of Days |
---|---|---|
1 | January | 31 |
2 | February | 28 (29 for leap years) |
3 | March | 31 |
4 | April | 30 |
5 | May | 31 |
6 | June | 30 |
7 | July | 31 |
8 | August | 31 |
9 | September | 30 |
10 | October | 31 |
11 | November | 30 |
12 | December | 31 |
Solution #1
A first attempt at an algorithm, writing the code in JavaScript, might look something like this:
var daysInMonth; if (month === 2) { // February if (isLeapYear) { daysInMonth = 29; } else { daysInMonth = 28; } } else if (month === 4 || month === 6 || month === 9 || month === 11) { daysInMonth = 30; } else { daysInMonth = 31; }
This works just fine, but we can improve upon this.
Solution #2
When trying to write cleaner code, you might be tempted to simply store the number of days in an array like the following:
var daysArray = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; daysInMonth = daysArray[month - 1]; if (month === 2 && isLeapYear) { daysInMonth = daysInMonth + 1; }
This is pretty straightforward and can be very fast for calculating many
results, provided the array of days (daysArray
) is declared only
once and reused:
var i; var daysArray = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; for (i = 0; i < months.length; i = i + 1) { daysInMonth[i] = daysArray[month[i] - 1]; if (month[i] === 2 && isLeapYear[i]) { daysInMonth[i] = daysInMonth[i] + 1; } }
Solution #3
Further analysis reveals and interesting pattern that we can use to our advantage. First, since February is a special exception, we can temporarily pretend that there are 31 days in February. Now, when we write the number of days in each month we get the following:
month | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
Number of days | 31 | 30 | 31 | 30 | 31 | 30 | 31 | 31 | 30 | 31 | 30 | 31 |
Rewriting the pattern in terms of letters A (31 days) and B (30 days) makes the pattern more obvious:
month | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
Pattern | A | B | A | B | A | B | A | A | B | A | B | A |
The numbers alternate between 31 days and 30 days, then the pattern restarts
in August (month 8). A seven digit repeating sequence can be created using the
modulus (mod, or %) operator (i % 7
). Performing a mod-2 operation
on the seven digit repeating sequence then gives us the final pattern that we
need ((i % 7) % 2
):
i | i % 7 | (i % 7) % 2 |
---|---|---|
0 | 0 | 0 |
1 | 1 | 1 |
2 | 2 | 0 |
3 | 3 | 1 |
4 | 4 | 0 |
5 | 5 | 1 |
6 | 6 | 0 |
7 | 0 | 0 |
8 | 1 | 1 |
9 | 2 | 0 |
10 | 3 | 1 |
11 | 4 | 0 |
A few small adjustments are needed:
- Translate the pattern from
i = (0, 1, 2, …, 11)
tomonth = (1, 2, 3, …, 12)
- Return a result of 31 and 30 instead of 0 and 1
- Handle the special case of
month == 2
(February)
And we get:
var daysInMonth; if (month === 2) { daysInMonth = 28 + isLeapYear; } else { daysInMonth = 31 - (month - 1) % 7 % 2; }
This solution can also be written in a different way by using the ternary conditional operator (i.e. 'condition' ? 'then' : 'else'):
var daysInMonth = 31 - ((month === 2) ? (3 - isLeapYear) : ((month - 1) % 7 % 2));
And we are finished! An elegant, one-line solution to the problem. Of course it is also necessary to determine whether the current year is a leap year. The algorithm for that is presented in my leap year article.
Other Programming Languages
C, C++ and Java
Very little modification is needed to express the leap year calculation in C, C++ or Java:
int daysInMonth = 31 - ((month == 2) ? (3 - isLeapYear) : ((month - 1) % 7 % 2));
Perl and PHP
And translating the leap year calculation into PHP or Perl:
$daysInMonth = 31 - (($month == 2) ? (3 - $isLeapYear) : (($month - 1) % 7 % 2));