Java says the year 0 is a leap year but year 0 never existed

TL;DR: LocalDate is doing what it is documented to do, following an international standard (ISO 8601). Whether this is "correct" or not is a whole different question.

The LocalDate Javadoc itself includes this caveat:

It is equivalent to the proleptic Gregorian calendar system, in which today's rules for leap years are applied for all time. For most applications written today, the ISO-8601 rules are entirely suitable. However, any application that makes use of historical dates, and requires them to be accurate will find the ISO-8601 approach unsuitable.

Wikipedia has more information on the proleptic Gregorian calendar. Among other things, it says:

Mathematically, it is more convenient to include a year 0 and represent earlier years as negative, for the specific purpose of facilitating the calculation of the number of years between a negative (BC) year and a positive (AD) year. This is the convention used in astronomical year numbering and in the international standard date system, ISO 8601. In these systems, the year 0 is a leap year.

Forgive me a moment while I digress into some historical context for all this.

Years in the Western calendar are ostensibly counted from the birth of Jesus Christ, but the idea of doing so started in the sixth century and our current calendar is based on calculations made in the sixteenth century. Since Roman numerals had no representation of zero nor of negative numbers, years were either counted "after Jesus" (AD, for anno domini) or "before Jesus" (BC, for "before Christ"). Thus, traditionally, 1 BC was followed by AD 1, with no year zero between.

However, in the first century, nobody counted years that way; for comparison, the Gospel of Luke describes the year when Jesus began his ministry as

in the fifteenth year of the reign of Tiberius Caesar, Pontius Pilate being governor of Judaea, and Herod being tetrarch of Galilee, and his brother Philip tetrarch of Ituraea and of the region of Trachonitis, and Lysanias the tetrarch of Abilene,

Ostensibly this would have been AD 30, since Luke describes Jesus as being "about thirty years of age" at the time. But modern historians generally agree that Dionysius Exiguus, who proposed the anno domini system in AD 525, got it wrong, and thus the numbering of years is off by at least one or two years. (The exact date is still somewhat controversial; see Wikipedia if you care for more detail.)

But it's too late to fix now; even the transition from the Julian to Gregorian calendar, which was a discrepancy of less than two weeks, was met with extensive political resistance as the changeover occurred throughout Europe over a period of several centuries -- you can imagine how disruptive a change of year numbering would be now!

So what does this history have to do with software today? Unfortunately, due to the myriad ways in which dates were calculated and written down throughout history, you either need to give up on the calendar behaving in a consistent way as you move forward and backward in time, or you have to give up on calculated dates having any correspondence to the dates that real people would have used at the time. The divergence happens more rapidly than you might think: many European countries were still using the Julian calendar less than 100 years ago, with a discrepancy of nearly two weeks from everyone else in Europe!


Understandably, LocalDate washes its hands of this mess and only implements the calendar the way we use it today. Reiterating what the Javadoc says: "For most applications written today, the ISO-8601 rules are entirely suitable. However, any application that makes use of historical dates, and requires them to be accurate will find the ISO-8601 approach unsuitable."


From Wikipedia:

...there is a year zero in astronomical year numbering (where it coincides with the Julian year 1 BC) and in ISO 8601:2004 (where it coincides with the Gregorian year 1 BC)