
A Formula for the Number of Days in Each Month - tolmasky
http://cmcenroe.me/2014/12/05/days-in-month-formula.html
======
Someone
Simple (warning: I did not verify this result):

    
    
        -(       11 x^11)/907200
        +(      163 x^10)/181440
        -(       37 x^9 )/1260
        +(    13481 x^8 )/24192
        -(  2055371 x^7 )/302400
        +(   240683 x^6 )/4320
        -( 28268521 x^5 )/90720
        +( 85774775 x^4 )/72576
        -(446998571 x^3 )/151200
        +( 46351537 x^2 )/10080
        -(   221017 x   )/56
        +1416
    

That is a nice continuous, infinitely differentiable function.

Source:
[http://www.wolframalpha.com/input/?i=interpolating+polynomia...](http://www.wolframalpha.com/input/?i=interpolating+polynomial+calculator&f1=%7B31%2C28%2C31%2C30%2C31%2C30%2C31%2C31%2C30%2C31%2C30%2C31%7D&f=InterpolatingPolynomialCalculator.data_%7B31%2C28%2C31%2C30%2C31%2C30%2C31%2C31%2C30%2C31%2C30%2C31%7D&a=*FVarOpt.1-_***InterpolatingPolynomialCalculator.data--.***InterpolatingPolynomialCalculator.data2---.*--)

To get something more impressive, I would use the discrete Fourier transform.
That leads to
[http://en.wikipedia.org/wiki/Trigonometric_interpolation](http://en.wikipedia.org/wiki/Trigonometric_interpolation).
That gives you a nice formula with lots of sines and cosines.

~~~
AnkhMorporkian
This is especially handy when you want to figure out how many days are in that
pesky month between January and February.

~~~
pavlov
Looking at the Wolfram Alpha plot, it appears that the month about halfway
between November and December is the longest of the year, with almost 33 days.

We could call it Gloomember, the month that seems to go on and on.

~~~
cgriswald
I say we call it Partyember because it is the month most likely to have five
full weekends.

------
USNetizen
I believe Zeller's Congruence had already solved this, no?

Zeller's is based upon calculating the day of the week, but it also calculates
days in the month and accounts for leap years. Plus, it's been around since
the 19th century and is used in many major programming languages to calculate
date (its implementation is sometimes a first year Comp Sci course project for
introductory programming):
[http://en.wikipedia.org/wiki/Zeller%27s_congruence](http://en.wikipedia.org/wiki/Zeller%27s_congruence)

For a programming example that does this same thing:
[https://github.com/mwhawkins/JavaZellers](https://github.com/mwhawkins/JavaZellers)

How did this make the front page of HN being a problem which has already been
solved and well documented for almost 200 years? Not to knock the author, as
they put in some time apparently, but was just curious.

~~~
chime
> How did this make the front page of HN being a problem which has already
> been solved and well documented for almost 200 years? Not to knock the
> author, as they put in some time apparently, but was just curious.

Zeller's Congruence is an algorithm. This is a formula. It made it to the
front page because it was a clever hack and people like me love those.

~~~
thaumasiotes
Zeller's Congruence is a formula as well (well, more than one depending on
which calendar you want to use). Go look at the wikipedia article.

More seriously, any algorithm that doesn't do user I/O is expressible as a
fixed formula. What distinction are you trying to draw?

~~~
JadeNB
> More seriously, any algorithm that doesn't do user I/O is expressible as a
> fixed formula.

This is probably true for some very weak definition of the terms, but not, I
think, in any sense that a random person would recognise as a 'formula'. For
example, what is the formula for the algorithm "on input `n`, output the first
position in the decimal expansion of `pi` at which a copy of the decimal
expansion of `n` begins"?

~~~
thaumasiotes
The fact that I can't tell you a formula doesn't mean there isn't one. For
example, it's trivial for me to come up with input for that problem that you
(or anyone else in the world) would be unable to solve. When the answers are
unknown, I don't find it all that surprising that it's difficult to list or
otherwise classify them.

If you believe that f(x) = |x| is a "formula" giving the distance of a real
number from zero, it's just as conditionally defined as (but less infinite
than) the infinite lookup table you hypothesize.

On a more fun-fact note, there was a result some years ago expressing pi as an
infinite series in powers of 16, with the headlining implication being "it may
be possible to calculate the _n_ th digit of pi (in base 16, or other powers
of 2) directly, without needing to know the preceding digits" (I don't know
the state of things since then, and details may have been jumbled in my
memory). I suspect that there are implications for the puzzle you pose.

~~~
JadeNB
> If you believe that f(x) = |x| is a "formula" giving the distance of a real
> number from zero, it's just as conditionally defined as (but less infinite
> than) the infinite lookup table you hypothesize.

