Recently, after being awake for longer than I should have, I started
thinking about methods of remembering the number of days in each month
of the year. There is a rhyme for it, and a way to count on your
knuckles, but these didn’t satisfy me. I wondered if there was a
mathematical formula for the problem, and upon not immediately finding
one, I challenged myself to create one.
In other words, the challenge was this: find a function f, such that
f(x) is equal to the number of days in month x,
represented by the integers 1 through 12. Or, as a table of values:
x |
1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 |
f(x) |
31 | 28 | 31 | 30 | 31 | 30 |
31 | 31 | 30 | 31 | 30 | 31 |
If you want to give this challenge a go before reading my solution, now
is your chance. If you’d rather see my complete solution right away,
scroll to the bottom of the page. What follows is my process for solving
the problem.
Firstly, here’s a quick refresher on two operations I found vital to
solving the problem: floor division and modulo.
Floor division is the operation performed by many programming languages when
dividing two integer numbers, that is, the result of the division is truncated
to the integer part. I will represent floor division as
⌊a⁄b⌋, for
example:
⌊5⁄3⌋ = 1
Modulo is an operation that results in the remainder of a division. It
is represented in many programming languages with the %
operator. I
will represent it as “a mod b”,
for example:
3 mod 2 = 1
Note that modulo has the same precedence as division.
The Basics
With those tools in mind, let’s get a basic pattern going. Months usually
alternate between lengths of 30 and 31 days. We can use x mod 2 to
get an alternating pattern of 1 and 0, then just add our constant base number
of days:
f(x) = 30 + x mod 2
x |
1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 |
f(x) |
31 | 30 |
31 | 30 |
31 | 30 |
31 | 30 |
31 | 30 |
31 | 30 |
That’s a pretty good start! We’ve already got January and March through
July done. February is its own special problem we’ll deal with later.
The problem after July is that the pattern should skip one, and the rest
of the months should follow the alternating pattern inversely.
To obtain an inverse pattern of alternating 0 and 1, we can add 1 to our
dividend:
f(x) = 30 + (x + 1) mod 2
x |
1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 |
f(x) |
30 | 31 |
30 | 31 |
30 | 31 |
30 | 31 |
30 | 31 |
30 | 31 |
Now we have August through December right, but the rest of the year is
wrong as expected. Let’s see how we can combine combine our two
formulas.
Masking
What we need here is basically a piece-wise function, but that’s just no
fun. This got me thinking of other ways to use a part of a function only
over a certain domain.
I figured the easiest way to do this would be to find an expression
equal to 1 over the desired domain and 0 otherwise. Multiplying a term
by this expression will result in the term being cancelled out outside
its domain. I’ve called this “masking,” since it involves creating a
sort of bit-mask.
To mask the latter piece of our function, we need an expression equal to
1 where 8 ≤ x ≤ 12. Floor
division by 8 is perfect for this, since all our values are less than
16.
x |
1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 |
⌊x⁄8⌋ |
0 | 0 | 0 | 0 | 0 | 0 |
0 | 1 | 1 | 1 | 1 | 1 |
Now if we substitute this expression in our x + 1 dividend, we can
invert the pattern using our mask:
f(x) = 30 + (x + ⌊x⁄8⌋) mod 2
x |
1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 |
f(x) |
31 | 30 |
31 | 30 |
31 | 30 |
31 | 31 |
30 | 31 |
30 | 31 |
Woot! Everything is correct except February. What a surprise.
February
Every month has either 30 or 31 days, but February has 28 (leap years are out
of scope). February currently has 30 days according to our formula, so an
expression equal to 2 when x = 2 would be ideal for subtraction.
The best I could come up with was 2 mod x, which gives us a sort of
mask over every month after February.
x |
1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 |
2 mod x |
0 | 0 | 2 | 2 | 2 | 2 |
2 | 2 | 2 | 2 | 2 | 2 |
With this, we’ll need to change our base constant to 28 so that adding 2
to the rest of the months will still be correct.
f(x) = 28 + (x + ⌊x⁄8⌋) mod 2 + 2 mod x
x |
1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 |
f(x) |
29 | 28 |
31 | 30 |
31 | 30 |
31 | 31 |
30 | 31 |
30 | 31 |
Unfortunately, January is now 2 days short. Luckily, finding an
expression that will apply to only the first month is easy: floored
inverse of x. Now just multiply that by 2 and we
get the final formula:
f(x) = 28 + (x + ⌊x⁄8⌋) mod 2 + 2 mod x + 2 ⌊1⁄x⌋
x |
1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 |
f(x) |
31 | 28 |
31 | 30 |
31 | 30 |
31 | 31 |
30 | 31 |
30 | 31 |
Conclusion
: Hello, Internet. This is
tongue-in-cheek. Why would anyone use this?
There you have it, a formula for the number of days in each month using simple
arithmetic. So next time you find yourself wondering how many days are in
September, just remember to apply f(9). For ease of use, here’s a
JavaScript one-liner:
function f(x) { return 28 + (x + Math.floor(x/8)) % 2 + 2 % x + 2 * Math.floor(1/x); }