Calendar Class method in Java seems to be not giving correct result

107 Views Asked by At

Can someone please explain why is the behavior of the below piece of code not as expected.

import java.util.Calendar;
import java.util.Date;
import java.text.SimpleDateFormat; 
public class Test {

    public static void main(String[] args) {
        int numberOfYears = 3;
        Calendar now = Calendar.getInstance();//Today's date is December 28, 2023
        now.add(Calendar.YEAR, numberOfYears);// Adding 3 years to it
        Date date = now.getTime(); 
        String expectedExpiryDate = new SimpleDateFormat("MMMM d, YYYY").format(date); //expected date should be December 28, 2026
        System.out.println(expectedExpiryDate + " expectedExpiryDate");// But we are getting output as December 28, 2027

    }

}

enter image description here

If we add 1 year to today's date then we do get expected result i.e December 28, 2024. However, if we add 2 or 3 years to today's date it becomes December 28, 2026 and December 28, 2027 respectively

2

There are 2 best solutions below

6
Basil Bourque On

tl;dr

LocalDate.now().plusYears( 3 ).toString()

Avoid legacy date-time classes

Stop using Calendar. That class is a legacy class, long ago supplanted by the modern java.time classes defined in JSR 310. Never use Calendar, SimpleDateFormat, Date, Date, Timestamp, etc.

Do not waste your time trying to understand these legacy date-time classes. They are terribly flawed, with bizarre features. They are entirely replaced by java.time, so move on. Sun, Oracle, and the JCP community have.

java.time

Capture the current date only, without time-of-day, and without time zone, use java.time.LocalDate.

Determining the current date requires a time zone. If omitted the JVM’s current default time zone is implicitly applied.

ZoneId zoneTokyo = ZoneId.of( "Asia/Tokyo" ) ;
LocalDate todayTokyo = LocalDate.now( zoneTokyo ) ;

Add three years.

LocalDate threeYearsLater = todayTokyo.plusYears( 3 ) ;

To generate text is standard ISO 8601 format, call toString. To generate text in localized format, use DateTimeFormatter.ofLocalizedDate.

Be sure to specify your desired/expected locale. Relying on the JVM’s current default locale can be dicey. And doing so implicitly (by omitting any mention of locale) leaves the user wondering if you are aware of how localization works.

Locale locale = Locale.US;
DateTimeFormatter f = 
        DateTimeFormatter
                .ofLocalizedDate ( FormatStyle.LONG )
                .withLocale ( locale );
String output = threeYearsLater.format ( f );

output = December 30, 2026

0
Nagraj1990 On

I came across org.joda.time package and this seems to be providing the expected output.

import org.joda.time.LocalDateTime;
import org.joda.time.Period;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class Test {

public static void main(String[] args) {
    LocalDateTime currentLocalDateTime = LocalDateTime.now();
    LocalDateTime expectedDate = currentLocalDateTime.plus(new Period().withYears(3));
    DateTimeFormatter formatter = DateTimeFormat.forPattern("MMMM d, YYYY");
    String dateString = formatter.print(expectedDate);
    System.out.println(dateString);

}

}