344

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...

Stéphane Bonniez
  • 4,577
  • 3
  • 21
  • 16
  • 5
    Isn't that kind of an implementation detail, since the constants JANUARY, FEBRUARY etc. exists? The date classes predates proper java enum support. – gnud Dec 05 '08 at 16:42
  • 7
    Even more annoying - why is there an Undecember? – matt b Dec 05 '08 at 17:04
  • 47
    @gnud: No, it's not an implementation detail. It makes it a pain when you've been given an integer in "natural" base (i.e. Jan=1) and you need to use it with the calendar API. – Jon Skeet Dec 05 '08 at 17:12
  • 1
    @matt b: it's for non-Gregorian calendars (lunar calendars, etc) that have thirteen months. That's why it's best not to think in terms numbers, but let Calendar do it's localization. – erickson Dec 05 '08 at 17:37
  • 9
    The 13-month argument makes no sense. If that's so, why not have the extra month be 0 or 13? – Quinn Taylor Aug 11 '09 at 03:23
  • 1
    This is the problem with a widely used API. Entire systems around the world will break if they change it. Why couldn't they just keep things simple? Even if *internally* they wanted to consider Jan=0 Dec=11, when a user asks for the month, they could at least return the value+1, as Jan=1 Dec=12. Or now, they could just add a new method called getMonth() that will always return 1-12 even if get(Calendar.MONTH) returns 0-11. Argh... – ADTC Feb 01 '13 at 06:55
  • Same with JavaScript – Christophe Roussy Jun 06 '16 at 16:28
  • I read once a piece by the lead designer of `Calendar`. She said that they expected people to prefer 0-based months because they were used to 0-based months from `Date`, and pointed this decision out as the greatest design mistake in `Calendar`. – Ole V.V. Dec 27 '17 at 14:30
  • Phew, spent half an hour and came across this article, Now I have to do `String month = String.valueOf(calendar.get(Calendar.MONTH) + 1);` add 1 to my month. Any better way to solve this problem or do I still have to +1 for month variable? – Yogesh D May 02 '19 at 23:21
  • 1
    @Yogesh There are several better ways. The best is to throw the old and poorly designed `Calendar` class overboard and use [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). There months are sanely numbered from 1 thourgh 12. If you cannot avoid getting a `Calendar`, assuming it’s a `GregorianCalendar` convert: `((GregornaCalendar) yourCalendar).toZonedDateTime()`. – Ole V.V. Mar 17 '21 at 21:52

18 Answers18

354

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)
assylias
  • 321,522
  • 82
  • 660
  • 783
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 18
    ...and what's up with deprecating all the useful simple Date methods? Now I have to use that horrible Calendar object in complicated ways to do things that used to be simple. – Brian Knoblauch Dec 05 '08 at 17:22
  • 3
    @Brian: I feel your pain. Again, Joda Time is simpler :) (The immutability factor makes things so much more pleasant to work with, too.) – Jon Skeet Dec 05 '08 at 17:26
  • 8
    You did not answer the question. – Zeemee Feb 02 '12 at 13:38
  • @Mulmoth: Okay, I'd left it before now as other answers provided guesses - but see if you prefer the new edit (which also no longer claims JSR-310 will be in Java 7...) – Jon Skeet Feb 02 '12 at 14:48
  • What's funny is I've always found it more convenient to use a 13-item array of month names, so that formatting 0/0/0 as DD-MMM-YY would yield 00-XXX-00 rather than 00-JAN-00. – supercat Jul 26 '14 at 00:25
  • 3
    If you are using Java 8 then you can ditch the Calendar class and switch to the new and sleek [DateTime API](https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html). The new API also includes an immutable/threadsafe [DateTimeFormatter](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html) which is a big improvement over the problematic and expensive SimpleDateFormat. – ccpizza Dec 25 '15 at 14:50
  • is there any way i can make month start from 1 ? – Derrick Jan 31 '18 at 06:16
  • 2
    @DerickDaniel: Not in java.util.Calendar, no. But I strongly advise you to use java.time instead, which *does* use 1-based months. – Jon Skeet Jan 31 '18 at 07:33
  • 1
    Yep. No good reason for this. The reason why classic array indexes in C start at 0 and not one is because, technically, you aren't asking for "item x"; instead you are asking for the "item offset X from the first item". The array name itself is the base address, the index to the array is the offset from the base, not the desired position. So... We don't think of days or months as "offset" from the beginning and thus they should be designated 1..n. And if you argue that it's good for arrays indexed by month then you damn well should have done days the same way for the same reason. – WiegleyJ Nov 21 '19 at 03:35
