It was intriguing, given the awkwardness of dates, some months 30 days, others 31 days, February 28 days except sometimes 29 dates etc, how does one convert from yyyy-mm-dd to serial number and back? Surely it had to be some awkward algorithm involving table lookups, yet somehow in the code there is a formula involving 1461 and 153m+2…
Month | Days | m | j |
March | 31 | 0 | 0 |
April | 30 | 1 | 31 |
May | 31 | 2 | 61 |
June | 30 | 3 | 92 |
July | 31 | 4 | 122 |
August | 31 | 5 | 153 |
September | 30 | 6 | 184 |
October | 31 | 7 | 214 |
November | 30 | 8 | 245 |
December | 31 | 9 | 275 |
January | 31 | 10 | 306 |
February | 28 | 11 | 337 |
The first important step is the leap period, it’s the first glimpse of a pattern amongst the uneven dates. There is a natural cycle of 1461=365+365+365+366 days, and for convenience we choose to leave the leap day 29-Feb to end of the cycle. Now focusing on the serial dates within a 365-day year starting from March, one can determine mathematically that the straight line,
j=30.6014m+0.02564
provides a very close fit to the serial number and the month number. However, one is only interested in values of j at m=0,1,2,…,11 and the values of m at j=0,1,..,365, so consider integer arithmetic, not least because it would likely be faster to execute. First reduce the truncation error by scaling by 5, that is, 5j=153.0007m + 0.1282. In integer arithmetic, j=(153m+0)/5 would seem a reasonable guess, but it does not quite work. However,
j=(153m+2)/5
works perfectly, the inelegant round off and integer truncation works in such a way as to exactly match the slightly uneven serial number to month number function. This is the elegant part of Algorithm 199 explained by Paul Kimpel.
The transformation, j=(153m+2)/5 is not unique, for example j=(306m+5)/10 would also work. Since it is integer arithmetic, it might be judicious to seek a divisor that is a power of 2. One such transformation is,
j=(979m+15)/32
(actually, replacing 15 by 16, 17, 18 or 19 will also work). The inverse is computed by m=(5*j-3)/153 (or m=(32*j-16)/979).