If you allow a formula to be an infinite look-up table, then the statement
"every [always halting] algorithm is a fixed formula" becomes true, but, I
think, almost meaningless—it just says that the algorithm produces an output
for every input. This is what I meant by saying:

    
    
        This is probably true for some very weak definition of the terms, but not, I think, in any sense that a random person would recognise as a 'formula'.
    

> On a more fun-fact note, there was a result some years ago expressing pi as
> an infinite series in powers of 16, with the headlining implication being
> "it may be possible to calculate the nth digit of pi (in base 16, or other
> powers of 2) directly, without needing to know the preceding digits" (I
> don't know the state of things since then, and details may have been jumbled
> in my memory). I suspect that there are implications for the puzzle you
> pose.

No 'may' about it—it _is_ possible to find the hexadecimal (and thus binary,
'quaternary', or octal, but not higher) digits using this algorithm. However,
the discoverers explicitly disclaim its utility for finding decimal digits of
pi in this way, and, indeed, say that no such 'look-ahead' algorithm is known
(see p. 55 of
[http://link.springer.com/article/10.1007%2FBF03024340](http://link.springer.com/article/10.1007%2FBF03024340)).

~~~
thaumasiotes
What do you mean, but not higher? If you want a base-256 digit, calculate the
two base-16 digits that make it up. That's actually a simpler process than
you'd have to go through for the nth octal digit (compare -- the second octal
digit of pi is composed of, as high bit, the low bit of the first hex digit,
followed by the two high bits of the second hex digit. But the 8th 256-ary
digit of pi is just the concatenation of the 15th and 16th hex digits). In
general, since you can calculate any adjacent 4 bits of the binary expansion,
you can then aggregate those bits however you like, making it easy to get the
nth digit in any power of two. The only complication is that, since you
generate bits in blocks of four, you may end up having calculated as many as 6
bits that you didn't need.

You object to my characterizing the pi-reading function you describe as an
infinite lookup table. But it can't be implemented as an "intuitive" algorithm
either. No matter how you code a function to solve that problem, it will
always be trivial to provide input that your function cannot handle (if you
have a representation of pi that you search, then you're relying on an
infinite lookup table. If you calculate pi on the fly, you won't be able to
handle large numbers as input). So I don't see why a defective (because you
don't have enough time, or space, to run it) algorithm tranforming into a
defective (because you don't have enough space to store it, but hey, time is
no longer a concern) formula is a big problem for the equivalence between
algorithms and formulae.

Also, I have to note that you've referred to the following mathematical
result:

    
    
        pi = \sum_{k=0}^\infty (16^{-k} * [4/(8k+1) - 2/(8k+4) - 1/(8k+5) - 1/(8k+6)]) 
    

as an "algorithm" instead of a "formula" ;)

~~~
JadeNB
> What do you mean, but not higher? If you want a base-256 digit, calculate
> the two base-16 digits that make it up.

I'm sorry; you're quite right, and I was wrong.

> No matter how you code a function to solve that problem, it will always be
> trivial to provide input that your function cannot handle (if you have a
> representation of pi that you search, then you're relying on an infinite
> lookup table. If you calculate pi on the fly, you won't be able to handle
> large numbers as input).

