Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange Java calendar inconsistency

Tags:

java

calendar

Why such inconsistency?

public static void main(String[] args) {
    final Calendar calendar = Calendar.getInstance();

    calendar.set(Calendar.MONTH, 1);
    System.out.println("KO current month: " + calendar.get(Calendar.MONTH));

    calendar.set(Calendar.MONTH, 1);
    System.out.println("OK current month: " + calendar.get(Calendar.MONTH));
}

Output:

KO current month: 2
OK current month: 1
like image 384
redochka Avatar asked Dec 08 '22 14:12

redochka


2 Answers

Today is 31st May. The Calendar object is by default lenient so after the first set() the date is switched to 31st February which doesn't exist. Calendar leniently fixes it by moving to 3rd March. Month 2 means March.

The second set() operation is applied to 3rd March so it switches to 3rd Feb as one would expect. Month 1 means February.

You can see this by formatting the full date:

SimpleDateFormat sdf = new SimpleDateFormat();
Calendar calendar = Calendar.getInstance();

calendar.set(Calendar.MONTH, 1);
System.out.println(sdf.format(calendar.getTime()));

calendar.set(Calendar.MONTH, 1);
System.out.println(sdf.format(calendar.getTime()));

which returns

03/03/18 10:40
03/02/18 10:40

If you want to prevent the behaviour you need to call setLenient() method

Calendar calendar = Calendar.getInstance();
calendar.setLenient(false);
calendar.set(Calendar.MONTH, 1);

which will results in the following IllegalArgumentException exception:

Exception in thread "main" java.lang.IllegalArgumentException: MONTH: 1 -> 2 at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2829) at java.util.Calendar.updateTime(Calendar.java:3393) ...

like image 54
Karol Dowbecki Avatar answered Dec 25 '22 21:12

Karol Dowbecki


Karol Dowbecki’s answer is correct and well explained (and I should say well spotted). I should like to provide the good and modern fix.

    LocalDate date = LocalDate.of(2018, Month.MAY, 31);
    date = date.with(Month.FEBRUARY);
    System.out.println(date.getMonth());
    System.out.println(date);

Output:

FEBRUARY
2018-02-28

The Calendar class is confusing and poorly designed, so instead I am using LocalDate from java.time, the modern Java date and time API. This API is so much nicer to work with. LocalDate will not give you February 31, fortunately, since this date does not exist. It gives you the last day of February and thus the month you had expected.

PS The following initialization would mimic your code:

    LocalDate date = LocalDate.now(ZoneId.of("Europe/Paris"))`

However, I preferred to give code that will also demonstrate the point when someone tries to run it some day in the future.

Link: Oracle tutorial: Date Time explaining how to use java.time.

like image 30
Ole V.V. Avatar answered Dec 25 '22 19:12

Ole V.V.