sunleo
sunleo

Reputation: 10947

Java Calendar issue with adding year

I am trying to add a year to date but which is giving the same date for different inputs. Input dates are 28/Feb/2020 and 29/Feb/2020 and output are the same for both of them as 28/Feb/2021. Please help me to find what is wrong here.

Update:

I expect as output as 28/Feb/2021 and 01/Mar/2021.

public static Date dateAdd(Date newDate, int field, int amount) {
        Calendar aCalendar = Calendar.getInstance();
        aCalendar.setTime(newDate);
        aCalendar.add(field, amount);
        return aCalendar.getTime();
    }

    public static void main(String[] args) throws Exception {

        System.out.println(dateAdd(new SimpleDateFormat("dd/MM/yyyy").parse("28/02/2020"),Calendar.YEAR,1));
        System.out.println(dateAdd(new SimpleDateFormat("dd/MM/yyyy").parse("29/02/2020"),Calendar.YEAR,1));

        // Console output below

        //Sun Feb 28 00:00:00 IST 2021
        //Sun Feb 28 00:00:00 IST 2021
    }

Upvotes: 1

Views: 866

Answers (5)

DevX
DevX

Reputation: 308

After going through documentation of add and roll. It is clear that what you are looking for is to be found in roll not add.

Further after some research on the topic I found that the given scenario is also to be business dependent and depends what do you want to do because the add and roll gives you the choice to choose either of these.

I found this while searching for the similar problem I had.

Coming back to your code following modification is needed

aCalendar.roll(field, amount);

Someone more expert here will be able to help clean up this answer. JavaDocs for me were not very clear for these so the following question has some interesting answers to it

Upvotes: 5

xerx593
xerx593

Reputation: 13261

Nothing is wrong, it depends on the interpretation of "adding one year", for java.util.Calendar#add, it is:

add(f, delta) adds delta to field f. This is equivalent to calling set(f, get(f) + delta) with two adjustments:

Add rule 1. The value of field f after the call minus the value of field f before the call is delta, modulo any overflow that has occurred in field f. Overflow occurs when a field value exceeds its range and, as a result, the next larger field is incremented or decremented and the field value is adjusted back into its range.

Add rule 2. If a smaller field is expected to be invariant, but it is impossible for it to be equal to its prior value because of changes in its minimum or maximum after field f is changed or other constraints, such as time zone offset changes, then its value is adjusted to be as close as possible to its expected value. A smaller field represents a smaller unit of time. HOUR is a smaller field than DAY_OF_MONTH. No adjustment is made to smaller fields that are not expected to be invariant. The calendar system determines what fields are expected to be invariant.

In addition, unlike set(), add() forces an immediate recomputation of the calendar's milliseconds and all fields.

Example: Consider a GregorianCalendar originally set to August 31, 1999. Calling add(Calendar.MONTH, 13) sets the calendar to September 30, 2000. Add rule 1 sets the MONTH field to September, since adding 13 months to August gives September of the next year. Since DAY_OF_MONTH cannot be 31 in September in a GregorianCalendar, add rule 2 sets the DAY_OF_MONTH to 30, the closest possible value. Although it is a smaller field, DAY_OF_WEEK is not adjusted by rule 2, since it is expected to change when the month changes in a GregorianCalendar.

Source: https://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html

It sounds consistent with the described behavior, what & why would you expect? Maybe there are better/more specialized solutions.

Upvotes: 2

racraman
racraman

Reputation: 5034

There is no reason to expect a result of 01/Mar/2021. Indeed, some would then question why adding a year to the two dates 29/Feb/2020 and 01/Mar/2020 would output the same result of 01/Mar/2021 for both of them !

Instead, Java follows a clear, consistent and simple rule : If the result of date arithmetic (such as adding months or years) results in an invalid date, then the answer returned will be the last day of that result’s month.

Eg, from : https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html#plusMonths-long-

  1. Add the input months to the month-of-year field
  2. Check if the resulting date would be invalid
  3. Adjust the day-of-month to the last valid day if necessary

For example, 2007-03-31 plus one month would result in the invalid date 2007-04-31. Instead of returning an invalid result, the last valid day of the month, 2007-04-30, is selected instead.

Upvotes: 2

shu
shu

Reputation: 139

you can add a year and then add a day:

Date d = dateAdd(new SimpleDateFormat("dd/MM/yyyy").parse("28/02/2020"),Calendar.YEAR,1); // add a year
System.out.println(d);
d = dateAdd(d, Calendar.DATE, 1); // add a day
System.out.println(d);

Upvotes: 0

Yuvaraj G
Yuvaraj G

Reputation: 1237

Use :

System.out.println(dateAdd(new SimpleDateFormat("dd/MM/yyyy").parse("28/02/2020"),Calendar.DAY_OF_YEAR,366));
System.out.println(dateAdd(new SimpleDateFormat("dd/MM/yyyy").parse("29/02/2020"),Calendar.DAY_OF_YEAR,366));

Upvotes: 1

Related Questions