Let's trace it.
The setMonths
method in DateUtils is defined as follows:
public static Date setMonths(Date date, int amount) {
return set(date, Calendar.MONTH, amount);
}
Let's check out the set
method. This methods throws the same exception class but for a different reason.
private static Date set(Date date, int calendarField, int amount) {
if (date == null) {
throw new IllegalArgumentException("The date must not be null");
}
// getInstance() returns a new object, so this method is thread safe.
Calendar c = Calendar.getInstance(); //returns an "empty" Calendar instance using default TimeZone and Local. Does not throw any exception
c.setLenient(false); // Just set the leniency value of the Calendar.
c.setTime(date); // set the time of the Calendar to the reference time by converting the date input into milliseconds
c.set(calendarField, amount); // this one looks interesting, but not quite
return c.getTime(); //returns the Date Object, possible source of the thrown Exception
}
The getTime
method in Calendar.java looks like:
public final Date getTime() {
return new Date(getTimeInMillis());
}
The method getTimeInMillis
in Calendar.java is defined as follows:
public long getTimeInMillis() {
if (!isTimeSet) {
updateTime();
}
return time;
}
The only statement in this method that looks interesting is updateTime
which in turn is defined as follows:
private void updateTime() {
computeTime();
// The areFieldsSet and areAllFieldsSet values are no longer
// controlled here (as of 1.5).
isTimeSet = true;
}
The computeTime
method in Calendar.java is an abstract method which for this case is concretely implemented in GregorianCalendar.java. I'll only show statements in the method that can throw that exception because the whole method is very long.
protected void computeTime() {
// In non-lenient mode, perform brief checking of calendar
// fields which have been set externally. Through this
// checking, the field values are stored in originalFields[]
// to see if any of them are normalized later.
if (!isLenient()) {
if (originalFields == null) {
originalFields = new int[FIELD_COUNT];
}
for (int field = 0; field < FIELD_COUNT; field++) {
int value = internalGet(field);
if (isExternallySet(field)) {
// Quick validation for any out of range values
**This is the part of the code that has thrown that Exception**
if (value < getMinimum(field) || value > getMaximum(field)) {
throw new IllegalArgumentException(getFieldName(field));
}
}
originalFields[field] = value;
}
//After this part, code that computes the time in milliseconds follows
.............................
.............................
}
As you can see, the value being supplied for a particular field is compared to the field's predefined minimum and maximum value. For the MONTH field, the minimum value is 0 (January) and the maximum value is 11 (December). You can verify these values from here.
Now as for your other question, the information you have provided is limited for us to provide a concrete answer. By the implementation of the Calendar API, if the leniency mode is set to false
, the value 0 for the month should correspond to January and 11 to December. The only way for a 0 month value to correspond to December is when you set the leniency mode to true
and you have a day value that "wraps around (roll over)" to December e.g. month = 0 but day = 369.
The best guess here as mentioned in one of the comments above is perhaps you are modifying the value of month
somehow, somewhere.