The Leap Day/Leap Year Problems

Lots of folks have problems remembering if there’s going to be a Leap Day for a particular year.  These days, the rules are more complex than they used to be, but it still isn’t all that hard to figure out.

Default Rule: A year will not be a Leap Year.

1st Exception: If the year value leaves a remainder of zero when it’s divided by four, it will be a Leap Year.

2nd Exception: If the year value leaves a remainder of zero when it’s divided by 100, it will not be a Leap Year.

3rd Exception: If the year value leaves a remainder of zero when it’s divided by 400, it will be a Leap Year.

The sequence to remember is “Divide by 1, divide by 4, divide by 100, divide by 400”, and that the ‘Leapness’ oscillates (false, true, false, true) if the results equal some number with a remainder of zero.  Note that dividing by one is CERTAIN to leave a remainder of zero; the above-listed “Default Rule” is shown in a simpler, more concise form.  Also note that when dividing by four (in the 2nd Exception) you can ignore the ‘hundreds’ digit and the ‘thousands’ digit – it won’t matter what numbers are in those positions, because those multiples of 100 and 1000 will (of necessity) be divisible by four.  Look at the two right-most digits and try to see if they are evenly divisible by four.

It seems reasonable to extrapolate that the next ‘Exception’ in this series would be “…divide by 10,000…”, but I don’t think that it is valid to do so.  The problem is in attempting to reconcile a rational day value against an integer-oriented calendar; I don’t think that the rational value for the fraction of a day resolves with the expected regularity.

Run the four tests shown above on the year value in question (and run them in the indicated sequence), and you’ll figure out if that year has a Leap Day.  There are a couple of restrictions, though: don’t try to use it on a year value that predates the existing calendar (more on this in a moment), and don’t try to use it on year values above 9999 (because by then the Earth will have slowed down some and they’ll have made new adjustments to the calendar).

I can’t tell you exactly when the cut-off was on the low end of the year value range.  The problem is that the adjustments to the calendar weren’t officially adopted at the same time from one country to the next – in some cases, the same country had different versions of the calendar going on in different regions of the country at the same time (usually for religious reasons).  If you need a safe cut-off point, things were organized in a manner substantially similar to what they are now by the time January 1, 1800 rolled around.  If it is possibly important to you to know the exact dates for some reason, I’d suggest that you research the history of it.  Good luck – an accurate emulation of what really went on back then would be a programming nightmare.

Obviously, this stuff will not apply to the Mayan, Chinese and Muslim calendars (and possibly others as well), so don’t try to apply this information where it isn’t appropriate.

A similar problem to the one above has to do with determining how many Leap Days will occur within a given range of dates.  This problem crops up when attempting to determine things like daily periodic interest rate charges.

As a programmer, the biggest difficulty I faced with this issue was in trying to determine if Leap Day had already occurred in a year.  I was trying to run calculations based on three partial year segments: Pre-Leap-Day, Leap Day and Post-Leap-Day, then summing the calculations as appropriate.

The simplest scheme I’ve seen to deal with this dilemma couples the above-given information (in order to determine Leap Years) with a modified Julian Date calculation scheme.

If you want to know the modified Julian Day for a year, start with March 1st as day #1.  This makes February 28th day # 365.  You can add a Leap Day as needed.  You may also find it useful to subtract one from the year value if the month in question is not January or February.

Some contemplation on this will let you get the point: January First is an arbitrary place to start the year.  If you pick a different starting point (much like a “Fiscal Year” for Accounting), you can ease some of the calculation problems by tagging on a Leap Day at the tail end of the year (if it’s needed) instead of trying to wedge it in between February and March.

For converting to the modified Julian Day, I suggest that you use a look-up table with 366 entries.  This approach might not be elegant, but it is quick (because the number of calculations is, on average, seriously reduced).  You might also be able to use the same table when re-converting back from the modified Julian Day.

I’ve typed all this up from memory (frail as that can be).  If I run across the source code for the algorithm (which I still have around here somewhere), I’ll try to show it here, and maybe give a step-by-step walk through.

In the meantime, this is what I’ve got for it.  If it helps you out, or if you have questions about it, let me know.