Why is January month 0 in Java Calendar?

Solution 1:

It's just part of the horrendous mess which is the Java date/time API. Listing what's wrong with it would take a very long time (and I'm sure I don't know half of the problems). Admittedly working with dates and times is tricky, but aaargh anyway.

Do yourself a favour and use Joda Time instead, or possibly JSR-310.

EDIT: As for the reasons why - as noted in other answers, it could well be due to old C APIs, or just a general feeling of starting everything from 0... except that days start with 1, of course. I doubt whether anyone outside the original implementation team could really state reasons - but again, I'd urge readers not to worry so much about why bad decisions were taken, as to look at the whole gamut of nastiness in java.util.Calendar and find something better.

One point which is in favour of using 0-based indexes is that it makes things like "arrays of names" easier:

// I "know" there are 12 months
String[] monthNames = new String[12]; // and populate...
String name = monthNames[calendar.get(Calendar.MONTH)];

Of course, this fails as soon as you get a calendar with 13 months... but at least the size specified is the number of months you expect.

This isn't a good reason, but it's a reason...

EDIT: As a comment sort of requests some ideas about what I think is wrong with Date/Calendar:

  • Surprising bases (1900 as the year base in Date, admittedly for deprecated constructors; 0 as the month base in both)
  • Mutability - using immutable types makes it much simpler to work with what are really effectively values
  • An insufficient set of types: it's nice to have Date and Calendar as different things, but the separation of "local" vs "zoned" values is missing, as is date/time vs date vs time
  • An API which leads to ugly code with magic constants, instead of clearly named methods
  • An API which is very hard to reason about - all the business about when things are recomputed etc
  • The use of parameterless constructors to default to "now", which leads to hard-to-test code
  • The Date.toString() implementation which always uses the system local time zone (that's confused many Stack Overflow users before now)

Solution 2:

Because doing math with months is much easier.

1 month after December is January, but to figure this out normally you would have to take the month number and do math

12 + 1 = 13 // What month is 13?

I know! I can fix this quickly by using a modulus of 12.

(12 + 1) % 12 = 1

This works just fine for 11 months until November...

(11 + 1) % 12 = 0 // What month is 0?

You can make all of this work again by subtracting 1 before you add the month, then do your modulus and finally add 1 back again... aka work around an underlying problem.

((11 - 1 + 1) % 12) + 1 = 12 // Lots of magical numbers!

Now let's think about the problem with months 0 - 11.

(0 + 1) % 12 = 1 // February
(1 + 1) % 12 = 2 // March
(2 + 1) % 12 = 3 // April
(3 + 1) % 12 = 4 // May
(4 + 1) % 12 = 5 // June
(5 + 1) % 12 = 6 // July
(6 + 1) % 12 = 7 // August
(7 + 1) % 12 = 8 // September
(8 + 1) % 12 = 9 // October
(9 + 1) % 12 = 10 // November
(10 + 1) % 12 = 11 // December
(11 + 1) % 12 = 0 // January

All of the months work the same and a work around isn't necessary.

Solution 3:

C based languages copy C to some degree. The tm structure (defined in time.h) has an integer field tm_mon with the (commented) range of 0-11.

C based languages start arrays at index 0. So this was convenient for outputting a string in an array of month names, with tm_mon as the index.

Solution 4:

There has been a lot of answers to this, but I will give my view on the subject anyway. The reason behind this odd behavior, as stated previously, comes from the POSIX C time.h where the months were stored in an int with the range 0-11. To explain why, look at it like this; years and days are considered numbers in spoken language, but months have their own names. So because January is the first month it will be stored as offset 0, the first array element. monthname[JANUARY] would be "January". The first month in the year is the first month array element.

The day numbers on the other hand, since they do not have names, storing them in an int as 0-30 would be confusing, add a lot of day+1 instructions for outputting and, of course, be prone to alot of bugs.

That being said, the inconsistency is confusing, especially in javascript (which also has inherited this "feature"), a scripting language where this should be abstracted far away from the langague.

TL;DR: Because months have names and days of the month do not.