59

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.

arucker
  • 709
  • 7
  • 7
  • 3
    "Lots of magical numbers" - nah, it's just one that appears twice. – user123444555621 Aug 20 '14 at 22:53
  • Going back a month is still sorta icky, though, thanks to C's unfortunate use of a "remainder" operator rather than "modulus". I'm also not sure how often one really needs to bump a month without adjusting the year, and having months go 1-12 creates no problem with `while (month > 12) { month-=12; year++;} – supercat Sep 14 '15 at 22:32
  • 4
    Because sane functions like DateTime.AddMonths are too hard to be implemented properly in a lib, we have to do the math you described yourselves... Mmmmmkay – nsimeonov Mar 30 '16 at 06:19
  • Someone does the math somewhere. Just because the system has a function defined to do math on months doesn't mean the same math didn't happen. My example wasn't to encourage people to do the math on the months, it was only to show the benefit of declaring January as 0. – arucker Apr 11 '16 at 18:39
  • 11
    I don't understand these upvotes — `((11 - 1 + 1) % 12) + 1 = 12` is just `(11 % 12) + 1` i.e. for months 1..12 you just need to add the 1 *after* doing the modulo. No magic required. – mfitzp Jun 20 '19 at 07:27
38

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.

stesch
  • 7,202
  • 6
  • 47
  • 62
27

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.

piksel bitworks
  • 693
  • 8
  • 14
  • 1
    *"months have names and days do not."* Ever heard of 'Friday'? ;) OK I'm guessing you meant '..days of the month do not' - maybe it would pay to edit your (otherwise good) answer. :-) – Andrew Thompson Aug 24 '11 at 17:11
  • Is 0/0/0000 better rendered as "00-Jan-0000" or as "00-XXX-0000"? IMHO, a lot of code would haveen been cleaner if there were thirteen "months" but month 0 was given a dummy name. – supercat Sep 14 '15 at 22:29
  • 1
    thats an interesting take, but 0/0/0000 is not a valid date. how would you render 40/40/0000? – piksel bitworks Sep 15 '15 at 19:09
12

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.

Alex Miller
  • 69,183
  • 25
  • 122
  • 167
11

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.

TheSmurf
  • 15,337
  • 3
  • 40
  • 48
  • 10
    No, I wouldn't. It is more important to optimize the efficiency of one's customers than one's programmers. Since this customer is spending time here asking, they failed at that. – TheSmurf Dec 05 '08 at 16:45
  • 2
    It's totally unrelated to efficiency — it's not as if months are stored in an array and you'd need 13 to represent 12 months. It's a matter of not making the API as user-friendly as they should have been in the first place. Josh Bloch rags on Date and Calendar in "Effective Java". Very few APIs are perfect, and the date/time APIs in Java have the unfortunate role of being the ones that were goofed. That's life, but let's not pretend it has anything to do with efficiency. – Quinn Taylor Aug 11 '09 at 03:06
  • 1
    Why don't count days from 0 to 30 then? It's just inconsistent and sloppy. – Juangui Jordán Jan 24 '17 at 13:00
9

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

Paul Tomblin
  • 179,021
  • 58
  • 319
  • 408
8

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.

Dinah
  • 52,922
  • 30
  • 133
  • 149
  • 1
    This is another of those idioms/habits that go _way_ back to assembler or machine language where everything is done in terms of offsets, not indexes. Array notation became a short cut for accessing contiguous blocks, starting at offset 0. – Ken Gentle Dec 05 '08 at 17:56
5

java.time.Month

Java provides you another way to use 1 based indexes for months. Use the java.time.Month enum. One object is predefined for each of the twelve months. They have numbers assigned to each 1-12 for January-December; call getValue for the number.

Make use of Month.JULY (Gives you 7) instead of Calendar.JULY (Gives you 6).

(import java.time.*;)
Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
Digital_Reality
  • 4,488
  • 1
  • 29
  • 31
5

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

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
4

For me, nobody explains it better than mindpro.com:

Gotchas

java.util.GregorianCalendar has far fewer bugs and gotchas than the old java.util.Date class but it is still no picnic.

Had there been programmers when Daylight Saving Time was first proposed, they would have vetoed it as insane and intractable. With daylight saving, there is a fundamental ambiguity. In the fall when you set your clocks back one hour at 2 AM there are two different instants in time both called 1:30 AM local time. You can tell them apart only if you record whether you intended daylight saving or standard time with the reading.

Unfortunately, there is no way to tell GregorianCalendar which you intended. You must resort to telling it the local time with the dummy UTC TimeZone to avoid the ambiguity. Programmers usually close their eyes to this problem and just hope nobody does anything during this hour.

Millennium bug. The bugs are still not out of the Calendar classes. Even in JDK (Java Development Kit) 1.3 there is a 2001 bug. Consider the following code:

GregorianCalendar gc = new GregorianCalendar();
gc.setLenient( false );
/* Bug only manifests if lenient set false */
gc.set( 2001, 1, 1, 1, 0, 0 );
int year = gc.get ( Calendar.YEAR );
/* throws exception */

The bug disappears at 7AM on 2001/01/01 for MST.

GregorianCalendar is controlled by a giant of pile of untyped int magic constants. This technique totally destroys any hope of compile-time error checking. For example to get the month you use GregorianCalendar. get(Calendar.MONTH));

GregorianCalendar has the raw GregorianCalendar.get(Calendar.ZONE_OFFSET) and the daylight savings GregorianCalendar. get( Calendar. DST_OFFSET), but no way to get the actual time zone offset being used. You must get these two separately and add them together.

GregorianCalendar.set( year, month, day, hour, minute) does not set the seconds to 0.

DateFormat and GregorianCalendar do not mesh properly. You must specify the Calendar twice, once indirectly as a Date.

If the user has not configured his time zone correctly it will default quietly to either PST or GMT.

In GregorianCalendar, Months are numbered starting at January=0, rather than 1 as everyone else on the planet does. Yet days start at 1 as do days of the week with Sunday=1, Monday=2,… Saturday=7. Yet DateFormat. parse behaves in the traditional way with January=1.

Edwin Dalorzo
  • 76,803
  • 25
  • 144
  • 205
4

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.)

Paul Brinkley
  • 6,283
  • 3
  • 24
  • 33
  • 1
    But which scheme is more likely to give you a headache when you don't have a constant in your code - you have a value which is the result of a web service call or whatever. – Jon Skeet Dec 05 '08 at 17:28
  • That web service call should also be using that constant, of course. :-) Same goes for any external caller. Once we've established that multiple standards exist, the need to enforce one becomes evident. (I do hope I understood your comment...) – Paul Brinkley Dec 05 '08 at 18:59
  • 3
    Yes, we should enforce the standard that almost everything else in the world uses when expressing months - the 1-based standard. – Jon Skeet Dec 05 '08 at 19:20
  • The key word here being "almost". Obviously, Jan=1, etc. feels natural in a date system with extremely wide use, but why allow ourselves to make an exception to avoiding hardcoded constants, in even this one case? – Paul Brinkley Dec 05 '08 at 21:34
  • 3
    Because it makes life easier. It just does. I have *never* encountered an off-by-one problem with a 1-based month system. I've seen *plenty* such bugs with the Java API. Ignoring what everyone else in the world does just makes no sense. – Jon Skeet Dec 06 '08 at 07:33
  • I sympathize; though I don't do very large amounts of Java date programming, I've never run into an off-by-one problem either, in any other language. The only thing that bugs me here is the hardcoded-ness of it. – Paul Brinkley Dec 08 '08 at 21:05
  • This example is contrived. In practice, most code doesn't actually work with these constants. Most of it is about reading, manipulating and writing data. When you read a month from a database column/web service/what-have-you, it's going to be 1-based. If you want to put it into a Calendar object for any reason, you'll need to subtract 1. The constants have nothing to do with the decision to make it 0-based. – yonil Jul 09 '13 at 13:16
3

tl;dr

Month.FEBRUARY.getValue()  // February → 2.

2

Details

The Answer by Jon Skeet is correct.

Now we have a modern replacement for those troublesome old legacy date-time classes: the java.time classes.

java.time.Month

Among those classes is the Month enum. An enum carries one or more predefined objects, objects that are automatically instantiated when the class loads. On Month we have a dozen such objects, each given a name: JANUARY, FEBRUARY, MARCH, and so on. Each of those is a static final public class constant. You can use and pass these objects anywhere in your code. Example: someMethod( Month.AUGUST )

Fortunately, they have sane numbering, 1-12 where 1 is January and 12 is December.

Get a Month object for a particular month number (1-12).

Month month = Month.of( 2 );  // 2 → February.

Going the other direction, ask a Month object for its month number.

int monthNumber = Month.FEBRUARY.getValue();  // February → 2.

Many other handy methods on this class, such as knowing the number of days in each month. The class can even generate a localized name of the month.

You can get the localized name of the month, in various lengths or abbreviations.

String output = 
    Month.FEBRUARY.getDisplayName( 
        TextStyle.FULL , 
        Locale.CANADA_FRENCH 
    );

février

Also, you should pass objects of this enum around your code base rather than mere integer numbers. Doing so provides type-safety, ensures a valid range of values, and makes your code more self-documenting. See Oracle Tutorial if unfamiliar with the surprisingly powerful enum facility in Java.

You also may find useful the Year and YearMonth classes.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to java.time.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

Where to obtain the java.time classes?

  • Java SE 8 and SE 9 and later
    • Built-in.
    • Part of the standard Java API with a bundled implementation.
    • Java 9 adds some minor features and fixes.
  • Java SE 6 and SE 7
    • Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
  • Android

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
1

Set the month to Calendar.MARCH, or compare to see if it == Calendar.JUNE, for example.

The Date and Calendar classes date back to the very early days of Java, when folks were still figuring things out, and they are widely regarded as not very well designed.

If Calendar were created today with the same design, rather than ints for Calendar.JUNE, etc., they'd use enums.

0

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.

Pål GD
  • 1,021
  • 8
  • 25
  • 1
    The values are one and the same. The APIs may as well return 0, it's identical to the constant. Calendar.JANUARY could have been defined as 1 — that's the whole point. An enum would be a nice solution, but true enums weren't added to the language until Java 5, and Date has been around since the beginning. It's unfortunate, but you really can't "fix" such a fundamental API once third-party code uses it. The best that can be done is to provide new API and deprecate the old one to encourage people to move on. Thank you, Java 7... – Quinn Taylor Aug 11 '09 at 03:20
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.

Tihamer
  • 935
  • 10
  • 8
-1

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.

Powerlord
  • 87,612
  • 17
  • 125
  • 175
  • 5
    That's all very well when you're explicitly writing the code for a particular month, but it's a pain when you've got the month in "normal" form from a different source. – Jon Skeet Dec 05 '08 at 17:11
  • 1
    It's also a pain when you're trying to print that month value in some particular way--you're always adding 1 to it. – Brian Warshaw Feb 11 '11 at 12:39
-2

Because everything starts with 0. This is a basic fact of programming in Java. If one thing were to deviate from that, then that would lead to a whole slue of confusion. Let's not argue the formation of them and code with them.

Syrrus
  • 79
  • 9
  • 4
    No, most things in real world start with 1. _Offsets_ start with 0, and the month of the year is not an offset, it's one of the twelve, just like the day of the month is one of the 31 or 30 or 29 or 28. Treating the month as an offset is just capricious, especially if at the same time we don't treat the day of the month in the same way. What would the reason be for this difference ? – SantiBailors Feb 08 '16 at 13:27
  • in the Real World start with 1, In the Java world start with 0. BUT... I think it's because: - to calculate the day of the week it can't be offset for a couple calcuations without adding a couple more steps to it... - additionally it shows the complete days in the month if needed (without confusion or needing to check Feb.) - For the month it forces you to output in a date format which should be used either way. Additionally since the number of months in a year are regular and days in a month aren't it makes sense if you need to declare arrays and use an offset to better fit the array. – Syrrus Apr 07 '16 at 21:04