0

I am facing very strange issue.. Here is the code, which generates a new Date object:

Date result = DateUtils.setYears(new Date(), year);
result = DateUtils.setMonths(result, month);
return DateUtils.setDays(result, day);

If I pass any value for month starting from 1 till 11 - everything works fine, 1 means January, 2 - February ... 11 - November. But with 12 it always fails with java.lang.IllegalArgumentException: MONTH exception..

When I try to pass 0-based values, the first one 0 means December of previous year.. Any ideas?

Thank you in advance

nKognito
  • 6,297
  • 17
  • 77
  • 138

2 Answers2

2

The method setMonths look like

 public static Date setMonths(Date date, int amount) {
        return set(date, Calendar.MONTH, amount);
    }

As you can notice that internally it uses Calendar.MONTH from java. Months in Calendar class starts from 0 till 12(12 value mean UNDECIMBER i.e. thireteenth month of the year Although GregorianCalendar does not use this value, lunar calendars do). So when you are passing 0 it means January, 1 it means February,... and 11 means December. For invalid month value calendar class throw

java.lang.IllegalArgumentException
sol4me
  • 15,233
  • 5
  • 34
  • 34
  • *And months in Calendar class starts from 0 till 11.* - not entirely correct. Don't forget about thirteenth month of the year. :) – Aleksandr M Jan 20 '15 at 11:58
0

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.

ultrajohn
  • 2,527
  • 4
  • 31
  • 56