I'm not sure why you say that I won't be able to handle large numbers as
input. It might take a long time (like, a universe-endingly long time), but so
would printing out the decimal expansion of 10↑↑10
([https://en.wikipedia.org/wiki/Knuth%27s_up-
arrow_notation](https://en.wikipedia.org/wiki/Knuth%27s_up-arrow_notation)),
and I don't think that anyone would claim that that means that exponentiation
algorithms "can't handle" this kind of exponentiation. (I dunno; maybe people
would claim that.)

> So I don't see why a defective (because you don't have enough time, or
> space, to run it) algorithm tranforming into a defective (because you don't
> have enough space to store it, but hey, time is no longer a concern) formula
> is a big problem for the equivalence between algorithms and formulae.

My problem is that I think that there is no such equivalence, unless the
definition of 'formula' is made so broad as to be essentially _synonymous_
with 'algorithm'—at which point it's true but un-interesting. All I am arguing
is (as a constructivist would) that it is worthwhile to maintain meaningful
distinctions.

> Also, I have to note that you've referred to the following mathematical
> result `pi = [elided]` as an "algorithm" instead of a "formula" ;)

I did not mean to do that; I was referring to the process of _using_ that
formula (which is, as it were, an 'inert' object) to _produce_ hexadecimal
digits as an algorithm.

------
pzxc
Thirty days hath September

April, June, and November

All the rest have 31

Except for the very special one.

...

The knuckles method, for those who don't know: Make a fist. Start with January
on your first knuckle. Counting knuckles AND the gaps between knuckles, so
each knuckle is 31 days, the gap between is 30 days (or 28/29 for february).
Repeat on the last knuckle before reversing.

...

So--

KNUCKLE_gap_KNUCKLE_gap_KNUCKLE_gap_KNUCKLE

JAN_feb_MAR_apr_MAY_jun_JUL (repeat last knuckle and reverse)

AUG (knuckle repeat)_sep_OCT_nov_DEC (ending on your middle knuckle)

ALLCAPS = KNUCKLE = 31 days

~~~
javert
Re: the knuckle technique. I prefer to simply start over again from the first
knuckle, as opposed to reversing direction.

It gives you one less thing to remember, since you don't have to remember to
repeat on the last knuckle.

~~~
smokey_the_bear
I learned it that you move onto the other hand.

~~~
bhrgunatha
Me too and it must mean something that I still use this method from time to
time. Why is is so difficult to just remember?

~~~
javert
> Why is is so difficult to just remember?

Probably because there is almost a pattern, but not quite.

------
Dylan16807
Great, in your distaste for the method that can involve knuckles, you have
recreated it exactly.

    
    
      1. alternate 31 and 30
      2. reset in month 8
      3. alter feb to 28
    

In my mind using modulus and floor in a mathematical formula is no more valid
than using cases. A clever formula would avoid all of them.

~~~
jcrites
> In my mind using modulus and floor in a mathematical formula is no more
> valid than using cases

Agreed. Using floor it seems like you can construct a function that's
effectively defined piecewise, by constructing a floor expression that's zero
outside of a specified input domain, and 1 within the domain. Then you
multiply the floor expression by the piecewise expression that you'd use to
define a piecewise function over that domain.

Nevertheless I found the article to be an interesting read on mathematical
reasoning - a thought process for designing functions with particular
behavior. Is there a rigorous way of describing why mod and floor seem like
cheating in a way, aside from the fact that they can be used to construct
piecewise functions? Is the ideal hack in this scenario a continuous, smooth
function? (And something more interesting than the result of polynomial
interpolation?)

~~~
thaumasiotes
> Using floor it seems like you can construct a function that's effectively
> defined piecewise, by constructing a floor expression that's zero outside of
> a specified input domain, and 1 within the domain. Then you multiply the
> floor expression by the piecewise expression that you'd use to define a
> piecewise function over that domain.

And you can fit a polynomial the same way by (using the example at hand)
defining

    
    
        f(x) =  c_1(x-2)(x-3)...(x-12)
             +  c_2(x-1)(x-3)...(x-12)
             +  c_3(x-1)(x-2)...(x-12) [but the (x-3) factor is missing!]
             +  .      .            .
             +  .       .           .
             +  .        .          .
             + c_12(x-1)(x-2)...(x-11)
    

