Why is January month 0 in Java Calendar?

Viewed 190384

In java.util.Calendar, January is defined as month 0, not month 1. Is there any specific reason to that ?

I have seen many people getting confused about that...

17 Answers

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)

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.

In Java 8, there is a new Date/Time API JSR 310 that is more sane. The spec lead is the same as the primary author of JodaTime and they share many similar concepts and patterns.

I'd say laziness. Arrays start at 0 (everyone knows that); the months of the year are an array, which leads me to believe that some engineer at Sun just didn't bother to put this one little nicety into the Java code.

Probably because C's "struct tm" does the same.

Because programmers are obsessed with 0-based indexes. OK, it's a bit more complicated than that: it makes more sense when you're working with lower-level logic to use 0-based indexing. But by and large, I'll still stick with my first sentence.

Personally, I took the strangeness of the Java calendar API as an indication that I needed to divorce myself from the Gregorian-centric mindset and try to program more agnostically in that respect. Specifically, I learned once again to avoid hardcoded constants for things like months.

Which of the following is more likely to be correct?

if (date.getMonth() == 3) out.print("March");

if (date.getMonth() == Calendar.MARCH) out.print("March");

This illustrates one thing that irks me a little about Joda Time - it may encourage programmers to think in terms of hardcoded constants. (Only a little, though. It's not as if Joda is forcing programmers to program badly.)

The true reason why

You would think that when we deprecated most of Date and added the new Calendar class, we would have fixed Date's biggest annoyance: the fact that January is month 0. We certainly should have, but unfortunately we didn't. We were afraid that programmers would be confused if Date used zero-based months and Calendar used one-based months. And a few programmers probably would have been. But in hindsight, the fact that Calendar is still zero-based has caused an enormous amount of confusion, and it was probably the biggest single mistake in the Java international API's.

Quoted from International Calendars in Java by Laura Werner, link at the bottom.

The better alternative: java.time

This may just be repeating what others have said, throw the old and poorly designed Calendar class overboard and use java.time, the modern Java date and time API. There months are consistently sanely numbered from 1 for January through 12 for December.

If you are getting a Calendar from a legacy API not yet upgraded to java.time, the first thing to do is to convert to a modern ZonedDateTime. Depending on your needs you may do further conversions from there. In most of the world the Calendar object you get will virtually always be an instance of the GregorianCalendar subclass (since the Calendar class itself is abstract). To demonstreate:

    Calendar oldfashionedCalendarObject = Calendar.getInstance();
    ZonedDateTime zdt
            = ((GregorianCalendar) oldfashionedCalendarObject).toZonedDateTime();
    
    System.out.println(zdt);
    System.out.format("Month is %d or %s%n", zdt.getMonthValue(), zdt.getMonth());

Output when I ran just now in my time zone:

2021-03-17T23:18:47.761+01:00[Europe/Copenhagen]
Month is 3 or MARCH

Links

It isn't exactly defined as zero per se, it's defined as Calendar.January. It is the problem of using ints as constants instead of enums. Calendar.January == 0.

Because language writing is harder than it looks, and handling time in particular is a lot harder than most people think. For a small part of the problem (in reality, not Java), see the YouTube video "The Problem with Time & Timezones - Computerphile" at https://www.youtube.com/watch?v=-5wpm-gesOY. Don't be surprised if your head falls off from laughing in confusion.

In addition to DannySmurf's answer of laziness, I'll add that it's to encourage you to use the constants, such as Calendar.JANUARY.

Related