And then choosing c_1 such that the c_1 term is equal to 31 when x=1, c_2 such
that the c_2 term is equal to 28 when x=2, and so forth. You'll get a well-
defined polynomial, impeccably "mathematical" \-- in fact, you should get the
polynomial listed in Someone's comment (since there should only be one 11th-
degree polynomial passing through those 12 points). The traditional way to fit
a polynomial to points is by reducing a matrix, linear-algebra style, but this
approach is equivalent and, as you can see, amounts to nothing more than
defining several functions which are zero except at the point of interest, and
then scaling them to take the proper value at the point of interest. Why is
that more "valid" than using a modulus?

Frankly, I'm much more concerned by the OP using "2 mod x" when 1 is a legal
value for x.

------
OMGWTF
// without floats, multiplication, division, modulo:

int result = (30|m^(m>>3)) - (((m^13)+1)>>4<<1);

~~~
grandalf
meant to upvote, accidentally downvoted.

------
btbuildem
Your solution (which does not account for leap years, btw) simplifies to:

    
    
      def days(month):
          return [31,28,31,30,31,30,30,31,30,31,30,31][month]

~~~
thaumasiotes
I can't wait for 0/0/2015 ;)

~~~
btbuildem
Ha, nice one. I'm mixing my Es with my Ps

------
svisser
The easiest function would be a mapping from 12 input values to 12 output
values.

For example, see the Collatz conjecture which only defines two cases (but one
could have 12):
[http://en.wikipedia.org/wiki/Collatz_conjecture](http://en.wikipedia.org/wiki/Collatz_conjecture)

------
doragcoder
There was a Hacker News article about why August has 31 days, breaking the
30/31 alternating days. I can't find it but here's an article about it.

[http://www.infoplease.com/spot/history-of-
august.html](http://www.infoplease.com/spot/history-of-august.html)

It just makes it a more interesting formula because of that!

------
kayamon

        days = 28 + ((0x3bbeecc>>(month*2))&3);

~~~
danieltillett
Where does this come from?

~~~
OMGWTF
The basic idea is to encode the array within a single integer:

    
    
       0 + [anything, 31,28,31,30,31,30,31,31,30,31,30,31]
      28 + [anything,  3, 0, 3, 2, 3, 2, 3, 3, 2, 3, 2, 3]
                  00, 11,00,11,10,11,10,11,11,10,11,10,11
    
       reverse:   11,10,11,10,11,11,10,11,10,11,00,11, 00
                0011  1011  1011  1110  1110  1100   1100
              0x   3     b     b     e     e     c      c   
    

We have 4 possible values in the array, so we need 2 bits per value. We have
one dummy value + 12 required values, so we need a total of 2*13 = 26 bits
which fits easily into a 4 byte integer (either 32 bits unsigned or 31 bits
signed). To retrieve one element from the "array", we need to shift our
constant 2 bits per month or twice by the numer of months. Then we need to
mask everything but the lowest 2 bits (&3).

~~~
danieltillett
Great explaination. This is the sort of post that makes me love HN.

------
xxxyy
Anybody knows the reason behind August having 31 days? This is the source of
the complexity of the days-in-a-month problem. All I can find is this legend,
which could even be true.

[http://timesofindia.indiatimes.com/home/stoi/Why-does-
Februa...](http://timesofindia.indiatimes.com/home/stoi/Why-does-February-
have-28-days-and-July-and-August-31-days/articleshow/2677672.cms)

~~~
GFK_of_xmaspast
[http://en.wikipedia.org/wiki/Julian_calendar#Sacrobosco.27s_...](http://en.wikipedia.org/wiki/Julian_calendar#Sacrobosco.27s_theory_on_month_lengths)

------
ChuckMcM
Great analysis. The next step would be to see if the code to implement the
function can be shorter than 12 bytes (and array of bytes holding the number
of days in the month such that f(x) = days_of_month[x]; is longer than the
computation. Of course from a pure cycle counting advantage doing it as a
calculation will always be faster as memory is slow to access.

~~~
OMGWTF
How about 15 bytes? (x86, month in ecx, result in eax)

    
    
      // 28 + ((0x3bbeecc>>m>>m)&3);
      b8 cc ee bb 03    mov  eax,0x3bbeecc
      d3 f8             sar  eax,cl
      d3 f8             sar  eax,cl
      83 e0 03          and  eax,0x3
      83 c0 1c          add  eax,0x1c

~~~
toast0
I think you can save a byte on the add with

    
    
        04 1c             add al, 0x1c
    

(but i've never written any x86 assembly)

~~~
OMGWTF
You are right. I was lazy, I compiled it with gcc -O2 and extracted the
assembler with objdump -Mintel -d.

------
joezydeco
Great start. Now let's add leap years. =)

~~~
eridal
but that's trival, isn't that already solved?

    
    
        function f(x) { return 28 + maybeLeap(x) + (...); }
    

I remember it's something related with "(x % 4) == 0" and "not ends_with(00)"

~~~
agrona
Except for every 400th year.

~~~
eridal
how about this?

    
    
            leap = function(x) { 
              if ((x % 4) != 0) {
                return 0;
              }
              return Number(
                (x % 100) != 0 || (x % 400) == 0
              );
            }
    
            function(x) { return 28 + leap(x) + (...) };

------
gordaco
For something more or less related, check out the calculation of the date of
Easter:
[http://en.wikipedia.org/wiki/Computus](http://en.wikipedia.org/wiki/Computus).
Some versions, like Gauss' algorithm, also relied heavily on moduli.

------
recursive
Here's mine. No floating point logic required.

let days = (i ^ (i >> 3) | 30) - 1 / (i * 9 % 17) * 2

~~~
arthurcolle
What language would this work in? let looks like ocaml but those don't look
like the write syntactical ways to do math stuff IIRC

~~~
ratsmack
Just execute it using the shell:

    
    
       echo $(( i = <month>, (i ^ (i >> 3) | 30) - 1 / (i * 9 % 17) * 2 ))

------
no_gravity
Can be shortened to 28+(x+~~(x/8))%2+2%x+2*~~(1/x)

------
danbruc
29 + 2 * ABS(SIGN(month - 2)) - FLOOR(ABS(month - 7.5)) % 2

------
mariodiana
A perfect illustration of why a simple lookup table is often the better
implementation. I'll stick to the knuckles method—thank you very much.

------
shitlord
Was I the only one who expected to see a Dirac delta function used for
February? I was pleasantly surprised.

------
kazagistar
Compiles down to something like a hundred bytes of code. What a savings!

------
xclojure
Why is it any better/faster than a simple switch case like this in Clojure:
#(case % 1 31 2 28 3 31 4 30 5 31 6 30 7 31 8 31 9 30 10 31 11 30 12 31)

~~~
arsenerei
#([31 28 31 30 31 30 31 31 30 31 30 31] (dec %))

> Why is it any better/faster than a simple switch case like this in Clojure

It's not about better or faster. It's fun to think of things like this. :)

------
mikesmith
Thirty days have September, April, June, and November. All the rest have 31,
Except for February all alone, It has 28 each year, but 29 each leap year.

------
grandalf
What's the formula for the number of days in a month+year (so that it
calculates leap years formulaically too?

------
allannienhuis
Javascript:

var f = function(i){return new Date(2014,i,0).getDate();};

JSON.stringify([1,2,3,4,5,6,7,8,9,10,11,12].map(f)) ==
JSON.stringify([31,28,31,30,31,30,31,31,30,31,30,31]);

:P

------
gckruger
This is awesome. Well done, OP.

------
tlarkworthy
my digital attempt:-

function days(m) { return 30 + ((m + (m > 6)) % 2) - 2 * (m == 2) }

------
freework

        def days_in_month(month_num):
            if month_num in [1, 3, 5, 7]:
                return 31
            elif month_num in [4, 6, 9, 11]:
               return 30
            return 28

